Zum Inhalt springen

Von UptimeRobot migrieren

Kopieren Sie Ihre UptimeRobot-Monitore mit einem Befehl zu OKStatus, betreiben Sie beide Werkzeuge ein paar Tage parallel und wechseln Sie dann. Ihr UptimeRobot-Konto wird nie verändert.

Ein Python-Skript liest Ihre Monitore über die UptimeRobot-API und legt sie über die OKStatus-API neu an. Es nutzt nur die Standardbibliothek: Sie müssen nichts installieren.

  • Ein UptimeRobot-Schlüssel nur zum Lesen: Integrations → API → Read-only API key. Er kann nichts ändern.
  • Ein OKStatus-Schlüssel mit Zugriff Voll, im Max-Tarif (Einstellungen → API-Schlüssel). Mit Free oder Pro liefert Ihnen der Probelauf aus Schritt 3 trotzdem die genaue Liste zum manuellen Anlegen.
  • Python 3.9 oder neuer.
UptimeRobotOKStatusDetail
HTTP(s)HTTPgleiche URL, gleiche Methode
KeywordHTTP + Schlüsselwort„not exists“ wird zu keywordInvert
PingPing
PortTCPHTTP 80, HTTPS 443, FTP 21, SMTP 25, POP3 110, IMAP 143 oder Ihr eigener Port
HeartbeatHeartbeat (push)eine neue Ping-URL für Ihre Cronjobs; Perioden über einem Tag werden übersprungen
PausiertPausiertder Monitor wird angelegt und dann pausiert
IntervallIntervallbei Bedarf auf das Minimum Ihres Tarifs angehoben, höchstens ein Tag

Laden Sie uptimerobot_to_okstatus.py herunter oder kopieren Sie es. Seine Meldungen sind auf Englisch.

uptimerobot_to_okstatus.py
#!/usr/bin/env python3
"""Copy UptimeRobot monitors to OKStatus.
Reads your monitors with UptimeRobot's API (v2, read-only key is enough) and
creates them in OKStatus with its REST API (Full key, Max plan). Without
--apply it only prints what it would create. Your UptimeRobot account is
never changed. Python 3.9+, standard library only.
export UPTIMEROBOT_API_KEY=ur...
export OKSTATUS_REGIONS=fr-dc3,de-fra,nl-ams # regions of your plan
export OKSTATUS_MIN_INTERVAL=30 # 300 Free, 60 Pro, 30 Max
python3 uptimerobot_to_okstatus.py # dry run
export OKSTATUS_API_KEY=oks_live_...
python3 uptimerobot_to_okstatus.py --apply
Guide: https://okstatus.eu/docs/tutorials/migrate-from-uptimerobot/
"""
import json
import os
import re
import sys
import urllib.error
import urllib.parse
import urllib.request
UR_API = os.environ.get("UPTIMEROBOT_API_URL", "https://api.uptimerobot.com/v2")
OK_API = os.environ.get("OKSTATUS_API_URL", "https://api.okstatus.eu/api/v1")
REGIONS = [r.strip() for r in os.environ.get("OKSTATUS_REGIONS", "fr-dc3,de-fra").split(",") if r.strip()]
MIN_INTERVAL = int(os.environ.get("OKSTATUS_MIN_INTERVAL", "60"))
APPLY = "--apply" in sys.argv
# UptimeRobot v2 codes (uptimerobot.com/api/legacy)
PORTS = {1: 80, 2: 443, 3: 21, 4: 25, 5: 110, 6: 143} # port monitor sub_type
METHODS = {1: "HEAD", 2: "GET", 3: "POST", 4: "PUT", 5: "PATCH", 6: "DELETE", 7: "OPTIONS"}
PAUSED = 0 # monitor status
def uptimerobot_monitors():
key = os.environ.get("UPTIMEROBOT_API_KEY")
if not key:
sys.exit("Set UPTIMEROBOT_API_KEY (Integrations → API → read-only key).")
offset = 0
while True:
form = {"api_key": key, "format": "json", "offset": offset, "limit": 50,
"http_request_details": "true"}
req = urllib.request.Request(f"{UR_API}/getMonitors", urllib.parse.urlencode(form).encode())
with urllib.request.urlopen(req, timeout=30) as r:
page = json.load(r)
if page.get("stat") != "ok":
sys.exit(f"UptimeRobot: {page.get('error')}")
yield from page.get("monitors", [])
offset += 50
if offset >= int(page.get("pagination", {}).get("total", 0)):
return
def as_int(value, default=0):
try:
return int(value)
except (TypeError, ValueError):
return default
def convert(m):
"""(body of POST /api/v1/monitors, None) or (None, why it is skipped)."""
kind = as_int(m.get("type"))
body = {
"name": str(m.get("friendly_name") or m.get("url") or "Monitor")[:100],
"regions": REGIONS,
# OKStatus accepts 30 s to 1 day, and never less than the plan's floor.
"interval": min(max(as_int(m.get("interval"), 300), MIN_INTERVAL), 86400),
}
if kind in (1, 2): # HTTP(s), Keyword
body.update(type="http", url=m["url"])
method = METHODS.get(as_int(m.get("http_method"), 2), "GET")
if method != "GET":
body["httpMethod"] = method
if kind == 2:
body["keyword"] = m.get("keyword_value") or ""
body["keywordInvert"] = as_int(m.get("keyword_type")) == 2 # 2 = "not exists"
elif kind == 3: # Ping
body.update(type="ping", url=m["url"])
elif kind == 4: # Port
sub = as_int(m.get("sub_type"))
port = as_int(m.get("port")) if sub == 99 else PORTS.get(sub)
if not port:
return None, "port monitor without a port"
body.update(type="tcp", url=m["url"], port=port)
elif kind == 5: # Heartbeat
if as_int(m.get("interval")) > 86400:
# A shorter period would report the job down between two runs.
return None, "heartbeat period over one day, the OKStatus maximum"
slug = re.sub(r"[^a-z0-9]+", "-", body["name"].lower()).strip("-")
body.update(type="push", url=slug or "heartbeat")
else:
return None, f"UptimeRobot type {m.get('type')} has no equivalent"
return body, None
def okstatus(method, url, body):
req = urllib.request.Request(url, json.dumps(body).encode(), method=method, headers={
"Authorization": f"Bearer {os.environ['OKSTATUS_API_KEY']}",
"Content-Type": "application/json",
})
with urllib.request.urlopen(req, timeout=30) as r:
return json.load(r)["data"]
def main():
if APPLY and not os.environ.get("OKSTATUS_API_KEY"):
sys.exit("Set OKSTATUS_API_KEY (a Full key, Settings → API Keys) to use --apply.")
created = skipped = failed = 0
for m in uptimerobot_monitors():
name = m.get("friendly_name", "?")
body, why = convert(m)
if body is None:
print(f"SKIPPED {name} ({why})")
skipped += 1
continue
paused = as_int(m.get("status"), -1) == PAUSED
if not APPLY:
print(f"TO CREATE {body['type']:5} {body['name']} -> {body['url']}"
+ (" (paused)" if paused else ""))
continue
try:
new = okstatus("POST", f"{OK_API}/monitors", body)
if paused:
okstatus("PATCH", f"{OK_API}/monitors/{new['id']}", {"status": "paused"})
extra = f" · new ping URL: {new['pushUrl']}" if body["type"] == "push" else ""
print(f"CREATED {body['name']}{extra}")
created += 1
except urllib.error.HTTPError as e:
print(f"FAILED {body['name']}: {e.code} {e.read().decode(errors='replace')[:200]}")
failed += 1
if APPLY:
print(f"\n{created} created, {skipped} skipped, {failed} failed.")
if failed:
sys.exit(1)
else:
print("\nDry run: nothing created. Add --apply to create these monitors.")
if __name__ == "__main__":
main()
Terminal-Fenster
export UPTIMEROBOT_API_KEY=ur…
export OKSTATUS_REGIONS=fr-dc3,de-fra,nl-ams # regions of your plan
export OKSTATUS_MIN_INTERVAL=30 # 300 on Free, 60 on Pro, 30 on Max
python3 uptimerobot_to_okstatus.py
TO CREATE http Website -> https://example.com
TO CREATE http API health -> https://api.example.com/health
TO CREATE tcp SMTP -> mail.example.com
TO CREATE push Nightly backup -> nightly-backup
TO CREATE ping Office router -> 203.0.113.10 (paused)
Dry run: nothing created. Add --apply to create these monitors.

