This PR adds support for migrating data from OpsGenie to Grafana IRM. Closes https://github.com/grafana/irm/issues/1179
317 lines
11 KiB
Python
317 lines
11 KiB
Python
import datetime
|
|
|
|
from pdpyras import APISession
|
|
|
|
from lib.common.report import TAB
|
|
from lib.common.resources.users import match_user
|
|
from lib.grafana.service_model_client import ServiceModelClient
|
|
from lib.oncall.api_client import OnCallAPIClient
|
|
from lib.pagerduty.config import (
|
|
EXPERIMENTAL_MIGRATE_EVENT_RULES,
|
|
MIGRATE_USERS,
|
|
MODE,
|
|
MODE_PLAN,
|
|
PAGERDUTY_API_TOKEN,
|
|
PAGERDUTY_FILTER_USERS,
|
|
PAGERDUTY_MIGRATE_SERVICES,
|
|
)
|
|
from lib.pagerduty.report import (
|
|
escalation_policy_report,
|
|
format_escalation_policy,
|
|
format_integration,
|
|
format_ruleset,
|
|
format_schedule,
|
|
format_user,
|
|
integration_report,
|
|
ruleset_report,
|
|
schedule_report,
|
|
services_report,
|
|
user_report,
|
|
)
|
|
from lib.pagerduty.resources.escalation_policies import (
|
|
filter_escalation_policies,
|
|
match_escalation_policy,
|
|
match_escalation_policy_for_integration,
|
|
migrate_escalation_policy,
|
|
)
|
|
from lib.pagerduty.resources.integrations import (
|
|
filter_integrations,
|
|
match_integration,
|
|
match_integration_type,
|
|
migrate_integration,
|
|
)
|
|
from lib.pagerduty.resources.notification_rules import migrate_notification_rules
|
|
from lib.pagerduty.resources.rulesets import match_ruleset, migrate_ruleset
|
|
from lib.pagerduty.resources.schedules import (
|
|
filter_schedules,
|
|
match_schedule,
|
|
migrate_schedule,
|
|
)
|
|
from lib.pagerduty.resources.services import (
|
|
BusinessService,
|
|
TechnicalService,
|
|
filter_services,
|
|
get_all_business_services_with_metadata,
|
|
get_all_technical_services_with_metadata,
|
|
migrate_all_services,
|
|
)
|
|
from lib.pagerduty.resources.users import (
|
|
filter_users,
|
|
match_users_and_schedules_for_escalation_policy,
|
|
match_users_for_schedule,
|
|
)
|
|
|
|
|
|
def migrate() -> None:
|
|
# Set up API sessions and timeout
|
|
session = APISession(PAGERDUTY_API_TOKEN)
|
|
session.timeout = 20
|
|
|
|
# Use a flag to track how many resources were eligible for migration in the final report
|
|
filtered_resources_summary = {
|
|
"schedules": 0,
|
|
"escalation_policies": 0,
|
|
"integrations": 0,
|
|
}
|
|
|
|
# Process users only if MIGRATE_USERS is true
|
|
users = []
|
|
oncall_users = []
|
|
user_id_map = {}
|
|
|
|
if MIGRATE_USERS:
|
|
print("▶ Fetching users...")
|
|
users = session.list_all("users", params={"include[]": "notification_rules"})
|
|
oncall_users = OnCallAPIClient.list_users_with_notification_rules()
|
|
|
|
# Apply filtering to users if specified
|
|
if PAGERDUTY_FILTER_USERS:
|
|
print("▶ Filtering users based on PAGERDUTY_FILTER_USERS...")
|
|
users = filter_users(users)
|
|
|
|
# Match users with Grafana OnCall users
|
|
for user in users:
|
|
match_user(user, oncall_users)
|
|
|
|
# Create a mapping from PagerDuty user IDs to Grafana OnCall user IDs
|
|
user_id_map = {
|
|
u["id"]: u["oncall_user"]["id"] if u["oncall_user"] else None for u in users
|
|
}
|
|
else:
|
|
print("▶ Skipping user fetching and migration as MIGRATE_USERS is false...")
|
|
|
|
print("▶ Fetching schedules...")
|
|
# Fetch schedules from PagerDuty
|
|
schedules = session.list_all(
|
|
"schedules",
|
|
params={"include[]": ["schedule_layers", "teams"], "time_zone": "UTC"},
|
|
)
|
|
|
|
# Apply filters to schedules
|
|
schedules = filter_schedules(schedules)
|
|
filtered_resources_summary["schedules"] = len(schedules)
|
|
print(f"Found {len(schedules)} schedules after filtering")
|
|
|
|
# Fetch overrides from PagerDuty
|
|
since = datetime.datetime.now(datetime.timezone.utc)
|
|
until = since + datetime.timedelta(
|
|
days=365
|
|
) # fetch overrides up to 1 year from now
|
|
for schedule in schedules:
|
|
response = session.jget(
|
|
f"schedules/{schedule['id']}/overrides",
|
|
params={"since": since.isoformat(), "until": until.isoformat()},
|
|
)
|
|
schedule["overrides"] = response["overrides"]
|
|
|
|
# Fetch schedules from OnCall
|
|
oncall_schedules = OnCallAPIClient.list_all("schedules")
|
|
|
|
print("▶ Fetching escalation policies...")
|
|
escalation_policies = session.list_all(
|
|
"escalation_policies", params={"include[]": "teams"}
|
|
)
|
|
|
|
# Apply filters to escalation policies
|
|
escalation_policies = filter_escalation_policies(escalation_policies)
|
|
filtered_resources_summary["escalation_policies"] = len(escalation_policies)
|
|
print(f"Found {len(escalation_policies)} escalation policies after filtering")
|
|
|
|
oncall_escalation_chains = OnCallAPIClient.list_all("escalation_chains")
|
|
|
|
print("▶ Fetching integrations...")
|
|
services = session.list_all(
|
|
"services", params={"include[]": ["integrations", "teams"]}
|
|
)
|
|
vendors = session.list_all("vendors")
|
|
|
|
integrations = []
|
|
for service in services:
|
|
service_integrations = service.pop("integrations")
|
|
for integration in service_integrations:
|
|
integration["service"] = service
|
|
integrations.append(integration)
|
|
|
|
# Apply filters to integrations
|
|
integrations = filter_integrations(integrations)
|
|
filtered_resources_summary["integrations"] = len(integrations)
|
|
print(f"Found {len(integrations)} integrations after filtering")
|
|
|
|
oncall_integrations = OnCallAPIClient.list_all("integrations")
|
|
|
|
rulesets = None
|
|
if EXPERIMENTAL_MIGRATE_EVENT_RULES:
|
|
print("▶ Fetching event rules (global rulesets)...")
|
|
rulesets = session.list_all("rulesets")
|
|
for ruleset in rulesets:
|
|
rules = session.list_all(f"rulesets/{ruleset['id']}/rules")
|
|
ruleset["rules"] = rules
|
|
|
|
# Match resources if we have users
|
|
for schedule in schedules:
|
|
match_schedule(schedule, oncall_schedules, user_id_map)
|
|
if MIGRATE_USERS:
|
|
match_users_for_schedule(schedule, users)
|
|
else:
|
|
# When not migrating users, mark schedule as having no unmatched users
|
|
schedule["unmatched_users"] = []
|
|
schedule["migration_errors"] = []
|
|
|
|
for policy in escalation_policies:
|
|
match_escalation_policy(policy, oncall_escalation_chains)
|
|
if MIGRATE_USERS:
|
|
match_users_and_schedules_for_escalation_policy(policy, users, schedules)
|
|
else:
|
|
# When not migrating users, mark policy as having no unmatched users
|
|
policy["unmatched_users"] = []
|
|
policy["flawed_schedules"] = []
|
|
|
|
for integration in integrations:
|
|
match_integration(integration, oncall_integrations)
|
|
match_integration_type(integration, vendors)
|
|
match_escalation_policy_for_integration(integration, escalation_policies)
|
|
|
|
if rulesets is not None:
|
|
for ruleset in rulesets:
|
|
match_ruleset(
|
|
ruleset,
|
|
oncall_integrations,
|
|
escalation_policies,
|
|
services,
|
|
integrations,
|
|
)
|
|
|
|
if PAGERDUTY_MIGRATE_SERVICES:
|
|
client = ServiceModelClient()
|
|
# Get all services
|
|
all_technical_services = get_all_technical_services_with_metadata(session)
|
|
technical_service_map = {
|
|
service.id: service for service in all_technical_services
|
|
}
|
|
all_business_services = get_all_business_services_with_metadata(
|
|
session, technical_service_map
|
|
)
|
|
|
|
# Apply filters to services
|
|
filtered_technical_data = filter_services(
|
|
[service.raw_data for service in all_technical_services]
|
|
)
|
|
filtered_business_data = filter_services(
|
|
[service.raw_data for service in all_business_services]
|
|
)
|
|
|
|
# Convert filtered data back to service objects
|
|
technical_services = [
|
|
TechnicalService(service) for service in filtered_technical_data
|
|
]
|
|
business_services = [
|
|
BusinessService(service) for service in filtered_business_data
|
|
]
|
|
|
|
# Print filtering and matching summary
|
|
print("\n▶ Migration summary after filtering and matching:")
|
|
if MIGRATE_USERS:
|
|
print(
|
|
f"Users: {sum(1 for u in users if u.get('oncall_user'))} matched of {len(users)} total"
|
|
)
|
|
print(
|
|
f"Schedules: {sum(1 for s in schedules if not s.get('unmatched_users') and not s.get('migration_errors'))} eligible of {filtered_resources_summary['schedules']} filtered"
|
|
)
|
|
print(
|
|
f"Escalation policies: {sum(1 for p in escalation_policies if not p.get('unmatched_users') and not p.get('flawed_schedules'))} eligible of {filtered_resources_summary['escalation_policies']} filtered"
|
|
)
|
|
print(
|
|
f"Integrations: {sum(1 for i in integrations if i.get('oncall_type') and not i.get('is_escalation_policy_flawed'))} eligible of {filtered_resources_summary['integrations']} filtered\n"
|
|
)
|
|
|
|
if MODE == MODE_PLAN:
|
|
if MIGRATE_USERS:
|
|
print(user_report(users), end="\n\n")
|
|
print(schedule_report(schedules), end="\n\n")
|
|
print(escalation_policy_report(escalation_policies), end="\n\n")
|
|
print(integration_report(integrations), end="\n\n")
|
|
|
|
if rulesets is not None:
|
|
print(ruleset_report(rulesets), end="\n\n")
|
|
|
|
if PAGERDUTY_MIGRATE_SERVICES:
|
|
print(
|
|
services_report(
|
|
all_technical_services,
|
|
all_business_services,
|
|
technical_services,
|
|
business_services,
|
|
),
|
|
end="\n\n",
|
|
)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
if MIGRATE_USERS:
|
|
print("▶ Migrating user notification rules...")
|
|
for user in users:
|
|
if user["oncall_user"]:
|
|
migrate_notification_rules(user)
|
|
print(TAB + format_user(user))
|
|
else:
|
|
print(
|
|
"▶ Skipping migrating user notification rules as MIGRATE_USERS is false..."
|
|
)
|
|
|
|
print("▶ Migrating schedules...")
|
|
for schedule in schedules:
|
|
if not schedule["unmatched_users"] and not schedule["migration_errors"]:
|
|
migrate_schedule(schedule, user_id_map)
|
|
print(TAB + format_schedule(schedule))
|
|
|
|
print("▶ Migrating escalation policies...")
|
|
for policy in escalation_policies:
|
|
if not policy["unmatched_users"] and not policy["flawed_schedules"]:
|
|
migrate_escalation_policy(policy, users, schedules)
|
|
print(TAB + format_escalation_policy(policy))
|
|
|
|
print("▶ Migrating integrations...")
|
|
for integration in integrations:
|
|
if (
|
|
integration["oncall_type"]
|
|
and not integration["is_escalation_policy_flawed"]
|
|
):
|
|
migrate_integration(integration, escalation_policies)
|
|
print(TAB + format_integration(integration))
|
|
|
|
if rulesets is not None:
|
|
print("▶ Migrating event rules (global rulesets)...")
|
|
for ruleset in rulesets:
|
|
if not ruleset["flawed_escalation_policies"]:
|
|
migrate_ruleset(ruleset, escalation_policies, services)
|
|
print(TAB + format_ruleset(ruleset))
|
|
|
|
if PAGERDUTY_MIGRATE_SERVICES:
|
|
print("▶ Migrating services to Grafana's service model...")
|
|
migrate_all_services(
|
|
client, technical_services, business_services, dry_run=False
|
|
)
|
|
else:
|
|
print("▶ Skipping service migration as PAGERDUTY_MIGRATE_SERVICES is false...")
|