# What this PR does Refactors the PagerDuty migration script to be a bit more generic + adds a migration script to migrate from Splunk OnCall (VictorOps) tldr; ```bash ❯ docker build -t oncall-migrator . [+] Building 0.4s (10/10) FINISHED ❯ docker run --rm \ -e MIGRATING_FROM="pagerduty" \ -e MODE="plan" \ -e ONCALL_API_URL="http://localhost:8080" \ -e ONCALL_API_TOKEN="<ONCALL_API_TOKEN>" \ -e PAGERDUTY_API_TOKEN="<PAGERDUTY_API_TOKEN>" \ oncall-migrator running pagerduty migration script... ❯ docker run --rm \ -e MIGRATING_FROM="splunk" \ -e MODE="plan" \ -e ONCALL_API_URL="http://localhost:8080" \ -e ONCALL_API_TOKEN="<ONCALL_API_TOKEN>" \ -e SPLUNK_API_ID="<SPLUNK_API_ID>" \ -e SPLUNK_API_KEY="<SPLUNK_API_KEY>" \ oncall-migrator migrating from splunk oncall... ``` https://www.loom.com/share/a855062d436a4ef79f030e22528d8c71 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
import typing
|
|
|
|
from lib.common.report import ERROR_SIGN, SUCCESS_SIGN, TAB, WARNING_SIGN
|
|
from lib.splunk import types
|
|
|
|
|
|
def format_user(user: types.SplunkUserWithPagingPolicies) -> str:
|
|
result = f"{user['firstName']} {user['lastName']} ({user['email']})"
|
|
|
|
if user["oncall_user"]:
|
|
result = f"{SUCCESS_SIGN} {result}"
|
|
else:
|
|
result = f"{ERROR_SIGN} {result} — no Grafana OnCall user found with this email"
|
|
return result
|
|
|
|
|
|
def format_team(team: types.SplunkTeam) -> str:
|
|
return f"{SUCCESS_SIGN} {team['name']} ({team['slug']})"
|
|
|
|
|
|
def format_schedule(schedule: types.SplunkScheduleWithTeamAndRotations) -> str:
|
|
schedule_name = schedule["name"]
|
|
if schedule["migration_errors"]:
|
|
result = f"{ERROR_SIGN} {schedule_name} — some layers cannot be migrated"
|
|
else:
|
|
result = f"{SUCCESS_SIGN} {schedule_name}"
|
|
return result
|
|
|
|
|
|
def format_escalation_policy(policy: types.SplunkEscalationPolicy) -> str:
|
|
policy_name = policy["name"]
|
|
unmatched_users = policy["unmatched_users"]
|
|
flawed_schedules = policy["flawed_schedules"]
|
|
|
|
if unmatched_users and flawed_schedules:
|
|
result = f"{ERROR_SIGN} {policy_name} — policy references unmatched users and schedules that cannot be migrated"
|
|
elif unmatched_users:
|
|
result = f"{ERROR_SIGN} {policy_name} — policy references unmatched users"
|
|
elif flawed_schedules:
|
|
result = f"{ERROR_SIGN} {policy_name} — policy references schedules that cannot be migrated"
|
|
else:
|
|
result = f"{SUCCESS_SIGN} {policy_name}"
|
|
|
|
return result
|
|
|
|
|
|
def user_report(users: typing.List[types.SplunkUserWithPagingPolicies]) -> str:
|
|
result = "User notification rules report:"
|
|
|
|
for user in sorted(users, key=lambda u: bool(u["oncall_user"]), reverse=True):
|
|
result += f"\n{TAB}{format_user(user)}"
|
|
|
|
if user["oncall_user"] and user["pagingPolicies"]:
|
|
result += " (existing notification rules will be deleted)"
|
|
|
|
return result
|
|
|
|
|
|
def schedule_report(schedules: list[types.SplunkScheduleWithTeamAndRotations]) -> str:
|
|
result = "Schedule report:"
|
|
|
|
for schedule in sorted(schedules, key=lambda s: s["migration_errors"]):
|
|
result += "\n" + TAB + format_schedule(schedule)
|
|
|
|
if schedule["oncall_schedule"] and not schedule["migration_errors"]:
|
|
result += " (existing schedule with name '{}' will be deleted)".format(
|
|
schedule["oncall_schedule"]["name"]
|
|
)
|
|
|
|
for error in schedule["migration_errors"]:
|
|
result += "\n" + TAB * 2 + "{} {}".format(ERROR_SIGN, error)
|
|
|
|
return result
|
|
|
|
|
|
def escalation_policy_report(policies: list[types.SplunkEscalationPolicy]) -> str:
|
|
result = "Escalation policy report: "
|
|
|
|
for policy in sorted(
|
|
policies, key=lambda p: bool(p["unmatched_users"] or p["flawed_schedules"])
|
|
):
|
|
unmatched_users = policy["unmatched_users"]
|
|
flawed_schedules = policy["flawed_schedules"]
|
|
unsupported_escalation_entry_types = policy[
|
|
"unsupported_escalation_entry_types"
|
|
]
|
|
result += f"\n{TAB}{format_escalation_policy(policy)}"
|
|
|
|
if (
|
|
not unmatched_users
|
|
and not flawed_schedules
|
|
and policy["oncall_escalation_chain"]
|
|
):
|
|
result += f" (existing escalation chain with name '{policy['oncall_escalation_chain']['name']}' will be deleted)"
|
|
|
|
for user in unmatched_users:
|
|
result += f"\n{TAB * 2}{format_user(user)}"
|
|
|
|
for schedule in policy["flawed_schedules"]:
|
|
result += f"\n{TAB * 2}{format_schedule(schedule)}"
|
|
|
|
for entry_type in unsupported_escalation_entry_types:
|
|
result += f"\n{TAB * 2}{WARNING_SIGN} unsupported escalation entry type: {entry_type}"
|
|
|
|
return result
|