oncall-engine/tools/migrators/lib/grafana/api_client.py
Joey Orlando c46dff09d9
Splunk OnCall migration tool (#4267)
# 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.
2024-05-14 13:53:59 +00:00

83 lines
2.6 KiB
Python

import secrets
from urllib.parse import urljoin
import requests
class GrafanaAPIClient:
def __init__(self, base_url, username, password):
self.base_url = base_url
self.username = username
self.password = password
def _api_call(self, method: str, path: str, **kwargs):
return requests.request(
method,
urljoin(self.base_url, path),
auth=(self.username, self.password),
**kwargs,
)
def create_user_with_random_password(self, name: str, email: str):
return self._api_call(
"POST",
"/api/admin/users",
json={
"name": name,
"email": email,
"login": email.split("@")[0],
"password": secrets.token_urlsafe(15),
},
)
def get_all_users(self):
"""
https://grafana.com/docs/grafana/v10.3/developers/http_api/user/#search-users
"""
return self._api_call("GET", "/api/users").json()
def idemopotently_create_team_and_add_users(
self, team_name: str, user_emails: list[str]
) -> int:
"""
Get team by name
https://grafana.com/docs/grafana/v10.3/developers/http_api/team/#using-the-name-parameter
Create team
https://grafana.com/docs/grafana/v10.3/developers/http_api/team/#add-team
Add team members
https://grafana.com/docs/grafana/v10.3/developers/http_api/team/#add-team-member
"""
existing_team = self._api_call(
"GET", "/api/teams/search", params={"name": team_name}
).json()
if existing_team["teams"]:
# team already exists
team_id = existing_team["teams"][0]["id"]
else:
# team doesn't exist create it
response = self._api_call("POST", "/api/teams", json={"name": team_name})
if response.status_code == 200:
team_id = response.json()["teamId"]
else:
raise Exception(f"Failed to fetch/create Grafana team '{team_name}'")
grafana_users = self.get_all_users()
grafana_user_id_to_email_map = {}
for user_email in user_emails:
for grafana_user in grafana_users:
if grafana_user["email"] == user_email:
grafana_user_id_to_email_map[grafana_user["id"]] = user_email
break
for user_id in grafana_user_id_to_email_map.keys():
self._api_call(
"POST", f"/api/teams/{team_id}/members", json={"userId": user_id}
)
return team_id