diff --git a/logicmonitor_core/Collector.py b/logicmonitor_core/Collector.py index 5471910..1df397a 100755 --- a/logicmonitor_core/Collector.py +++ b/logicmonitor_core/Collector.py @@ -20,6 +20,7 @@ def __init__(self, params): logging.debug("Instantiating Collector object") self.change = False self.params = params + self.api = self.rpc LogicMonitor.__init__(self, **params) @@ -353,11 +354,11 @@ def sdt(self): start = datetime.utcnow() # Use user UTC offset - logging.debug("Making RPC call to 'getTimeZoneSetting'") - accountresp = json.loads(self.rpc("getTimeZoneSetting", {})) + logging.debug("Making API call to 'getTimeZoneSetting'") + accountresp = json.loads(self.api("getTimeZoneSetting", {})) if accountresp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") offset = accountresp["data"]["offset"] offsetstart = start + timedelta(0, offset) @@ -380,14 +381,14 @@ def sdt(self): "endHour": offsetend.hour, "endMinute": offsetend.minute} - logging.debug("Making RPC call to 'setAgentSDT'") - resp = json.loads(self.rpc("setAgentSDT", h)) + logging.debug("Making API call to 'setAgentSDT'") + resp = json.loads(self.api("setAgentSDT", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail(msg=resp["errmsg"]) def site_facts(self): @@ -452,11 +453,11 @@ def _create(self): h = {"autogen": True, "description": self.description} - logging.debug("Making RPC call to 'addAgent'") - create = (json.loads(self.rpc("addAgent", h))) + logging.debug("Making API call to 'addAgent'") + create = (json.loads(self.api("addAgent", h))) if create["status"] is 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") self.info = create["data"] self.id = create["data"]["id"] return create["data"] @@ -488,12 +489,12 @@ def _unregister(self): if self.check_mode: self.exit(changed=True) - logging.debug("Making RPC call to 'deleteAgent'") - delete = json.loads(self.rpc("deleteAgent", + logging.debug("Making API call to 'deleteAgent'") + delete = json.loads(self.api("deleteAgent", {"id": self.id})) if delete["status"] is 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return delete else: # The collector couldn't unregister. Start the service again diff --git a/logicmonitor_core/Datasource.py b/logicmonitor_core/Datasource.py index 84debe6..6263cb8 100644 --- a/logicmonitor_core/Datasource.py +++ b/logicmonitor_core/Datasource.py @@ -14,6 +14,7 @@ def __init__(self, params): logging.debug("Instantiating Datasource object") self.change = False self.params = params + self.api = self.rpc LogicMonitor.__init__(self, **params) @@ -45,11 +46,11 @@ def sdt(self): start = datetime.utcnow() # Use user UTC offset - logging.debug("Making RPC call to 'getTimeZoneSetting'") - accountresp = json.loads(self.rpc("getTimeZoneSetting", {})) + logging.debug("Making API call to 'getTimeZoneSetting'") + accountresp = json.loads(self.api("getTimeZoneSetting", {})) if accountresp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") offset = accountresp["data"]["offset"] offsetstart = start + timedelta(0, offset) @@ -72,12 +73,12 @@ def sdt(self): "endHour": offsetend.hour, "endMinute": offsetend.minute} - logging.debug("Making RPC call to 'setHostDataSourceSDT'") - resp = json.loads(self.rpc("setHostDataSourceSDT", h)) + logging.debug("Making API call to 'setHostDataSourceSDT'") + resp = json.loads(self.api("setHostDataSourceSDT", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail(msg=resp["errmsg"]) diff --git a/logicmonitor_core/Host.py b/logicmonitor_core/Host.py index c978a30..0582feb 100755 --- a/logicmonitor_core/Host.py +++ b/logicmonitor_core/Host.py @@ -15,6 +15,7 @@ def __init__(self, params): self.change = False self.params = params self.collector = None + self.api = self.rpc LogicMonitor.__init__(self, **self.params) @@ -89,13 +90,13 @@ def get_properties(self): logging.debug("Running Host.get_properties...") if self.info: - logging.debug("Making RPC call to 'getHostProperties'") - properties_json = (json.loads(self.rpc("getHostProperties", + logging.debug("Making API call to 'getHostProperties'") + properties_json = (json.loads(self.api("getHostProperties", {'hostId': self.info["id"], "filterSystemProperties": True}))) if properties_json["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return properties_json["data"] else: logging.debug("Error: there was an issue retrieving the " + @@ -144,14 +145,14 @@ def add(self): self.properties, self.alertenable) - logging.debug("Making RPC call to 'addHost'") - resp = json.loads(self.rpc("addHost", h)) + logging.debug("Making API call to 'addHost'") + resp = json.loads(self.api("addHost", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") logging.debug(resp) return resp["errmsg"] elif self.collector is None: @@ -185,13 +186,13 @@ def update(self): h["id"] = self.info["id"] h["opType"] = "replace" - logging.debug("Making RPC call to 'updateHost'") - resp = json.loads(self.rpc("updateHost", h)) + logging.debug("Making API call to 'updateHost'") + resp = json.loads(self.api("updateHost", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail(msg="Error: unable to update the host.") else: logging.debug("Host properties match supplied properties. " + @@ -219,18 +220,18 @@ def remove(self): if self.check_mode: self.exit(changed=True) - logging.debug("Making RPC call to 'deleteHost'") - resp = json.loads(self.rpc("deleteHost", + logging.debug("Making API call to 'deleteHost'") + resp = json.loads(self.api("deleteHost", {"hostId": self.info["id"], "deleteFromSystem": True, "hostGroupId": 1})) if resp["status"] == 200: logging.debug(resp) - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp else: - logging.debug("RPC call failed") + logging.debug("API call failed") logging.debug(resp) self.fail(msg=resp["errmsg"]) @@ -308,11 +309,11 @@ def sdt(self): start = datetime.utcnow() # Use user UTC offset - logging.debug("Making RPC call to 'getTimeZoneSetting'") - accountresp = (json.loads(self.rpc("getTimeZoneSetting", {}))) + logging.debug("Making API call to 'getTimeZoneSetting'") + accountresp = (json.loads(self.api("getTimeZoneSetting", {}))) if accountresp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") offset = accountresp["data"]["offset"] offsetstart = start + timedelta(0, offset) @@ -335,14 +336,14 @@ def sdt(self): "endHour": offsetend.hour, "endMinute": offsetend.minute} - logging.debug("Making RPC call to 'setHostSDT'") - resp = (json.loads(self.rpc("setHostSDT", h))) + logging.debug("Making API call to 'setHostSDT'") + resp = (json.loads(self.api("setHostSDT", h))) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail(msg=resp["errmsg"]) else: self.fail(msg="Error: Host doesn't exit.") @@ -422,10 +423,10 @@ def _verify_property(self, propname): "propValue0": self.properties[propname]} logging.debug("Making RCP call to 'verifyProperties'") - resp = json.loads(self.rpc('verifyProperties', h)) + resp = json.loads(self.api('verifyProperties', h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"]["match"] else: self.fail( @@ -447,7 +448,7 @@ def _compare_groups(self, hostresp): if path != []: h = {'hostGroupId': path[-1]} - hgresp = json.loads(self.rpc("getHostGroup", h)) + hgresp = json.loads(self.api("getHostGroup", h)) if (hgresp["status"] == 200 and hgresp["data"]["appliesTo"] == ""): diff --git a/logicmonitor_core/HostList.py b/logicmonitor_core/HostList.py index aeb5f92..4398fab 100644 --- a/logicmonitor_core/HostList.py +++ b/logicmonitor_core/HostList.py @@ -34,13 +34,13 @@ def get_hosts(self): associated with this LogicMonitor account""" logging.debug("Running HostList.get_hosts...") - logging.debug("Making RPC call to 'getHosts'") - properties_json = (json.loads(self.rpc("getHosts", + logging.debug("Making API call to 'getHosts'") + properties_json = (json.loads(self.api("getHosts", {"hostGroupId": self.groupId or 1}))) if properties_json["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return properties_json["data"] else: logging.debug("Error: there was an issue retrieving the " + diff --git a/logicmonitor_core/Hostgroup.py b/logicmonitor_core/Hostgroup.py index 9f22f36..cc05137 100755 --- a/logicmonitor_core/Hostgroup.py +++ b/logicmonitor_core/Hostgroup.py @@ -24,6 +24,7 @@ def __init__(self, params): self.starttime = self.params["starttime"] self.duration = self.params["duration"] self.alertenable = self.params["alertenable"] + self.api = self.rpc def create(self): """Wrapper for self.update()""" @@ -38,17 +39,17 @@ def get_properties(self, final=False): if self.info: logging.debug("Group found") - logging.debug("Making RPC call to 'getHostGroupProperties'") - properties_json = json.loads(self.rpc( + logging.debug("Making API call to 'getHostGroupProperties'") + properties_json = json.loads(self.api( "getHostGroupProperties", {'hostGroupId': self.info["id"], "finalResult": final})) if properties_json["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return properties_json["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail(msg=properties_json["status"]) else: logging.debug("Group not found") @@ -113,14 +114,14 @@ def update(self): if self.fullpath != "/": h["id"] = self.info["id"] - logging.debug("Making RPC call to 'updateHostGroup'") - resp = json.loads(self.rpc("updateHostGroup", h)) + logging.debug("Making API call to 'updateHostGroup'") + resp = json.loads(self.api("updateHostGroup", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail( msg="Error: Unable to update the " + "host.\n" + resp["errmsg"]) @@ -152,18 +153,18 @@ def remove(self): if self.check_mode: self.exit(changed=True) - logging.debug("Making RPC call to 'deleteHostGroup'") - resp = json.loads(self.rpc("deleteHostGroup", + logging.debug("Making API call to 'deleteHostGroup'") + resp = json.loads(self.api("deleteHostGroup", {"hgId": self.info["id"]})) if resp["status"] == 200: logging.debug(resp) - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp elif resp["errmsg"] == "No such group": logging.debug("Group doesn't exist") else: - logging.debug("RPC call failed") + logging.debug("API call failed") logging.debug(resp) self.fail(msg=resp["errmsg"]) else: @@ -229,11 +230,11 @@ def sdt(self, duration=30, starttime=None): start = datetime.utcnow() # Use user UTC offset - logging.debug("Making RPC call to 'getTimeZoneSetting'") - accountresp = json.loads(self.rpc("getTimeZoneSetting", {})) + logging.debug("Making API call to 'getTimeZoneSetting'") + accountresp = json.loads(self.api("getTimeZoneSetting", {})) if accountresp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") offset = accountresp["data"]["offset"] offsetstart = start + timedelta(0, offset) @@ -256,14 +257,14 @@ def sdt(self, duration=30, starttime=None): "endHour": offsetend.hour, "endMinute": offsetend.minute} - logging.debug("Making RPC call to setHostGroupSDT") - resp = json.loads(self.rpc("setHostGroupSDT", h)) + logging.debug("Making API call to setHostGroupSDT") + resp = json.loads(self.api("setHostGroupSDT", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail(msg=resp["errmsg"]) def site_facts(self): @@ -338,10 +339,10 @@ def _verify_property(self, propname): "propValue0": self.properties[propname]} logging.debug("Making RCP call to 'verifyProperties'") - resp = json.loads(self.rpc('verifyProperties', h)) + resp = json.loads(self.api('verifyProperties', h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"]["match"] else: self.fail( diff --git a/logicmonitor_core/LogicMonitor.py b/logicmonitor_core/LogicMonitor.py index f1140f7..04ba656 100755 --- a/logicmonitor_core/LogicMonitor.py +++ b/logicmonitor_core/LogicMonitor.py @@ -1,9 +1,14 @@ #!/usr/bin/python +import base64 +import hashlib +import hmac import json import logging +import requests import socket import sys +import time import urllib import urllib2 @@ -16,14 +21,15 @@ def __init__(self, **params): self.check_mode = False self.company = params["company"] - self.user = params["user"] - self.password = params["password"] + self.accessid = params['accessid'] + self.accesskey = params['accesskey'] self.fqdn = socket.getfqdn() self.lm_url = "logicmonitor.com/santaba" self.urlopen = urllib2.urlopen + self.api = self.rpc def rpc(self, action, params): - """Make a call to the LogicMonitor RPC library + """Make a call to the LogicMonitor API library and return the response""" logging.debug("Running LogicMonitor.rpc") @@ -56,7 +62,7 @@ def rpc(self, action, params): return raw except IOError as ioe: logging.debug(ioe) - self.fail(msg="Error: Unknown exception making RPC call") + self.fail(msg="Error: Unknown exception making API call") def do(self, action, params): """Make a call to the LogicMonitor @@ -76,7 +82,8 @@ def do(self, action, params): try: # log param string without credentials logging.debug("Attempting to open URL: " + - "https://" + self.company + "." + self.lm_url + + "https://" + self.company + "." + + self.lm_url + "/do/" + action + "?" + param_str) f = self.urlopen( "https://" + self.company + "." + self.lm_url + @@ -86,17 +93,89 @@ def do(self, action, params): logging.debug("Error opening URL. " + ioe) self.fail("Unknown exception opening URL") + def rest(self, path, method, data=None): + '''Make a call to the LogicMonitor server REST API''' + logging.debug("Running LogicMonitor.rest...") + + if method == 'PATCH' or method == 'POST' and data is None: + self.fail('Message body required for method ' + method) + + # convert data to json + if data is not None: + data = json.dumps(data) + try: + url = 'https://' + self.company + '.' + self.lm_url + ' + /rest' + path + + auth_header = self.get_auth_header(path, method, data) + headers = {'Content-Type': 'application/json', + 'Authorization': auth_header} + + logging.debug('Sending ' + method + ' to: ' + url) + resp = '' + if method == 'DELETE': + resp = requests.delete(url, headers=headers) + elif method == 'GET': + resp = requests.get(url, headers=headers) + elif method == 'PATCH': + resp = requests.patch(url, data=data, headers=headers) + elif method == 'POST': + resp = requests.post(url, data=data, headers=headers) + elif method == 'PUT': + resp = requests.put(url, data=data, headers=headers) + else: + self.fail('Invalid method ' + method + 'specified') + + if resp.status_code != 200: + logging.error('HTTP response ' + str(resp.status_code) + + ' from API while making ' + method + + ' request to ' + url) + else: + logging.debug('Successful API call to ' + url) + return resp + except Exception as e: + logging.error('Error making API request: ' + e.message) + return None + + def get_auth_header(self, path, method, data): + '''Construct an REST API authentication header''' + logging.debug("Running LogicMonitor.get_auth_header...") + + if self.accesskey is None or self.accessid is None: + self.fail('Must specify Access Key and Access ID for ' + + 'authenticating') + + epoch = str(int(time.time() * 1000)) + + # concatenate request details + msg = '' + if data is None: + msg = method + epoch + path + else: + msg = method + epoch + data + path + + # construct signature + digest = hmac.new(self.accesskey, + msg=msg, + digestmod=hashlib.sha256).hexdigest() + signature = base64.b64encode(digest) + + # construct header + auth = ('LMv1 ' + self.accessid + ':' + signature + ':' + epoch) + + return auth + def get_collectors(self): """Returns a JSON object containing a list of LogicMonitor collectors""" logging.debug("Running LogicMonitor.get_collectors...") - logging.debug("Making RPC call to 'getAgents'") - resp = self.rpc("getAgents", {}) + logging.debug("Making API call to 'getAgents'") + resp = self.api("getAgents", {}) resp_json = json.loads(resp) if resp_json["status"] is 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp_json["data"] else: self.fail(msg=resp) @@ -107,12 +186,12 @@ def get_host_by_hostname(self, hostname, collector): logging.debug("Running LogicMonitor.get_host_by_hostname...") logging.debug("Looking for hostname " + hostname) - logging.debug("Making RPC call to 'getHosts'") - hostlist_json = json.loads(self.rpc("getHosts", {"hostGroupId": 1})) + logging.debug("Making API call to 'getHosts'") + hostlist_json = json.loads(self.api("getHosts", {"hostGroupId": 1})) if collector: if hostlist_json["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") hosts = hostlist_json["data"]["hosts"] @@ -129,7 +208,7 @@ def get_host_by_hostname(self, hostname, collector): logging.debug("No host match found") return None else: - logging.debug("RPC call failed") + logging.debug("API call failed") logging.debug(hostlist_json) else: logging.debug("No collector specified") @@ -141,15 +220,15 @@ def get_host_by_displayname(self, displayname): logging.debug("Running LogicMonitor.get_host_by_displayname...") logging.debug("Looking for displayname " + displayname) - logging.debug("Making RPC call to 'getHost'") - host_json = (json.loads(self.rpc("getHost", + logging.debug("Making API call to 'getHost'") + host_json = (json.loads(self.api("getHost", {"displayName": displayname}))) if host_json["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return host_json["data"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") logging.debug(host_json) return None @@ -174,11 +253,11 @@ def get_group(self, fullpath): specified path""" logging.debug("Running LogicMonitor.get_group...") - logging.debug("Making RPC call to getHostGroups") - resp = json.loads(self.rpc("getHostGroups", {})) + logging.debug("Making API call to getHostGroups") + resp = json.loads(self.api("getHostGroups", {})) if resp["status"] == 200: - logging.debug("RPC called succeeded") + logging.debug("API called succeeded") groups = resp["data"] logging.debug("Looking for group matching " + fullpath) @@ -190,7 +269,7 @@ def get_group(self, fullpath): logging.debug("No group match found") return None else: - logging.debug("RPC call failed") + logging.debug("API call failed") logging.debug(resp) return None @@ -245,19 +324,19 @@ def create_group(self, fullpath): "alertEnable": True, "description": ""} - logging.debug("Making RPC call to 'addHostGroup'") + logging.debug("Making API call to 'addHostGroup'") resp = json.loads( - self.rpc("addHostGroup", h)) + self.api("addHostGroup", h)) if resp["status"] == 200: - logging.debug("RPC call succeeded") + logging.debug("API call succeeded") return resp["data"]["id"] elif resp["errmsg"] == "The record already exists": logging.debug("The hostgroup already exists") group = self.get_group(fullpath) return group["id"] else: - logging.debug("RPC call failed") + logging.debug("API call failed") self.fail( msg="Error: unable to create new hostgroup \"" + name + "\".\n" + resp["errmsg"]) diff --git a/setup.py b/setup.py index a06892f..76dab86 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup(name='logicmonitor_core', - version='1.0.7.5', + version='2.0.0.0', description='LogicMonitor API python classes', long_description='LogicMonitor is a cloud-based, full stack, IT \ infrastructure monitoring solution that allows you to manage your \