Ohne --apply wird nichts angelegt. Lesen Sie die Liste, passen Sie die Variablen an und wiederholen Sie es so oft wie nötig.

Terminal-Fenster
export OKSTATUS_API_KEY=oks_live_…
python3 uptimerobot_to_okstatus.py --apply
CREATED Website
CREATED API health
CREATED SMTP
CREATED Nightly backup · new ping URL: https://backend.okstatus.eu/push/…
CREATED Office router
5 created, 0 skipped, 0 failed.
  1. Legen Sie Ihre Alarmkanäle neu an im Dashboard und verknüpfen Sie sie mit den Monitoren oder einer Gruppe. Das Skript kopiert sie nicht: Sie enthalten Geheimnisse.
  2. Tauschen Sie die Ping-URL jedes Heartbeats in Ihren Cronjobs aus. Der Aufruf bleibt gleich: GET, POST und HEAD zählen alle als Ping. Siehe Heartbeat.
  3. Betreiben Sie beide Werkzeuge eine Woche und vergleichen Sie die Alarme. Mit mehreren Regionen alarmiert OKStatus nur, wenn die Mehrheit zustimmt: Rechnen Sie mit weniger vereinzelten Alarmen.
  4. Legen Sie Ihre Statusseite neu an und richten Sie Ihre Domain (CNAME) auf OKStatus. Siehe Statusseiten.
  5. Pausieren Sie die UptimeRobot-Monitore und schließen Sie das Konto, wenn Sie bereit sind.
  • der Verfügbarkeitsverlauf: OKStatus beginnt am Tag der Migration zu zählen;
  • Alarmkanäle, Statusseiten und Wartungsfenster;
  • Zugangsdaten, Header und Request-Bodies von HTTP-Monitoren: Fügen Sie sie im Dashboard hinzu;
  • Typen ohne Entsprechung (UDP, Drittanbieter-Dienste) und Heartbeats mit einer Periode über einem Tag: Das Skript listet sie als übersprungen (SKIPPED).