From 310913902de573d7f8b930f87384f21bdf05e6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Qui=C3=B1ones?= Date: Wed, 14 Sep 2016 16:30:01 -0300 Subject: [PATCH 1/7] Feature: IterMonitors for paginated monitors Allows iterating all the monitors, regardless of the getMonitors limit, which defaults to 50. IterMonitors is a Python generator. The API calls are done on demand, using Python yield. Thus preventing more calls than needed. Adds options 'offset' and 'limit' to getMonitors in order to implement the pagination. --- uptimerobot/uptimerobot.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 17e884a..67e6cb1 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -17,6 +17,7 @@ apiKey = None monitorAlertContacts = "" +MONITORS_PER_PAGE = 50 class UptimeRobot(object): def __init__(self, apiKey): @@ -41,7 +42,7 @@ def addMonitor(self, monitorFriendlyName, monitorURL): return False - def getMonitors(self, response_times=0, logs=0, uptime_ratio=''): + def getMonitors(self, response_times=0, logs=0, uptime_ratio='', offset=None, limit=None): """ Returns status and response payload for all known monitors. """ @@ -62,6 +63,10 @@ def getMonitors(self, response_times=0, logs=0, uptime_ratio=''): # uptime ratios for those periods) if uptime_ratio: url += '&customUptimeRatio=%s' % uptime_ratio + if offset is not None: + url += "&offset=%s" % offset + if limit is not None: + url += "&limit=%s" % limit return self.requestApi(url) @@ -216,6 +221,18 @@ def getMonitorId(self, name): def deleteMonitorByName(self, name): return self.deleteMonitorById(self.getMonitorId(name)) + def iterMonitors(self, response_times=0, logs=0, uptime_ratio=''): + "Iterate all monitors." + total = None + offset = 0 + while total is None or offset < total: + response = self.getMonitors(response_times, logs, uptime_ratio, offset=offset, limit=MONITORS_PER_PAGE) + if total is None: + total = int(response[1]['total']) + offset += MONITORS_PER_PAGE + for m in response[1]['monitors']['monitor']: + yield m + if __name__ == "__main__": for arg in sys.argv: if arg.startswith("monitorFriendlyName="): From 463f45861840b713a219f6e6465e51bf4f493113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Qui=C3=B1ones?= Date: Thu, 15 Sep 2016 12:51:49 -0300 Subject: [PATCH 2/7] Improvements in getMonitorByName, getMonitorById getMonitorByName now works with more than 50 monitors, because it makes use of the pagination added in iterMonitors. getMonitorByName and getMonitorById now return the whole monitor information. Previously they returned just status and alltimeuptimeratio. They do so reusing getMonitors. --- uptimerobot/uptimerobot.py | 50 ++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 67e6cb1..8c33ad0 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -42,7 +42,7 @@ def addMonitor(self, monitorFriendlyName, monitorURL): return False - def getMonitors(self, response_times=0, logs=0, uptime_ratio='', offset=None, limit=None): + def getMonitors(self, response_times=0, logs=0, uptime_ratio='', offset=None, limit=None, search=None, monitors=None): """ Returns status and response payload for all known monitors. """ @@ -67,42 +67,36 @@ def getMonitors(self, response_times=0, logs=0, uptime_ratio='', offset=None, li url += "&offset=%s" % offset if limit is not None: url += "&limit=%s" % limit + if search is not None: + url += "&search=%s" % search + if monitors is not None: + url += "&monitors=%s" % '-'.join(str(m) for m in monitors) return self.requestApi(url) def getMonitorById(self, monitorId): """ - Returns monitor status and alltimeuptimeratio for a MonitorId. + Returns monitor by MonitorId. """ - url = self.baseUrl - url += "getMonitors?apiKey=%s&monitors=%s" % (self.apiKey, monitorId) - url += "&noJsonCallback=1&format=json" - success, response = self.requestApi(url) - if success: - status = response.get('monitors').get('monitor')[0].get('status') - alltimeuptimeratio = response.get('monitors').get('monitor')[0].get('alltimeuptimeratio') - return status, alltimeuptimeratio - return None, None + try: + monitor = self.iterMonitors(monitors=[monitorId]).next() + except StopIteration: + return None + return monitor def getMonitorByName(self, monitorFriendlyName): """ - Returns monitor status and alltimeuptimeratio for a MonitorFriendlyName. + Returns monitor by MonitorFriendlyName. """ - url = self.baseUrl - url += "getMonitors?apiKey=%s" % self.apiKey - url += "&noJsonCallback=1&format=json" - success, response = self.requestApi(url) - if success: - monitors = response.get('monitors').get('monitor') - for i in range(len(monitors)): - monitor = monitors[i] - if monitor.get('friendlyname') == monitorFriendlyName: - status = monitor.get('status') - alltimeuptimeratio = monitor.get('alltimeuptimeratio') - return status, alltimeuptimeratio - return None, None + try: + monitor = self.iterMonitors(search=monitorFriendlyName).next() + except StopIteration: + return None + if monitor['friendlyname'] != monitorFriendlyName: + return None + return monitor def editMonitor(self, monitorID, monitorStatus=None, monitorFriendlyName=None, monitorURL=None, monitorType=None, @@ -221,12 +215,14 @@ def getMonitorId(self, name): def deleteMonitorByName(self, name): return self.deleteMonitorById(self.getMonitorId(name)) - def iterMonitors(self, response_times=0, logs=0, uptime_ratio=''): + def iterMonitors(self, response_times=0, logs=0, uptime_ratio='', search=None, monitors=None): "Iterate all monitors." total = None offset = 0 while total is None or offset < total: - response = self.getMonitors(response_times, logs, uptime_ratio, offset=offset, limit=MONITORS_PER_PAGE) + response = self.getMonitors(response_times, logs, uptime_ratio, offset=offset, limit=MONITORS_PER_PAGE, search=search, monitors=monitors) + if not response[0]: + return if total is None: total = int(response[1]['total']) offset += MONITORS_PER_PAGE From 20524405363f01c3a6d7ce905b17c7c0ddf60f39 Mon Sep 17 00:00:00 2001 From: Joshua Schmidlkofer Date: Thu, 29 Dec 2016 15:53:51 -0800 Subject: [PATCH 3/7] Python2/3 Compatibility Bugfix --- uptimerobot/__init__.py | 2 +- uptimerobot/uptimerobot.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/uptimerobot/__init__.py b/uptimerobot/__init__.py index 4963adc..fb80f39 100644 --- a/uptimerobot/__init__.py +++ b/uptimerobot/__init__.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -__version__ = '0.1.5' +__version__ = '0.1.5.1' diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 8c33ad0..48328f2 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import print_function + try: import urllib.request as urllib_request except ImportError: @@ -238,7 +240,7 @@ def iterMonitors(self, response_times=0, logs=0, uptime_ratio='', search=None, m elif arg.startswith("apiKey="): apiKey = arg.split("=")[1] if not monitorFriendlyName or not monitorURL: - print "Usage: uptimerobot.py monitorFriendlyName=\"name\" monitorURL=\"www.url.com\"" + print ("Usage: uptimerobot.py monitorFriendlyName=\"name\" monitorURL=\"www.url.com\"") sys.exit(1) if not apiKey: From a1c3bf1a0520cd28cd8f35712830ff4875675407 Mon Sep 17 00:00:00 2001 From: Joshua Schmidlkofer Date: Thu, 29 Dec 2016 15:55:05 -0800 Subject: [PATCH 4/7] Add workaround for strange JSON reporting anomaly. --- uptimerobot/uptimerobot.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 48328f2..67468e3 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -164,11 +164,29 @@ def deleteMonitorById(self, monitorID): else: return False - + def strangeJsonUnpack(self, jsonContent): + ''' + some responses have a strange container: "jsonUptimeRobotApi([json content here])" + ''' + jsonContent = jsonContent.strip() + if jsonContent.find("jsonUptimeRobotApi(") == 0: + sys.stderr.write("w") + retval = jsonContent[19:-1] + else: + sys.stderr.write(".") + retval = jsonContent + return retval + def requestApi(self, url): response = urllib_request.urlopen(url) - content = response.read().decode('utf-8') - jContent = json.loads(content) + content = self.strangeJsonUnpack(response.read().decode('utf-8')) + + try: + jContent = json.loads(content) + except: + sys.stderr.write('Content Wrapper Error:\n%s\n' % content) + pass + if jContent.get('stat'): stat = jContent.get('stat') if stat == "ok": From 942d9e36ff7fb93dc69e927f096b13c5a241a111 Mon Sep 17 00:00:00 2001 From: Joshua Schmidlkofer Date: Thu, 29 Dec 2016 18:25:43 -0800 Subject: [PATCH 5/7] API Update: set noJsonCallBack for all requests. --- uptimerobot/uptimerobot.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 67468e3..59fa8c2 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -73,7 +73,8 @@ def getMonitors(self, response_times=0, logs=0, uptime_ratio='', offset=None, li url += "&search=%s" % search if monitors is not None: url += "&monitors=%s" % '-'.join(str(m) for m in monitors) - + pass + url += "&noJsonCallback=1&format=json" return self.requestApi(url) @@ -164,29 +165,10 @@ def deleteMonitorById(self, monitorID): else: return False - def strangeJsonUnpack(self, jsonContent): - ''' - some responses have a strange container: "jsonUptimeRobotApi([json content here])" - ''' - jsonContent = jsonContent.strip() - if jsonContent.find("jsonUptimeRobotApi(") == 0: - sys.stderr.write("w") - retval = jsonContent[19:-1] - else: - sys.stderr.write(".") - retval = jsonContent - return retval - def requestApi(self, url): response = urllib_request.urlopen(url) content = self.strangeJsonUnpack(response.read().decode('utf-8')) - - try: - jContent = json.loads(content) - except: - sys.stderr.write('Content Wrapper Error:\n%s\n' % content) - pass - + jContent = json.loads(content) if jContent.get('stat'): stat = jContent.get('stat') if stat == "ok": @@ -201,11 +183,14 @@ def getAlertContacts(self, alertContacts=None, offset=None, limit=None): url += "getAlertContacts?apiKey=%s" % self.apiKey if alertContacts: url += "&alertContacts=%s" % alertContacts + pass if offset: url += "&offset=%s" % offset + pass if limit: url += "&limit=%s" % limit - url += "&format=json" + pass + url += "&noJsonCallback=1&format=json" return self.requestApi(url) def getAlertContactIds(self, urlFormat=False): From 1db60c28b08597497ca1e1fe6abcfab61347c9b3 Mon Sep 17 00:00:00 2001 From: Joshua Schmidlkofer Date: Thu, 29 Dec 2016 18:28:44 -0800 Subject: [PATCH 6/7] API Update: set noJsonCallBack for all requests. --- uptimerobot/uptimerobot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 59fa8c2..565b671 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -167,7 +167,7 @@ def deleteMonitorById(self, monitorID): def requestApi(self, url): response = urllib_request.urlopen(url) - content = self.strangeJsonUnpack(response.read().decode('utf-8')) + content = response.read().decode('utf-8') jContent = json.loads(content) if jContent.get('stat'): stat = jContent.get('stat') From 0b923cda7aac5e3071f57387fc0e5ca1517bf869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Qui=C3=B1ones?= Date: Fri, 30 Dec 2016 10:42:32 -0300 Subject: [PATCH 7/7] Remove 'pass' lines, style --- uptimerobot/uptimerobot.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/uptimerobot/uptimerobot.py b/uptimerobot/uptimerobot.py index 565b671..9c664dc 100755 --- a/uptimerobot/uptimerobot.py +++ b/uptimerobot/uptimerobot.py @@ -73,7 +73,6 @@ def getMonitors(self, response_times=0, logs=0, uptime_ratio='', offset=None, li url += "&search=%s" % search if monitors is not None: url += "&monitors=%s" % '-'.join(str(m) for m in monitors) - pass url += "&noJsonCallback=1&format=json" return self.requestApi(url) @@ -183,13 +182,10 @@ def getAlertContacts(self, alertContacts=None, offset=None, limit=None): url += "getAlertContacts?apiKey=%s" % self.apiKey if alertContacts: url += "&alertContacts=%s" % alertContacts - pass if offset: url += "&offset=%s" % offset - pass if limit: url += "&limit=%s" % limit - pass url += "&noJsonCallback=1&format=json" return self.requestApi(url) @@ -243,7 +239,7 @@ def iterMonitors(self, response_times=0, logs=0, uptime_ratio='', search=None, m elif arg.startswith("apiKey="): apiKey = arg.split("=")[1] if not monitorFriendlyName or not monitorURL: - print ("Usage: uptimerobot.py monitorFriendlyName=\"name\" monitorURL=\"www.url.com\"") + print("Usage: uptimerobot.py monitorFriendlyName=\"name\" monitorURL=\"www.url.com\"") sys.exit(1) if not apiKey: