def main(): npm_server = 'localhost' username = '******' password = '' swis = SwisClient(npm_server, username, password) print("Add an SNMP v2c node:") # fill these in for the node you want to add! ip_address = '127.0.0.1' community = 'public' # set up property bag for the new node props = { 'IPAddress': ip_address, 'EngineID': 1, 'ObjectSubType': 'SNMP', 'SNMPVersion': 2, 'Community': community } print("Adding node {}... ".format(props['IPAddress']), end="") results = swis.create('Orion.Nodes', **props) print("DONE!") # extract the nodeID from the result nodeid = re.search('(\d+)$', results).group(0) pollers_enabled = { 'N.Status.ICMP.Native': True, 'N.Status.SNMP.Native': False, 'N.ResponseTime.ICMP.Native': True, 'N.ResponseTime.SNMP.Native': False, 'N.Details.SNMP.Generic': True, 'N.Uptime.SNMP.Generic': True, 'N.Cpu.SNMP.HrProcessorLoad': True, 'N.Memory.SNMP.NetSnmpReal': True, 'N.AssetInventory.Snmp.Generic': True, 'N.Topology_Layer3.SNMP.ipNetToMedia': False, 'N.Routing.SNMP.Ipv4CidrRoutingTable': False } pollers = [] for k in pollers_enabled: pollers.append( { 'PollerType': k, 'NetObject': 'N:' + nodeid, 'NetObjectType': 'N', 'NetObjectID': nodeid, 'Enabled': pollers_enabled[k] } ) for poller in pollers: print(" Adding poller type: {} with status {}... ".format(poller['PollerType'], poller['Enabled']), end="") response = swis.create('Orion.Pollers', **poller) print("DONE!")
def main(): npm_server = 'localhost' username = '******' password = '' swis = SwisClient(npm_server, username, password) print("Add an SNMP v2c node:") # fill these in for the node you want to add! ip_address = '127.0.0.1' community = 'public' # set up property bag for the new node props = { 'IPAddress': ip_address, 'EngineID': 1, 'ObjectSubType': 'SNMP', 'SNMPVersion': 2, 'Community': community, 'DNS': '', 'SysName': '' } print("Adding node {}... ".format(props['IPAddress']), end="") results = swis.create('Orion.Nodes', **props) print("DONE!") # extract the nodeID from the result nodeid = re.search(r'(\d+)$', results).group(0) pollers_enabled = { 'N.Status.ICMP.Native': True, 'N.Status.SNMP.Native': False, 'N.ResponseTime.ICMP.Native': True, 'N.ResponseTime.SNMP.Native': False, 'N.Details.SNMP.Generic': True, 'N.Uptime.SNMP.Generic': True, 'N.Cpu.SNMP.HrProcessorLoad': True, 'N.Memory.SNMP.NetSnmpReal': True, 'N.AssetInventory.Snmp.Generic': True, 'N.Topology_Layer3.SNMP.ipNetToMedia': False, 'N.Routing.SNMP.Ipv4CidrRoutingTable': False } pollers = [] for k in pollers_enabled: pollers.append({ 'PollerType': k, 'NetObject': 'N:' + nodeid, 'NetObjectType': 'N', 'NetObjectID': nodeid, 'Enabled': pollers_enabled[k] }) for poller in pollers: print(" Adding poller type: {} with status {}... ".format( poller['PollerType'], poller['Enabled']), end="") response = swis.create('Orion.Pollers', **poller) print("DONE!")
def main(): # Connect to SWIS server = 'localhost' username = '******' password = '' swis = SwisClient(server, username, password) engine_id = 1 node_caption = 'example.com' node_props = { 'IPAddress': '10.0.0.1', 'EngineID': engine_id, 'Caption': node_caption, 'ObjectSubType': 'SNMP', 'Community': 'public', 'SNMPVersion': 2, 'DNS': '', 'SysName': '' } # Add node swis.create('Orion.Nodes', **node_props) query_results = swis.query( 'SELECT NodeID FROM Orion.Nodes WHERE Caption = @caption_par', caption_par=node_caption) node_id = query_results['results'][0]['NodeID'] print('New node with ID {0} created'.format(node_id)) # Discovere and add interfaces results = swis.invoke('Orion.NPM.Interfaces', 'DiscoverInterfacesOnNode', node_id) swis.invoke('Orion.NPM.Interfaces', 'AddInterfacesOnNode', node_id, results['DiscoveredInterfaces'], 'AddDefaultPollers') query_results = swis.query( 'SELECT InterfaceID FROM Orion.NPM.Interfaces WHERE NodeID = @node_id_par', node_id_par=node_id) print('Discovered and added {0} interfaces for node with id {1}'.format( len(query_results['results']), node_id)) interface_ids = [r['InterfaceID'] for r in query_results['results']] # Add Flow sources for every interface - enable flow collection on every interface swis.invoke('Orion.Netflow.Source', 'EnableFlowSources', interface_ids, 'AddDefaultPollers') query_results = swis.query( 'SELECT NetflowSourceID FROM Orion.Netflow.Source WHERE NodeID = @node_id_par', node_id_par=node_id) print('Added {0} Flow sources for node with id {1}'.format( len(query_results['results']), node_id))
def addNodeToOrion(self,orion_server,client_address,service_description='',engine_id=3): # disable SSL warnings requests.packages.urllib3.disable_warnings() # connect to orion API port=17778 npm = SwisClient(orion_server, self.username, self.password) # define node properties # EngineID can be found in the solarwind orion database server props = {'IPAddress': client_address, 'ObjectSubType': 'ICMP', 'EngineID': engine_id, 'NodeName': service_description} # add the node try : record = npm.create('Orion.Nodes', **props) #extract the node id nodeid = re.findall(r'(\d+)$',record)[0] #enable ICMP polling pollers_enabled = {'N.Status.ICMP.Native': True, 'N.ResponseTime.ICMP.Native': True} # define pollers properties in a dictionary and create a list of those dicts pollers_props = [] for poller in pollers_enabled: pollers_props.append({'PollerType': poller, 'NetObject': 'N:' + nodeid, 'NetObjectType': 'N', 'NetObjectID': nodeid, 'Enabled': pollers_enabled[poller]}) # update the pollers in orion for pollers in pollers_props: npm.create('Orion.Pollers', **pollers) # poll the node npm.invoke('Orion.Nodes', 'PollNow', 'N:' + nodeid) return True except ConnectionError: return ConnectionError except requests.exceptions.HTTPError: return AttributeError # wrong credentials except Exception as e: return '%s\n%s' %(str(type(e)),str(e))
class NodeManager: def __init__(self, username, password): # Change 'orion' to your Orion NPM server name or IP. self._swis = SwisClient('orion', username, password) def add_node(self, **node): self._node_element(**node) self._node_pollers() self._node_custom_props(**node) self._poll() self._node_ncm(**node) def _node_element(self, **node): # Extract IP Address and node name from node variables ip_address = node['IP_Address'] node_name = node['Caption'] # Setup properties for the new node props = { 'IPAddress': ip_address, 'EngineID': 5, 'ObjectSubType': 'SNMP', 'SNMPVersion': 3, 'Caption': node_name, 'SNMPV3AuthKey': 'YourKey', # Enter your key here 'SNMPv3AuthKeyIsPwd': True, 'SNMPv3AuthMethod': 'SHA1', 'SNMPv3PrivKey': 'YourKey', # Enter your key here 'SNMPv3PrivKeyIsPwd': True, 'SNMPv3PrivMethod': 'AES128', 'SNMPV3Username': '******' # Enter your SNMPv3 Username here } # Create the node print("Adding node {}... ".format(props['IPAddress']), end="") self._results = self._swis.create('Orion.Nodes', **props) print("DONE!") # Extract nodeID from the results self._nodeid = self._parse_node(self._results) def _parse_node(self, results): return re.search('(\d+)$', self._results).group(0) def _node_pollers(self): # Setup poller status for the node pollers_enabled = { 'N.Status.ICMP.Native': True, 'N.Status.SNMP.Native': False, 'N.ResponseTime.ICMP.Native': True, 'N.ResponseTime.SNMP.Native': False, 'N.Details.SNMP.Generic': True, 'N.Uptime.SNMP.Generic': True, 'N.Cpu.SNMP.HrProcessorLoad': True, 'N.Memory.SNMP.NetSnmpReal': True, 'N.AssetInventory.Snmp.Generic': True, 'N.Topology_Layer3.SNMP.ipNetToMedia': False, 'N.Routing.SNMP.Ipv4CidrRoutingTable': False } pollers = [] for k in pollers_enabled: pollers.append({ 'PollerType': k, 'NetObject': 'N:' + self._nodeid, 'NetObjectType': 'N', 'NetObjectID': self._nodeid, 'Enabled': pollers_enabled[k] }) # Add node to pollers for poller in pollers: print(" Adding poller type: {} with status {}... ".format( poller['PollerType'], poller['Enabled']), end="") response = self._swis.create('Orion.Pollers', **poller) print("DONE!") def _node_custom_props(self, **node): # Copy 'node' dict to new dict 'props' and remove keys not relative to custom properties props = node ignore_keys = ('IPAddress', 'Caption', 'NodeGroup', 'DeviceTemplate', 'ConnectionProfile') for k in ignore_keys: props.pop(k, None) # Add custom properties to node for k, v in props.items(): print(" Adding custom property: {} with value {}... ".format( k, v), end="") self._swis.update(self._results + '/CustomProperties', **{k: v}) print("DONE!") def _poll(self): # Poll the node print(" Polling node... ", end="") self._swis.invoke('Orion.Nodes', 'PollNow', 'N:' + self._nodeid) print("DONE!") def _node_ncm(self, **node): # Add node to NCM self._swis.invoke('Cirrus.Nodes', 'AddNodeToNCM', self._nodeid) # Lookup the NCM NodeID, which is a Guid ncmNodeID = self._swis.query( 'SELECT NodeID FROM Cirrus.Nodes WHERE CoreNodeID=@node', node=self._nodeid)['results'][0]['NodeID'] # Fetch the NCM Node object ncmNode = self._swis.invoke('Cirrus.Nodes', 'GetNode', ncmNodeID) # Modify our local copy of the NCM Node object ncmNode['ConnectionProfile'] = node['ConnectionProfile'] ncmNode['DeviceTemplate'] = node['DeviceTemplate'] ncmNode['NodeGroup'] = node['NodeGroup'] # Commit our changes print(" Adding node to NCM... ", end="") self._swis.invoke('Cirrus.Nodes', 'UpdateNode', ncmNode) print("DONE!")
class OrionBaseAction(Action): def __init__(self, config): super(OrionBaseAction, self).__init__(config) self.client = None if "orion_host" not in self.config: raise ValueError("Orion host details not in the config.yaml") elif "orion_user" not in self.config: raise ValueError("Orion user details not in the config.yaml") elif "orion_password" not in self.config: raise ValueError("Orion password details not in the config.yaml") def connect(self): """ Connect to the Orion server listed in the config. """ self.client = SwisClient(self.config['orion_host'], self.config['orion_user'], self.config['orion_password']) return self.config['orion_label'] def get_node(self, node): """ Get an OrionNode object """ orion_node = OrionNode() if is_ip(node): query_for_where = "IPAddress" else: query_for_where = "Caption" swql = """SELECT NodeID, Uri, IPAddress, Caption FROM Orion.Nodes WHERE {}=@query_on""".format(query_for_where) kargs = {'query_on': node} data = self.query(swql, **kargs) if 'results' not in data: msg = "No results from Orion: {}".format(data) self.logger.info(msg) raise Exception(msg) if len(data['results']) == 1: try: orion_node.npm_id = data['results'][0]['NodeID'] orion_node.uri = data['results'][0]['Uri'] orion_node.ip_address = data['results'][0]['IPAddress'] orion_node.caption = data['results'][0]['Caption'] except IndexError: pass elif len(data['results']) >= 2: self.logger.debug( "Muliple Nodes match '{}' Caption: {}".format( node, data)) raise ValueError("Muliple Nodes match '{}' Caption".format( node)) if orion_node.npm: swql = """SELECT NodeID FROM Cirrus.Nodes WHERE CoreNodeID=@CoreNodeID""" kargs = {'CoreNodeID': orion_node.npm_id} data = self.query(swql, **kargs) # Don't raise an exception if this fails. # The platform may not haev NCM installed. if 'results' not in data: msg = "No results from Orion NCM: {}".format(data) self.logger.info(msg) elif len(data['results']) == 1: try: orion_node.ncm_id = data['results'][0]['NodeID'] except IndexError: pass return orion_node def query(self, swql, **kargs): """ Run SWQL against the Orion Platform. """ return self.client.query(swql, **kargs) def invoke(self, entity, verb, *args): """ Run an Invoke against the Orion Platform. """ return self.client.invoke(entity, verb, *args) def create(self, entity, **kargs): """ Run an Create against the Orion Platform. """ return self.client.create(entity, **kargs) def read(self, uri): """ Run an Read against the Orion Platform. """ return self.client.read(uri) def update(self, uri, **kargs): """ Run an Update against the Orion Platform. """ return self.client.update(uri, **kargs) def delete(self, uri): """ Run an Delete of an URI against the Orion Platform. """ return self.client.delete(uri) def get_snmp_community(self, community): """ Return the correct SNMP comminity to use. """ if community == "customer": return self.config['snmp_customer'] elif community == "internal": return self.config['snmp_internal'] elif community is None: return self.config['snmp_default'] else: return community def get_snmp_cred_id(self, community): """ Look up an SNMP community in the config and then look up the Orion ID for the Credential. """ # Check if the community should be replaced. name = self.get_snmp_community(community) swql = """SELECT ID FROM Orion.Credential WHERE CredentialType=@CredentialType and Name=@name""" kargs = {'CredentialType': 'SolarWinds.Orion.Core.Models.Credentials.SnmpCredentialsV2', 'name': name} orion_data = self.query(swql, **kargs) if len(orion_data['results']) == 1: return orion_data['results'][0]['ID'] else: msg = "Could not get ID for community in Orion.Credential: {}".format( community) send_user_error(msg) raise ValueError(msg) def get_engine_id(self, poller): """ Takes a poller name (or primary) and returns the EngineID for the poller. Raises: ValueError on an invaild poller. Returns: The EngineID (int) """ if poller == "primary": return 1 else: swql = """SELECT EngineID, ServerName, IP, ServerType FROM Orion.Engines WHERE ServerName=@poller""" kargs = {'poller': poller} data = self.query(swql, **kargs) if len(data['results']) == 1: return data['results'][0]['EngineID'] else: send_user_error("Invalid poller name") raise ValueError("Invalid poller name") def get_ncm_transfer_results(self, transfer_id, sleep_delay=10): """ Gets the completed (waits until finished). NCM job transfer status from Orion. Retruns: The completed status. """ ts = {} while True: swql = """SELECT TransferID, NodeID, Action, RequestedConfigType, RequestedScript, RequestedReboot, ConfigID, TransferProtocol, Status, ErrorMessage, DeviceOutput, DateTime, UserName FROM NCM.TransferResults WHERE TransferID=@transfer_id""" kargs = {'transfer_id': transfer_id} transfer_data = self.query(swql, **kargs) status = transfer_data['results'][0]['Status'] if status == 1: time.sleep(sleep_delay) elif status == 2: ts['status'] = "Complete" break elif status == 3: ts['status'] = "Error" break else: ts['status'] = "Unknown" break ts['RequestedScript'] = transfer_data['results'][0]['RequestedScript'] ts['RequestedReboot'] = transfer_data['results'][0]['RequestedReboot'] ts['ErrorMessage'] = transfer_data['results'][0]['ErrorMessage'] ts['DeviceOutput'] = transfer_data['results'][0]['DeviceOutput'] ts['UserName'] = transfer_data['results'][0]['UserName'] return ts
class OrionBaseAction(Action): def __init__(self, config): super(OrionBaseAction, self).__init__(config) self.client = None if "orion_host" not in self.config: raise ValueError("Orion host details not in the config.yaml") elif "orion_user" not in self.config: raise ValueError("Orion user details not in the config.yaml") elif "orion_password" not in self.config: raise ValueError("Orion password details not in the config.yaml") def connect(self): """ Connect to the Orion server listed in the config. """ self.client = SwisClient(self.config['orion_host'], self.config['orion_user'], self.config['orion_password']) return self.config['orion_label'] def get_node(self, node): """ Get an OrionNode object """ orion_node = OrionNode() if is_ip(node): query_for_where = "IPAddress" else: query_for_where = "Caption" swql = """SELECT NodeID, Uri, IPAddress, Caption FROM Orion.Nodes WHERE {}=@query_on""".format(query_for_where) kargs = {'query_on': node} data = self.query(swql, **kargs) if 'results' not in data: msg = "No results from Orion: {}".format(data) self.logger.info(msg) raise Exception(msg) if len(data['results']) == 1: try: orion_node.npm_id = data['results'][0]['NodeID'] orion_node.uri = data['results'][0]['Uri'] orion_node.ip_address = data['results'][0]['IPAddress'] orion_node.caption = data['results'][0]['Caption'] except IndexError: pass elif len(data['results']) >= 2: self.logger.debug("Muliple Nodes match '{}' Caption: {}".format( node, data)) raise ValueError("Muliple Nodes match '{}' Caption".format(node)) if orion_node.npm: swql_ncm = """SELECT NodeID FROM Cirrus.Nodes WHERE CoreNodeID=@CoreNodeID""" kargs = {'CoreNodeID': orion_node.npm_id} try: data_ncm = self.query(swql_ncm, **kargs) except requests.exceptions.HTTPError: # Create an empty dict to allow remaining code to fail gracefully data_ncm = {} self.logger.info("Connection to NCM failed. NCM not installed") # Don't raise an exception if this fails. # The platform may not have NCM installed. if 'results' not in data_ncm: msg = "No results from Orion NCM: {}".format(data_ncm) self.logger.info(msg) elif len(data_ncm['results']) == 1: try: orion_node.ncm_id = data_ncm['results'][0]['NodeID'] except IndexError: pass data_agent = self.get_agent(orion_node.npm_id) if data_agent: try: orion_node.agent_id = data_agent['AgentID'] orion_node.agent_uri = data_agent['Uri'] except IndexError: pass return orion_node def get_agent(self, query_param): """ Searches for an agent by the ip address or the node_id """ if is_ip(query_param): query_for_where = "IP" elif isinstance(query_param, int): query_for_where = "NodeId" else: query_for_where = "Name" swql_agent = """SELECT AgentID, Uri FROM Orion.AgentManagement.Agent WHERE {}=@query_on""".format(query_for_where) kargs = {'query_on': query_param} data_agent = self.query(swql_agent, **kargs) # Since there might not always be an agent we # will not raise if the results do not return anything if 'results' not in data_agent or len(data_agent['results']) == 0: msg = "No Orion Agent found: {}".format(data_agent) self.logger.info(msg) return None if len(data_agent['results']) >= 2: self.logger.debug("Muliple Agents match '{}' Caption: {}".format( query_param, data_agent)) raise ValueError( "Muliple Agents match '{}' Caption".format(query_param)) return data_agent['results'][0] def query(self, swql, **kargs): """ Run SWQL against the Orion Platform. """ return self.client.query(swql, **kargs) def invoke(self, entity, verb, *args): """ Run an Invoke against the Orion Platform. """ return self.client.invoke(entity, verb, *args) def create(self, entity, **kargs): """ Run an Create against the Orion Platform. """ return self.client.create(entity, **kargs) def read(self, uri): """ Run an Read against the Orion Platform. """ return self.client.read(uri) def update(self, uri, **kargs): """ Run an Update against the Orion Platform. """ return self.client.update(uri, **kargs) def delete(self, uri): """ Run an Delete of an URI against the Orion Platform. """ return self.client.delete(uri) def get_snmp_community(self, community): """ Return the correct SNMP comminity to use. """ if community == "customer": return self.config['snmp_customer'] elif community == "internal": return self.config['snmp_internal'] elif community is None: return self.config['snmp_default'] else: return community def get_snmp_cred_id(self, community): """ Look up an SNMP community in the config and then look up the Orion ID for the Credential. """ # Check if the community should be replaced. name = self.get_snmp_community(community) swql = """SELECT ID FROM Orion.Credential WHERE CredentialType=@CredentialType and Name=@name""" kargs = { 'CredentialType': 'SolarWinds.Orion.Core.Models.Credentials.SnmpCredentialsV2', 'name': name } orion_data = self.query(swql, **kargs) if len(orion_data['results']) == 1: return orion_data['results'][0]['ID'] else: msg = "Could not get ID for community in Orion.Credential: {}".format( community) send_user_error(msg) raise ValueError(msg) def get_engine_id(self, poller): """ Takes a poller name (or primary) and returns the EngineID for the poller. Raises: ValueError on an invaild poller. Returns: The EngineID (int) """ if poller == "primary": return 1 else: swql = """SELECT EngineID, ServerName, IP, ServerType FROM Orion.Engines WHERE ServerName=@poller""" kargs = {'poller': poller} data = self.query(swql, **kargs) if len(data['results']) == 1: return data['results'][0]['EngineID'] else: send_user_error("Invalid poller name") raise ValueError("Invalid poller name") def get_ncm_transfer_results(self, transfer_id, sleep_delay=10): """ Gets the completed (waits until finished). NCM job transfer status from Orion. Retruns: The completed status. """ ts = {} while True: swql = """SELECT TransferID, NodeID, Action, RequestedConfigType, RequestedScript, RequestedReboot, ConfigID, TransferProtocol, Status, ErrorMessage, DeviceOutput, DateTime, UserName FROM NCM.TransferResults WHERE TransferID=@transfer_id""" kargs = {'transfer_id': transfer_id} transfer_data = self.query(swql, **kargs) status = transfer_data['results'][0]['Status'] if status == 1: time.sleep(sleep_delay) elif status == 2: ts['status'] = "Complete" break elif status == 3: ts['status'] = "Error" break else: ts['status'] = "Unknown" break ts['RequestedScript'] = transfer_data['results'][0]['RequestedScript'] ts['RequestedReboot'] = transfer_data['results'][0]['RequestedReboot'] ts['ErrorMessage'] = transfer_data['results'][0]['ErrorMessage'] ts['DeviceOutput'] = transfer_data['results'][0]['DeviceOutput'] ts['UserName'] = transfer_data['results'][0]['UserName'] return ts
class OrionBaseAction(Action): def __init__(self, config): super(OrionBaseAction, self).__init__(config) self.client = None if "orion" not in self.config: raise ValueError("Orion host details not in the config.yaml") def connect(self, platform): """ Connect to an Orion platform from the packs config.yaml. """ if platform is None: try: platform = self.config['defaults']['platform'] except IndexError: send_user_error("No default Orion platform.") raise ValueError("No default Orion platform.") self.logger.debug("Connecting to Orion platform: {}".format(platform)) try: self.client = SwisClient( self.config['orion'][platform]['host'], self.config['orion'][platform]['user'], self.config['orion'][platform]['password']) except KeyError: raise ValueError("Orion host details not in the config.yaml") return platform def get_node(self, node): """ Get an OrionNode object """ orion_node = OrionNode() if is_ip(node): query_for_where = "IPAddress" else: query_for_where = "Caption" swql = """SELECT NodeID, Uri, IPAddress, Caption FROM Orion.Nodes WHERE {}=@query_on""".format(query_for_where) kargs = {'query_on': node} data = self.query(swql, **kargs) if 'results' not in data: msg = "No results from Orion: {}".format(data) self.logger.info(msg) raise Exception(msg) if len(data['results']) == 1: try: orion_node.npm_id = data['results'][0]['NodeID'] orion_node.uri = data['results'][0]['Uri'] orion_node.ip_address = data['results'][0]['IPAddress'] orion_node.caption = data['results'][0]['Caption'] except IndexError: pass elif len(data['results']) >= 2: self.logger.debug( "Muliple Nodes match '{}' Caption: {}".format( node, data)) raise ValueError("Muliple Nodes match '{}' Caption".format( node)) if orion_node.npm: swql = """SELECT NodeID FROM Cirrus.Nodes WHERE CoreNodeID=@CoreNodeID""" kargs = {'CoreNodeID': orion_node.npm_id} data = self.query(swql, **kargs) # Don't raise an exception if this fails. # The platform may not haev NCM installed. if 'results' not in data: msg = "No results from Orion NCM: {}".format(data) self.logger.info(msg) elif len(data['results']) == 1: try: orion_node.ncm_id = data['results'][0]['NodeID'] except IndexError: pass return orion_node def query(self, swql, **kargs): """ Run SWQL against the Orion Platform. """ return self.client.query(swql, **kargs) def invoke(self, entity, verb, *args): """ Run an Invoke against the Orion Platform. """ return self.client.invoke(entity, verb, *args) def create(self, entity, **kargs): """ Run an Create against the Orion Platform. """ return self.client.create(entity, **kargs) def read(self, uri): """ Run an Read against the Orion Platform. """ return self.client.read(uri) def update(self, uri, **kargs): """ Run an Update against the Orion Platform. """ return self.client.update(uri, **kargs) def delete(self, uri): """ Run an Delete of an URI against the Orion Platform. """ return self.client.delete(uri) def get_snmp_community(self, community, std_community): """ Return the correct SNMP comminity to use. """ if community is not None: return community elif std_community is not None: try: return self.config['defaults']['snmp'][std_community] except KeyError: raise ValueError("Invalid standard community") elif std_community is None: raise ValueError("Need one of community or std_community") def get_snmp_cred_id(self, community): """ Look up an SNMP community in the config and then look up the Orion ID for the Credential. """ # Check if community is a know standard, otherwise # use it as the community. try: name = self.get_snmp_community(None, community) except ValueError: name = community swql = """SELECT ID FROM Orion.Credential WHERE CredentialType=@CredentialType and Name=@name""" kargs = {'CredentialType': 'SolarWinds.Orion.Core.Models.Credentials.SnmpCredentialsV2', 'name': name} orion_data = self.query(swql, **kargs) if len(orion_data['results']) == 1: return orion_data['results'][0]['ID'] else: msg = "Could not get ID for community in Orion.Credential: {}".format( community) send_user_error(msg) raise ValueError(msg) def get_engine_id(self, poller): """ Takes a poller name (or primary) and returns the EngineID for the poller. Raises: ValueError on an invaild poller. Returns: The EngineID (int) """ if poller == "primary": return 1 else: swql = """SELECT EngineID, ServerName, IP, ServerType FROM Orion.Engines WHERE ServerName=@poller""" kargs = {'poller': poller} data = self.query(swql, **kargs) if len(data['results']) == 1: return data['results'][0]['EngineID'] else: send_user_error("Invalid poller name") raise ValueError("Invalid poller name") def get_ncm_transfer_results(self, transfer_id, sleep_delay=10): """ Gets the completed (waits until finished). NCM job transfer status from Orion. Retruns: The completed status. """ ts = {} while True: swql = """SELECT TransferID, NodeID, Action, RequestedConfigType, RequestedScript, RequestedReboot, ConfigID, TransferProtocol, Status, ErrorMessage, DeviceOutput, DateTime, UserName FROM NCM.TransferResults WHERE TransferID=@transfer_id""" kargs = {'transfer_id': transfer_id} transfer_data = self.query(swql, **kargs) status = transfer_data['results'][0]['Status'] if status == 1: time.sleep(sleep_delay) elif status == 2: ts['status'] = "Complete" break elif status == 3: ts['status'] = "Error" break else: ts['status'] = "Unknown" break ts['RequestedScript'] = transfer_data['results'][0]['RequestedScript'] ts['RequestedReboot'] = transfer_data['results'][0]['RequestedReboot'] ts['ErrorMessage'] = transfer_data['results'][0]['ErrorMessage'] ts['DeviceOutput'] = transfer_data['results'][0]['DeviceOutput'] ts['UserName'] = transfer_data['results'][0]['UserName'] return ts
class Orion(): def __init__(self): # start the connection self.server = 'server fqdn or ip' self.username = '******' self.password = '******' self.con = SwisClient(self.server,self.username,self.password) requests.packages.urllib3.disable_warnings() def get_all_juniper_node_ids(self, **kwargs): self.q = self.con.query('SELECT NodeID,Caption FROM Orion.Nodes WHERE Vendor LIKE @vendor', vendor='Juniper%') return self.q def get_all_cisco_node_ids(self, **kwargs): self.q = self.con.query('SELECT NodeID,Caption FROM Orion.Nodes WHERE Vendor LIKE @vendor', vendor='Cisco') return self.q def npm_add_node(self, **kwargs): s = Settings() settings = s.get_all_settings() # first check this device isn't already in Orion self.ip_check = self.con.query('SELECT NodeID FROM Orion.Nodes WHERE IPAddress=@ip_address', ip_address=kwargs.get('ip_address')) self.hostname_check = self.con.query('SELECT NodeID FROM Orion.Nodes WHERE Caption=@hostname', hostname=kwargs.get('hostname')) if len(self.ip_check['results']) > 0 or len(self.hostname_check['results']) > 0: # if this is greater than 0, then the device already exists in orion return else: # assign the device poperties for adding to the database # support only for SNMPv2 at this stage self.properties = { 'Caption': kwargs.get('hostname'), 'IPAddress': kwargs.get('ip_address'), 'DynamicIP': False, 'EngineID': kwargs.get('engine_id') or 1, 'Status': 1, 'Allow64BitCounters': 1, 'ObjectSubType': 'SNMP', 'SNMPVersion': kwargs.get('snmp_version') or 2, 'Community': kwargs.get('community'), # Set NextRediscovery to now + 2 mins so that rediscovery happens at the next rediscovery interval, default 30mins 'NextRediscovery': datetime.datetime.now() + datetime.timedelta(minutes = 2) } # create the node self.results = self.con.create('Orion.Nodes', **self.properties) # get the NodeID self.node_id = re.search('(\d+)$', self.results).group(0) # setup device pollers self.pollers_enabled = { 'N.Status.ICMP.Native': True, 'N.Status.SNMP.Native': False, 'N.ResponseTime.ICMP.Native': True, 'N.ResponseTime.SNMP.Native': False, 'N.Details.SNMP.Generic': True, 'N.Uptime.SNMP.Generic': True, 'N.Cpu.SNMP.HrProcessorLoad': True, 'N.Memory.SNMP.NetSnmpReal': True, 'N.AssetInventory.Snmp.Generic': True, 'N.Topology_Layer3.SNMP.ipNetToMedia': False, 'N.Routing.SNMP.Ipv4CidrRoutingTable': False } # create a list of dictionarys for each poller self.pollers = [] for self.k in self.pollers_enabled: self.pollers.append( { 'PollerType': self.k, 'NetObject': 'N:' + self.node_id, 'NetObjectType': 'N', 'NetObjectID': self.node_id, 'Enabled': self.pollers_enabled[self.k] } ) # loop through pollers and turn them on for self.poller in self.pollers: self.response = self.con.create('Orion.Pollers', **self.poller) # add the custom properties self.results = self.con.query( "SELECT Uri FROM Orion.Nodes WHERE NodeID=@id", id=self.node_id ) # grab the uri - whatever this is self.uri = self.results['results'][0]['Uri'] # update the custom properties self.con.update( self.uri + '/CustomProperties', City = kwargs.get('city'), Site = kwargs.get('site'), Network = kwargs.get('network') ) return self.node_id def npm_add_interfaces(self, node_id): # run interface discovery self.interfaces = self.con.invoke('Orion.NPM.Interfaces', 'DiscoverInterfacesOnNode', node_id) # imports only interfaces with a description # alternatively any physical Cisco interfaces with and up status could be added self.descr_only = [ self.x for self.x in self.interfaces['DiscoveredInterfaces'] if re.search('\xb7', self.x['Caption']) ] # add the interfaces self.execute = self.con.invoke( 'Orion.NPM.Interfaces', 'AddInterfacesOnNode', node_id, self.descr_only, 'AddDefaultPollers' ) def npm_poll_now(self, node_id, **kwargs): self.con.invoke('Orion.Nodes', 'PollNow', 'N:%s' % self.node_id) def ncm_add_node(self, node_id, **kwargs): s = Settings() settings = s.get_all_settings() # check that device isn't already managed in Orion NCM self.ip_check = self.con.query('SELECT NodeID FROM Cirrus.Nodes WHERE AgentIP=@ip_address', ip_address=kwargs.get('ip_address')) self.hostname_check = self.con.query('SELECT NodeID FROM Cirrus.Nodes WHERE NodeCaption=@hostname', hostname=kwargs.get('hostname')) if len(self.ip_check['results']) > 0 or len(self.hostname_check['results']) > 0: # if this is greater than 0, then the device already exists in orion return else: self.con.invoke('Cirrus.Nodes', 'AddNodeToNCM', node_id) # now update the selected columns to ensure we can log into this device # first using rhe NodeID from Orion.NPM get the Guid self.ncm_node_id = self.con.query('SELECT NodeID FROM Cirrus.Nodes WHERE CoreNodeID=@node', node=node_id)['results'][0]['NodeID'] # fetch the NCM Node Object self.ncm_node_data = self.con.invoke('Cirrus.Nodes', 'GetNode', self.ncm_node_id) # verfify that the submitted connection_profile exists # if it doesn't set the profile to '-1' self.profiles = self.con.invoke('Cirrus.Nodes', 'GetAllConnectionProfiles') for self.pro in self.profiles: if self.pro['ID'] == kwargs.get('connection_profile'): self.connection_profile_id = kwargs.get('connection_profile') else: self.connection_profile_id = -1 # modify the device properties but only if the submitted profile is valid if self.connection_profile_id != -1: self.ncm_node_data['Username'] = '' self.ncm_node_data['Password'] = '' self.ncm_node_data['ConnectionProfile'] = self.connection_profile_id # Commit our changes self.con.invoke('Cirrus.Nodes', 'UpdateNode', self.ncm_node_data) def ncm_get_guid(self, hostname): # used to determine the guid for a hostname as it exists in Orion NCM self.node_id = self.con.query("SELECT NodeID FROM Cirrus.Nodes WHERE NodeCaption=@node", node=hostname) if self.node_id is not None: return self.node_id else: return False def ncm_download_configs(self, hostname): # cannot be called until a device has been discovered, this action in Orion depends on a known sysObjectID OID # get the System.Guid[] into a list, if submitted as a simple string the API errors out # HTTPError: 400 Client Error: Verb Cirrus.ConfigArchive.DownloadConfig cannot unpackage parameter 0 with type System.Guid[] \ # for url: https://lab-win2012.it-ninja.xyz:17778/SolarWinds/InformationService/v3/Json/Invoke/Cirrus.ConfigArchive/DownloadConfig self.node_ids = [] self.node_data = self.con.query("SELECT NodeID FROM Cirrus.Nodes WHERE NodeCaption=@node", node=hostname) self.node_ids.append(self.node_data['results'][0]['NodeID']) configs = ['Active', 'Set', 'Rescue'] for c in configs: self.con.invoke("Cirrus.ConfigArchive", "DownloadConfig", self.node_ids, c) def get_connection_profiles(self): self.profiles = self.con.invoke('Cirrus.Nodes', 'GetAllConnectionProfiles') return self.profiles def ncm_remove_node(self, hostname): # used to unmange a node from Orion NCM # called pre-delete node from Orion NPM self.node_id = self.ncm_get_guid(hostname) if self.node_id is not False: self.con.invoke("Cirrus.Nodes", "RemoveNode", self.node_id) def npm_get_uri(self, hostname): # used to get the node_id of a pre-existing node in Orion NPM self.node_uri = self.con.query("SELECT Uri FROM Orion.Nodes WHERE Caption=@hostname", hostname=hostname) return self.node_uri def npm_delete_node(self, hostname): # get the devices uri. This is a list of dictionaries self.node_uri = self.npm_get_uri(hostname) # call swis to delete the node if self.node_uri is not False: self.con.delete(self.node_uri['results'][0]['Uri'])
def run(job=None, *args, **kwargs): # Disable SSL warnings verify = False if not verify: from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # Get SolarWinds ConnectionInfo solarwinds = ConnectionInfo.objects.get(name='SolarWinds') swis = SwisClient(solarwinds.ip, solarwinds.username, solarwinds.password) if not solarwinds: return "FAILURE", "", "Missing required SolarWinds connection info. (Admin -> Connection Info -> New Connection Info)" # Get Server server = job.server_set.first() # Get subnet server.ip sep = '.' octets = server.ip.split(sep='.') network = sep.join(octets[:3] + list('0')) # Get next available IP from SW based on subnet next_ip = swis.query( "SELECT TOP 1 I.Status, I.DisplayName FROM IPAM.IPNode I WHERE Status=2 AND I.Subnet.Address = '{}'" .format(network)) # Set the server ip server.ip = next_ip['results'][0]['DisplayName'] server.save() # Setup Node Properties props = { 'IPAddress': server.ip, 'EngineID': 1, 'ObjectSubType': 'Agent', 'Caption': server.hostname, 'DNS': server.hostname, 'Description': 'Added by CloudBolt', 'Contact': server.owner, } #Create the Node job.set_progress("Adding '{}' to SolarWinds".format(server.hostname)) results = swis.create('Orion.Nodes', **props) # Extract NodeID from results nodeid = re.search('(\d+)$', results).group(0) # Setup Poller Status for the node pollers_enabled = { 'N.Status.ICMP.Native': True, 'N.Status.SNMP.Native': False, 'N.ResponseTime.ICMP.Native': True, 'N.ResponseTime.SNMP.Native': False, 'N.Details.SNMP.Generic': True, 'N.Uptime.SNMP.Generic': True, 'N.Cpu.SNMP.HrProcessorLoad': True, 'N.Memory.SNMP.NetSnmpReal': True, 'N.AssetInventory.Snmp.Generic': True, 'N.Topology_Layer3.SNMP.ipNetToMedia': False, 'N.Routing.SNMP.Ipv4CidrRoutingTable': False } # Add Node to Pollers pollers = [] for k in pollers_enabled: pollers.append({ 'PollerType': k, 'NetObject': 'N:' + nodeid, 'NetObjectType': 'N', 'NetObjectID': nodeid, 'Enabled': pollers_enabled[k] }) for poller in pollers: job.set_progress("Adding poller type: '{}' with status {}...".format( poller['PollerType'], poller['Enabled'])) response = swis.create('Orion.Pollers', **poller) #Poll the node swis.invoke('Orion.Nodes', 'PollNow', 'N:' + nodeid) return "", "", ""
class OrionBaseAction(Action): def __init__(self, config): super(OrionBaseAction, self).__init__(config) self.client = None if "orion" not in self.config: raise ValueError("Orion host details not in the config.yaml") def connect(self, platform): """ Connect to an Orion platform from the packs config.yaml. """ try: self.client = SwisClient( self.config['orion'][platform]['host'], self.config['orion'][platform]['user'], self.config['orion'][platform]['password']) except KeyError: raise ValueError("Orion host details not in the config.yaml") def query(self, swql, **kargs): """ Run SWQL against the Orion Platform. """ return self.client.query(swql, **kargs) def invoke(self, entity, verb, *args): """ Run an Invoke against the Orion Platform. """ return self.client.invoke(entity, verb, *args) def create(self, entity, **kargs): """ Run an Create against the Orion Platform. """ return self.client.create(entity, **kargs) def node_exists(self, caption, ip_address): """ Check if an Node exists (caption and or ip) on the Orion platform. Returns: True or False. """ swql = """SELECT NodeID, IPAddress FROM Orion.Nodes WHERE Caption=@caption""" kargs = {'caption': caption} caption_data = self.query(swql, **kargs) if len(caption_data['results']) >= 1: self.logger.debug( "One (or more) Nodes match '{}' Caption.".format(caption)) return True swql = """SELECT NodeID, IPAddress FROM Orion.Nodes WHERE IPAddress=@ip_address""" kargs = {'ip_address': ip_address} ip_data = self.query(swql, **kargs) if len(ip_data['results']) >= 1: self.logger.debug( "One (or more) Nodes match '{}' IP.".format(ip_address)) return True else: return False def get_node_id(self, caption): """ Gets an NodeID from the Orion platform. Raises: ValueError on muliple or no matching caption. Returns: the NodeID (int) """ swql = "SELECT NodeID FROM Orion.Nodes WHERE Caption=@caption" kargs = {'caption': caption} data = self.query(swql, **kargs) if len(data['results']) == 1: try: return data['results'][0]['NodeID'] except IndexError: raise ValueError("Invalid Node") elif len(data['results']) >= 2: self.logger.debug("Muliple Nodes match '{}' Caption: {}".format( caption, data)) raise ValueError( "Muliple Nodes match '{}' Caption".format(caption)) elif len(data['results']) == 0: self.logger.debug("No Nodes match '{}' Caption: {}".format( caption, data)) raise ValueError("No matching Caption for '{}'".format(caption)) def get_engine_id(self, poller): """ Takes a poller name (or primary) and returns the EngineID for the poller. Raises: ValueError on an invaild poller. Returns: The EngineID (int) """ if poller == "primary": return 1 else: swql = """SELECT EngineID, ServerName, IP, ServerType FROM Orion.Engines WHERE ServerName=@poller""" kargs = {'poller': poller} data = self.query(swql, **kargs) if len(data['results']) == 1: return data['results'][0]['EngineID'] else: self.send_user_error("Invalid poller name") raise ValueError("Invalid poller name") def get_ncm_node_id(self, caption): """ Queries the Network configuration Manager nodes table on the Orion platform for the NodeID of a given node name (aka NodeCaption). Raises: IndexError on Invalid number of nodes (e.g. 0 or 2+). Returns: A single node id. """ swql = "SELECT NodeID FROM Cirrus.Nodes WHERE NodeCaption=@node" kargs = {'node': caption} data = self.query(swql, **kargs) if len(data['results']) == 1: try: return data['results'][0]['NodeID'] except IndexError: raise IndexError("Invalid Node") elif len(data['results']) >= 2: raise IndexError( "Muliple Nodes match '{}' NodeCaption".format(caption)) elif len(data['results']) == 0: raise IndexError( "No matching NodeCaption for '{}'".format(caption)) def get_ncm_transfer_results(self, transfer_id, sleep_delay=10): """ Gets the completed (waits until finished). NCM job transfer status from Orion. Retruns: The completed status. """ ts = {} while True: swql = """SELECT TransferID, Action, Status, ErrorMessage, DeviceOutput FROM NCM.TransferResults WHERE TransferID=@transfer_id""" kargs = {'transfer_id': transfer_id} transfer_data = self.query(swql, **kargs) status = transfer_data['results'][0]['Status'] if status == 1: time.sleep(sleep_delay) elif status == 2: ts['status'] = "Complete" break elif status == 3: ts['status'] = "Error" ts['ErrorMessage'] = transfer_data['results'][0][ 'ErrorMessage'] break else: ts['status'] = "Unknown" ts['ErrorMessage'] = "Invalid stauts: {}".format(status) break return ts def status_code_to_text(self, status): """ Takes an Solarwinds Orion status code and translates it to human text and also a colour that can be used in Slack. """ if status == 0: return ("Unknown", "grey") elif status == 1: return ("Up", "good") elif status == 2: return ("Down", "danger") elif status == 3: return ("Warning", "warning") elif status == 14: return ("Critical", "danger") def send_user_error(self, message): """ Prints an user error message. """ print(message)
def main(ip, ctrel, pci, province, sbu, sitename): npm_server = '' username = '' password = '' target_node_ip = ip snmpv3_credential_id = 6 orion_engine_id = 1 swis = SwisClient(npm_server, username, password) print("Add an SNMP v3 node:") corePluginContext = { 'BulkList': [{ 'Address': target_node_ip }], 'Credentials': [{ 'CredentialID': snmpv3_credential_id, 'Order': 1 }], 'WmiRetriesCount': 0, 'WmiRetryIntervalMiliseconds': 1000 } corePluginConfig = swis.invoke('Orion.Discovery', 'CreateCorePluginConfiguration', corePluginContext) discoveryProfile = { 'Name': 'API discovery', 'EngineID': orion_engine_id, 'JobTimeoutSeconds': 3600, 'SearchTimeoutMiliseconds': 5000, 'SnmpTimeoutMiliseconds': 5000, 'SnmpRetries': 2, 'RepeatIntervalMiliseconds': 1800, 'SnmpPort': 161, 'HopCount': 0, 'PreferredSnmpVersion': 'SNMP3', 'DisableIcmp': False, 'AllowDuplicateNodes': False, 'IsAutoImport': True, 'IsHidden': True, 'PluginConfigurations': [{ 'PluginConfigurationItem': corePluginConfig }] } print("Running discovery...") discoveryProfileID = swis.invoke('Orion.Discovery', 'StartDiscovery', discoveryProfile) print("Returned discovery profile id {}".format(discoveryProfileID)) running = True while running: try: query = "SELECT Status FROM Orion.DiscoveryProfiles WHERE ProfileID = " + str( discoveryProfileID) status = swis.query(query)['results'][0]['Status'] print("##") except: running = False print('Done with discovery') batchresults = swis.query( "SELECT Result, ResultDescription, ErrorMessage, BatchID FROM Orion.DiscoveryLogs WHERE ProfileID=@id", id=discoveryProfileID) batchid = batchresults['results'][0]['BatchID'] nodeidresults = swis.query( "SELECT DisplayName,NetObjectID FROM Orion.DiscoveryLogItems WHERE BatchID=@id", id=batchid) netobjectid = nodeidresults['results'][0]['NetObjectID'] nodeid = netobjectid.split(':')[1] print(nodeid) #enablehardwarehealth hardwarehealth = swis.invoke('Orion.HardwareHealth.HardwareInfoBase', 'EnableHardwareHealth', netobjectid, 9) #add pollers pollers_enabled = { 'N.Topology_Vlans.SNMP.VtpVlan': True, 'N.Topology_Layer2.SNMP.Dot1dTpFdbNoVLANs': True, 'N.Topology_CDP.SNMP.cdpCacheTable': True, 'N.Topology_STP.SNMP.Dot1dStp': True, 'N.Topology_PortsMap.SNMP.Dot1dBaseNoVLANs': True, 'N.Topology_Layer3.SNMP.ipNetToMedia': True, 'N.VRFRouting.SNMP.MPLSVPNStandard': True, } pollers = [] for k in pollers_enabled: pollers.append({ 'PollerType': k, 'NetObject': 'N:' + nodeid, 'NetObjectType': 'N', 'NetObjectID': nodeid, 'Enabled': pollers_enabled[k] }) for poller in pollers: print(" Adding poller type: {} with status {}... ".format( poller['PollerType'], poller['Enabled']), end="") response = swis.create('Orion.Pollers', **poller) print("DONE!") #update custom properties customresults = swis.query("SELECT Uri FROM Orion.Nodes WHERE NodeID=@id", id=nodeid) uri = customresults['results'][0]['Uri'] #swis.update(uri + '/CustomProperties', City='Brampton') #swis.update(uri + '/CustomProperties', Comments='Test Device') swis.update(uri + '/CustomProperties', CTREL_Code=ctrel) swis.update(uri + '/CustomProperties', PCI=pci) #swis.update(uri + '/CustomProperties', Postal_Code='L7A4B4') swis.update(uri + '/CustomProperties', Province=province) swis.update(uri + '/CustomProperties', SBU=sbu) #swis.update(uri + '/CustomProperties', Site_Contact='Anesh') #swis.update(uri + '/CustomProperties', Site_Contact_Number='666-666-666') #swis.update(uri + '/CustomProperties', Site_ID='666') swis.update(uri + '/CustomProperties', Site_Name=sitename) #swis.update(uri + '/CustomProperties', Site_Type='PROD') #swis.update(uri + '/CustomProperties', Street_Address='AJ Distrubution') swis.update(uri + '/CustomProperties', Support_Team='Corp Network') obj = swis.read(uri + '/CustomProperties') print(obj)
class OrionBaseAction(Action): def __init__(self, config): super(OrionBaseAction, self).__init__(config) self.client = None if "orion" not in self.config: raise ValueError("Orion host details not in the config.yaml") def connect(self, platform): """ Connect to an Orion platform from the packs config.yaml. """ try: self.client = SwisClient( self.config['orion'][platform]['host'], self.config['orion'][platform]['user'], self.config['orion'][platform]['password']) except KeyError: raise ValueError("Orion host details not in the config.yaml") def query(self, swql, **kargs): """ Run SWQL against the Orion Platform. """ return self.client.query(swql, **kargs) def invoke(self, entity, verb, *args): """ Run an Invoke against the Orion Platform. """ return self.client.invoke(entity, verb, *args) def create(self, entity, **kargs): """ Run an Create against the Orion Platform. """ return self.client.create(entity, **kargs) def node_exists(self, caption, ip_address): """ Check if an Node exists (caption and or ip) on the Orion platform. Returns: True or False. """ swql = """SELECT NodeID, IPAddress FROM Orion.Nodes WHERE Caption=@caption""" kargs = {'caption': caption} caption_data = self.query(swql, **kargs) if len(caption_data['results']) >= 1: self.logger.debug( "One (or more) Nodes match '{}' Caption.".format(caption)) return True swql = """SELECT NodeID, IPAddress FROM Orion.Nodes WHERE IPAddress=@ip_address""" kargs = {'ip_address': ip_address} ip_data = self.query(swql, **kargs) if len(ip_data['results']) >= 1: self.logger.debug( "One (or more) Nodes match '{}' IP.".format(ip_address)) return True else: return False def get_node_id(self, caption): """ Gets an NodeID from the Orion platform. Raises: ValueError on muliple or no matching caption. Returns: the NodeID (int) """ swql = "SELECT NodeID FROM Orion.Nodes WHERE Caption=@caption" kargs = {'caption': caption} data = self.query(swql, **kargs) if len(data['results']) == 1: try: return data['results'][0]['NodeID'] except IndexError: raise ValueError("Invalid Node") elif len(data['results']) >= 2: self.logger.debug( "Muliple Nodes match '{}' Caption: {}".format( caption, data)) raise ValueError("Muliple Nodes match '{}' Caption".format( caption)) elif len(data['results']) == 0: self.logger.debug( "No Nodes match '{}' Caption: {}".format( caption, data)) raise ValueError("No matching Caption for '{}'".format( caption)) def get_engine_id(self, poller): """ Takes a poller name (or primary) and returns the EngineID for the poller. Raises: ValueError on an invaild poller. Returns: The EngineID (int) """ if poller == "primary": return 1 else: swql = """SELECT EngineID, ServerName, IP, ServerType FROM Orion.Engines WHERE ServerName=@poller""" kargs = {'poller': poller} data = self.query(swql, **kargs) if len(data['results']) == 1: return data['results'][0]['EngineID'] else: self.send_user_error("Invalid poller name") raise ValueError("Invalid poller name") def get_ncm_node_id(self, caption): """ Queries the Network configuration Manager nodes table on the Orion platform for the NodeID of a given node name (aka NodeCaption). Raises: IndexError on Invalid number of nodes (e.g. 0 or 2+). Returns: A single node id. """ swql = "SELECT NodeID FROM Cirrus.Nodes WHERE NodeCaption=@node" kargs = {'node': caption} data = self.query(swql, **kargs) if len(data['results']) == 1: try: return data['results'][0]['NodeID'] except IndexError: raise IndexError("Invalid Node") elif len(data['results']) >= 2: raise IndexError("Muliple Nodes match '{}' NodeCaption".format( caption)) elif len(data['results']) == 0: raise IndexError("No matching NodeCaption for '{}'".format( caption)) def get_ncm_transfer_results(self, transfer_id, sleep_delay=10): """ Gets the completed (waits until finished). NCM job transfer status from Orion. Retruns: The completed status. """ ts = {} while True: swql = """SELECT TransferID, Action, Status, ErrorMessage, DeviceOutput FROM NCM.TransferResults WHERE TransferID=@transfer_id""" kargs = {'transfer_id': transfer_id} transfer_data = self.query(swql, **kargs) status = transfer_data['results'][0]['Status'] if status == 1: time.sleep(sleep_delay) elif status == 2: ts['status'] = "Complete" break elif status == 3: ts['status'] = "Error" ts['ErrorMessage'] = transfer_data['results'][0][ 'ErrorMessage'] break else: ts['status'] = "Unknown" ts['ErrorMessage'] = "Invalid stauts: {}".format(status) break return ts def status_code_to_text(self, status): """ Takes an Solarwinds Orion status code and translates it to human text and also a colour that can be used in Slack. """ if status == 0: return ("Unknown", "grey") elif status == 1: return ("Up", "good") elif status == 2: return ("Down", "danger") elif status == 3: return ("Warning", "warning") elif status == 14: return ("Critical", "danger") def send_user_error(self, message): """ Prints an user error message. """ print(message)
def create_node(name_tid, ip_address, oob_ip_address): npm_server = 'serverip' username = '******' password = '******' swis = SwisClient(npm_server, username, password) print("Adding node with ping..") # # fill these in for the node you want to add! # name_tid = 'xxxxxxx' # ip_address = '1.1.1.1' # oob_ip_address = '2.2.2.2' # community = 'public' # set up property bag for the new node props = { 'Caption': name_tid, 'IPAddress': ip_address, 'EngineID': 1, 'ObjectSubType': 'ICMP', } print("Adding node {}... ".format(props['IPAddress']), end="") results = swis.create('Orion.Nodes', **props) print("DONE!") # extract the nodeID from the result nodeid = re.search('(\d+)$', results).group(0) # add extra custom properites :D custom_props = { 'MY_PROPERTY_1': oob_ip_address, 'MY_PROPERTY_2': 'client name', } for k, v in custom_props.items(): swis.update(results + '/CustomProperties', **{k: v}) pollers_enabled = { 'N.Status.ICMP.Native': True, 'N.Status.SNMP.Native': False, 'N.ResponseTime.ICMP.Native': True, # 'N.ResponseTime.SNMP.Native': False, # 'N.Details.SNMP.Generic': False, # 'N.Uptime.SNMP.Generic': False, # 'N.Cpu.SNMP.HrProcessorLoad': False, # 'N.Memory.SNMP.NetSnmpReal': False, # 'N.AssetInventory.Snmp.Generic': False, # 'N.Topology_Layer3.SNMP.ipNetToMedia': False, # 'N.Routing.SNMP.Ipv4CidrRoutingTable': False } pollers = [] for k in pollers_enabled: pollers.append({ 'PollerType': k, 'NetObject': 'N:' + nodeid, 'NetObjectType': 'N', 'NetObjectID': nodeid, 'Enabled': pollers_enabled[k] }) for poller in pollers: print(" Adding poller type: {} with status {}... ".format( poller['PollerType'], poller['Enabled']), end="") response = swis.create('Orion.Pollers', **poller) print("DONE!")
class SolarWinds: def __init__(self, npm_server, username, password, logger=None): self.logger = logger or logging.getLogger('__name__') # Create the SWIS client for use throughout the instance. self.swis = SwisClient(npm_server, username, password) def does_node_exist(self, node_name): """ Checks to see if a SolarWinds node exists with the given name. Calls the get_node_id method of the class and uses the returned value to determine whether or not the node exists. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: True: The node exists. False: The node does not exist. """ if self.get_node_id(node_name): return True else: return False def get_node_id(self, node_name): """ Returns the NodeID for the given NodeName. Uses a SWIS query to the SolarWinds database to retrieve this information. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: node_id (string): The node ID that corresponds to the specified node name. """ node_id = self.swis.query( "SELECT NodeID, Caption FROM Orion.Nodes WHERE Caption = @caption", caption=node_name) self.logger.info("get_node_id - node_id query results: %s.", node_id) if node_id['results']: return node_id['results'][0]['NodeID'] else: return "" def get_node_uri(self, node_name): """ Returns the NodeURI for the given NodeName. Uses a SWIS query to the SolarWinds database to retrieve this information. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: node_id (string): The node URI that corresponds to the specified node name. """ node_uri = self.swis.query( "SELECT Caption, Uri FROM Orion.Nodes WHERE Caption = @caption", caption=node_name) self.logger.info("get_node_uri - node uri query results: %s.", node_uri) if node_uri['results']: return node_uri['results'][0]['Uri'] else: return "" def add_node_using_snmp_v3(self, node_name, ip_address, snmpv3_username, snmpv3_priv_method, snmpv3_priv_pwd, snmpv3_auth_method, snmpv3_auth_pwd): """ Creates a new node using the supplied name an IP address. Configure with our standard SNMPv3 credentials. Once created, attached all of the standard Cisco pollers. Args: node_name(string): A node name to be used for the newly created node object. ip_address(string): The IP address that is associated with the supplied node name. snmpv3_username(string): The SNMPv3 username that will be associated with the node object. snmpv3_priv_method(string): The SNMPv3 privilege method that will be used. snmpv3_priv_pwd (string): The SNMPv3 privilege password that will be used. snmpv3_auth_method(string): The SNMPv3 authentication method that will be used. snmpv3_auth_pwd (string): The SNMPv3 authentication password that will be used. Returns: None. """ if not self.does_node_exist(node_name): # set up property bag for the new node node_properties = { 'IPAddress': ip_address, 'EngineID': 1, 'ObjectSubType': 'SNMP', 'SNMPVersion': 3, 'SNMPV3Username': snmpv3_username, 'SNMPV3PrivMethod': snmpv3_priv_method, 'SNMPV3PrivKeyIsPwd': True, 'SNMPV3PrivKey': snmpv3_priv_pwd, 'SNMPV3AuthMethod': snmpv3_auth_method, 'SNMPV3AuthKeyIsPwd': True, 'SNMPV3AuthKey': snmpv3_auth_pwd, 'DNS': '', 'SysName': '', 'Caption': node_name } # Create base node object. results = self.swis.create('Orion.Nodes', **node_properties) self.logger.info("add_node - add node invoke results: %s", results) # Assign pollers to node. self.attach_poller_to_node(node_name, 'N.Status.ICMP.Native') self.attach_poller_to_node(node_name, 'N.Status.SNMP.Native', False) self.attach_poller_to_node(node_name, 'N.ResponseTime.ICMP.Native') self.attach_poller_to_node(node_name, 'N.ResponseTime.SNMP.Native', False) self.attach_poller_to_node(node_name, 'N.Details.SNMP.Generic') self.attach_poller_to_node(node_name, 'N.Uptime.SNMP.Generic') def is_poller_attached_to_node(self, node_name, poller_name): """ Checks to see if the specified poller is attached to the specified node. Makes a SWIS query to see if there's a corresponding entry in the Orion.Pollers table. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. poller_name(string): The name of the poller as represented in the SolarWinds database. Returns: True: The poller is currently attached to the node. False: The poller is not currently attached to the node. """ net_object_id = str(self.get_node_id(node_name)) net_object = 'N:' + net_object_id results = self.swis.query( "SELECT PollerType FROM Orion.Pollers WHERE NetObject = @net_object AND PollerType " "= @poller_name", net_object=net_object, poller_name=poller_name) self.logger.info( "is_poller_attached_to_node - check for poller query results: %s", results) if results['results']: return True else: return False def attach_poller_to_node(self, node_name, poller_name, enabled=True): """ Checks to see if the specified poller is attached to the specified node. If it is not, a SWIS create is executed against Orion.Pollers to attach the poller to the node. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. poller_name(string): The name of the poller as represented in the SolarWinds database. enabled(boolean): Whether or not to enable the attached poller. Returns: None. """ if not self.is_poller_attached_to_node(node_name, poller_name): net_object_id = str(self.get_node_id(node_name)) net_object = 'N:' + net_object_id poller_properties = { 'PollerType': poller_name, 'NetObject': net_object, 'NetObjectType': 'N', 'NetObjectID': net_object_id, 'Enabled': enabled } results = self.swis.create('Orion.Pollers', **poller_properties) self.logger.info( "attach_poller_to_node - poller create results: %s", results) def enable_hardware_health_on_node(self, node_name): """ Enables the hardware health monitoring on the specified node. Executes a SWIS invoke of the 'EnableHardwareHealth' verb, passing it the node's net object ID. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: None. """ net_object_id = str(self.get_node_id(node_name)) net_object = 'N:' + net_object_id results = self.swis.invoke('Orion.HardwareHealth.HardwareInfo', 'EnableHardwareHealth', net_object, 9) self.logger.info( "enable_hardware_health - enable hardware health invoke results: %s", results) def add_node_to_ncm(self, node_name): """ Adds the specified node to the SolarWinds NCM module. Executes a SWIS invoke of the 'AddNodetoNCM' verb, passing it the node's object ID. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: None. """ results = self.swis.invoke('Cirrus.Nodes', 'AddNodeToNCM', self.get_node_id(node_name)) self.logger.info( "add_node_to_ncm - add node to ncm invoke results: %s", results) def add_node_to_udt(self, node_name): udt_properties = { 'NodeID': self.get_node_id(node_name), 'Capability': '2', 'Enabled': True } results = self.swis.create('Orion.UDT.NodeCapability', **udt_properties) self.logger.info( "add_node_to_udt - add node at l2 to udt create results: %s", results) udt_properties = { 'NodeID': self.get_node_id(node_name), 'Capability': '3', 'Enabled': True } results = self.swis.create('Orion.UDT.NodeCapability', **udt_properties) self.logger.info( "add_node_to_udt - add node at l3 to udt create results: %s", results) def add_node_to_ip_vnqm(self, node_name): vnqm_node_properties = { 'NodeID': self.get_node_id(node_name), 'Name': node_name, 'IsHub': False, 'IsAutoConfigured': True } results = self.swis.create('Orion.IpSla.Sites', **vnqm_node_properties) self.logger.info( "add_node_to_vnqm - add node to vnqm create results: %s", results) def add_icmp_echo_ip_sla_operation_to_node(self, node_name, ip_sla_operation_number, ip_sla_name): ip_sla_properties = { 'NodeID': self.get_node_id(node_name), 'OperationTypeID': 5, 'OperationType': "ICMP Echo", 'IsAutoConfigured': False, 'Frequency': 10, 'IpSlaOperationNumber': ip_sla_operation_number, 'OperationName': ip_sla_name } results = self.swis.create('Orion.IpSla.Operations', **ip_sla_properties) self.logger.info( "add_icmp_echo_ip_sla_operation_to_node - add IP SLA operation to node create results: %s", results) def set_custom_properties(self, node_name, custom_property_name, custom_property_value): """ For a given node, sets the specified custom property to the specified value. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. custom_property_name(string): The custom property who's value we want to change. The custom property needs to have been previously created or nothing will be changed. custom_property_value(string): The desired value that the custom property will be set to. Returns: None. """ node_uri = self.get_node_uri(node_name) custom_property = {custom_property_name: custom_property_value} self.swis.update(node_uri + '/CustomProperties', **custom_property) def get_custom_properties(self, node_name): """ For a given node, gets a list of the custom properties and values associated with it. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: custom_properties(list): A list of dictionaries. Each dictionary is a single key/value pair that contains the custom property name and value. """ node_uri = self.get_node_uri(node_name) custom_properties = self.swis.read(node_uri + '/CustomProperties') self.logger.info( "set_custom_properties - custom_properties read results: %s", custom_properties) return custom_properties def get_list_of_custom_pollers_for_node(self, node_name): """ For a given node, gets a list of the currently assigned custom pollers. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: custom_pollers(dictionary): Returns a dictionary that represents all of the custom pollers attached to the node. Each key is the custom property name and the value is the associated custom property value. """ node_id = self.get_node_id(node_name) custom_pollers = self.swis.query( "SELECT CustomPollerName FROM Orion.NPM.CustomPollerAssignment WHERE NodeID " "= @node_id", node_id=node_id) self.logger.info( "get_list_of_custom_pollers_by_name - custom_pollers query results: %s", custom_pollers) return custom_pollers['results'] def remove_custom_poller_by_name(self, node_name, poller_name): """ For a given node, detaches the specified custom poller. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. poller_name(string): The name of the custom poller that will be removed from the node. Returns: None. """ node_id = self.get_node_id(node_name) custom_poller_id = self.swis.query( "SELECT CustomPollerID FROM Orion.NPM.CustomPollers WHERE UniqueName = " "@poller_name", poller_name=poller_name) self.logger.info( "remove_custom_poller_by_name - custom_poller_id query results: %s", custom_poller_id) custom_poller_uri = self.swis.query( "SELECT Uri FROM Orion.NPM.CustomPollerAssignmentOnNode WHERE " "NodeID=@node_id AND CustomPollerID=@custom_poller_id", node_id=node_id, custom_poller_id=custom_poller_id['results'][0]['CustomPollerID']) self.logger.info( "remove_custom_poller_by_name - custom_poller_uri query results: %s", custom_poller_uri) self.swis.delete(custom_poller_uri['results'][0]['Uri']) def add_custom_poller_by_name(self, node_name, poller_name): """ For a given node, attaches the specified custom poller. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. poller_name(string): The name of the custom poller which is to be attached to the specified node. Returns: None. """ node_id = self.get_node_id(node_name) custom_poller_id = self.swis.query( "SELECT CustomPollerID FROM Orion.NPM.CustomPollers WHERE UniqueName = @poller_name", poller_name=poller_name) self.logger.info( "add_custom_poller_by_name - custom_poller_id query results: %s", custom_poller_id) poller_properties = { 'NodeID': node_id, 'customPollerID': custom_poller_id['results'][0]['CustomPollerID'] } self.swis.create('Orion.NPM.CustomPollerAssignmentOnNode', **poller_properties) def does_group_exist(self, group_name): """ Checks to see if a SolarWinds group exists with the given name. Calls the get_group_id method of the class and uses the returned value to determine whether or not the group exists. Args: group_name(string): A group name which should equal the name used in SolarWinds for the container object. Returns: True: The group exists. False: The group does not exist. """ if self.get_group_id(group_name): return True else: return False def get_group_id(self, group_name): """ Returns the ContainerID for the given Group Name. Uses a SWIS query to the SolarWinds database to retrieve this information. Args: group_name(string): A group name which should equal the name used in SolarWinds for the container object. Returns: group_id (string): The group ID that corresponds to the specified group name. """ group_id = self.swis.query( "SELECT ContainerID FROM Orion.Container WHERE Name = @group_name", group_name=group_name) self.logger.info("get_group_id - group_id query results: %s", group_id) if group_id['results']: return group_id['results'][0]['ContainerID'] else: return "" def get_group_uri(self, group_name): """ Returns the ContainerUri for the given Group Name. Uses a SWIS query to the SolarWinds database to retrieve this information. Args: group_name(string): A group name which should equal the name used in SolarWinds for the container object. Returns: group_uri (string): The group URI that corresponds to the specified group name. """ group_uri = self.swis.query( "SELECT Uri FROM Orion.Container WHERE Name = @group_name", group_name=group_name) self.logger.info("get_group_uri - group_uri query results: %s", group_uri) if group_uri['results']: return group_uri['results'][0]['Uri'] else: return "" def add_group(self, group_name, owner='Core', refresh_frequency=60, status_rollup=0, group_description='', polling_enabled=True, group_members=None): """ Creates a new empty group using the supplied name. Sets all of the additional parameters to the default values. Args: group_name(string): A group name to be used for the newly created container. owner(string): Must be 'Core'. refresh_frequency(int): How often the group membership is updated. status_rollup(int): Status rollup mode. # 0 = Mixed status shows warning # 1 = Show worst status # 2 = Show best status group_description(string): polling_enabled(boolean): Whether polling of the group is enabled or disabled. group_members(list): A list of group members and/or dynamic filters. Returns: None. """ if group_members is None: group_members = [] if not self.does_group_exist(group_name): results = self.swis.invoke('Orion.Container', 'CreateContainer', group_name, owner, refresh_frequency, status_rollup, group_description, polling_enabled, group_members) self.logger.info("add_group - add group invoke results: %s", results) def is_node_in_group(self, node_name, group_name): """ Checks to see if a node is a member of a particular group. Runs a SWIS query against the ContainerMembers table to see if there's a corresponding table entry. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. group_name(string): A group name which should equal the name used in SolarWinds for the container object. Returns: True: The node is in the group. False: The node is not in the group. """ results = self.swis.query( "SELECT Name FROM Orion.ContainerMembers WHERE ContainerID = @group_id and FullName " "= @node_name", group_id=self.get_group_id(group_name), node_name=node_name) self.logger.info( "is_node_in_group - is_node_in_group query results: %s", results) if results['results']: return True else: return False def add_node_to_group(self, node_name, group_name): """ If the specified node is not already in the specified group, a SWIS invoke of Orion.Container.AddDefinition is made to add the node to the group. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. group_name(string): A group name which should equal the name used in SolarWinds for the container object. Returns: None. """ if not self.is_node_in_group(node_name, group_name): member_definition = { "Name": node_name, "Definition": self.get_node_uri(node_name) } self.swis.invoke('Orion.Container', 'AddDefinition', int(self.get_group_id(group_name)), member_definition) def delete_group_by_id(self, group_id): """ Delete a group that has the specified group id. Args: group_id(string): A group id which should equal the ContainerID used in SolarWinds for the container object. Returns: None. """ self.swis.invoke('Orion.Container', 'DeleteContainer', int(group_id)) def delete_group_by_name(self, group_name): """ Delete a group that has the specified group name. Args: group_name(string): A group name which should equal the Name used in SolarWinds for the container object. Returns: None. """ group_id = self.get_group_id(group_name) self.delete_group_by_id(group_id) def does_dependency_exist(self, dependency_name): """ Checks to see if a SolarWinds dependency exists with the given name. Calls the get_dependency_id method of the class and uses the returned value to determine whether or not the dependency exists. Args: dependency_name(string): A dependency name which should equal the name used in SolarWinds for the dependency object. Returns: True: The dependency exists. False: The dependency does not exist. """ if self.get_dependency_id(dependency_name): return True else: return False def get_dependency_id(self, dependency_name): """ Returns the DependencyID for the given Dependency Name. Uses a SWIS query to the SolarWinds database to retrieve this information. Args: dependency_name(string): A dependency name which should equal the name used in SolarWinds for the dependency object. Returns: dependency_id (string): The dependency ID that corresponds to the specified dependency name. """ dependency_id = self.swis.query( "SELECT DependencyId FROM Orion.Dependencies WHERE Name = @dependency_name", dependency_name=dependency_name) self.logger.info("get_dependency_id - dependency_id query results: %s", dependency_id) if dependency_id['results']: return dependency_id['results'][0]['DependencyId'] else: return "" def add_dependency(self, dependency_name, parent_name, child_name): """ Creates a new dependency using the specified parent and child. Does a SWIS create to the Orion.Dependencies table to create the dependency. Args: dependency_name(string): A dependency name to be used for the newly created dependency. parent_name(string): Name of the parent to be used in the dependency definition. child_name(string): Name of the child to be used in the dependency definition. Returns: True: The dependency was successfully created. False: The dependency was not created. """ if not self.does_dependency_exist(dependency_name): if self.does_node_exist(parent_name): self.logger.info("add-dependency - The parent is a node.") parent_id = self.get_node_id(parent_name) parent_uri = self.get_node_uri(parent_name) parent_entity_type = 'Orion.Nodes' elif self.does_group_exist(parent_name): self.logger.info("add-dependency - The parent is a group.") parent_id = self.get_group_id(parent_name) parent_uri = self.get_group_uri(parent_name) parent_entity_type = 'Orion.Groups' else: return False if self.does_node_exist(child_name): self.logger.info("add-dependency - The child is a node.") child_id = self.get_node_id(child_name) child_uri = self.get_node_uri(child_name) child_entity_type = 'Orion.Nodes' elif self.does_group_exist(child_name): self.logger.info("add-dependency - The child is a group.") child_id = self.get_group_id(child_name) child_uri = self.get_group_uri(child_name) child_entity_type = 'Orion.Groups' else: return False dependency_properties = { 'Name': dependency_name, 'ParentUri': parent_uri, 'ParentEntityType': parent_entity_type, 'ParentNetObjectId': parent_id, 'ChildUri': child_uri, 'ChildEntityType': child_entity_type, 'ChildNetObjectId': child_id } self.swis.create('Orion.Dependencies', **dependency_properties) def get_list_of_interfaces(self, node_name): """ Returns a dictionary of existing Interfaces on a given Node Name. Uses a SWIS query to the SolarWinds database to retrieve this information. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: list_interfaces_names(dictionary): Returns a dictionary that represents all of the interfaces, by name, attached to the node. """ node_id = self.get_node_id(node_name) list_interfaces_names = self.swis.query( "SELECT Name FROM Orion.NPM.Interfaces WHERE NodeID " "= @node_id", node_id=node_id) self.logger.info( "get_list_of_interfaces_by_name - list_of_interface_names query results: %s", list_interfaces_names) if list_interfaces_names['results']: return list_interfaces_names['results'] else: return "" def remove_interface(self, node_name, interface_name): """ For a given node, remove the given interface from the node using the interface name. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. interface_name(string): The name of the interface that will be removed from the node. Returns: True: Interface was successfully removed. False: Interface was not removed. """ interface_uri = self.get_interface_uri(node_name, interface_name) if self.does_interface_exist(node_name, interface_name): self.logger.info( "remove_interface_by_name - interface_uri query results: %s", interface_uri) self.swis.delete(interface_uri) else: return False def get_interface_uri(self, node_name, interface_name): """ Returns the URI for a given interface belonging to a given node. Uses a SWIS query to the SolarWinds database to retrieve this information Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. interface_name(string): The name of the interface that you are getting the URI for Returns: interface_uri(string): The interface URI that corresponds to the specified interface name """ node_id = self.get_node_id(node_name) interface_uri = self.swis.query( "SELECT Uri FROM Orion.NPM.Interfaces WHERE NodeID=@node_id AND " "InterfaceName=@interface_name", node_id=node_id, interface_name=interface_name) if interface_uri['results']: return interface_uri['results'][0]['Uri'] else: return "" def get_interface_id(self, node_name, interface_name): """ Returns the InterfaceID for a given interface belonging to a given node. Uses a SWIS query to the SolarWinds database to retrieve this information Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. interface_name(string): The name of the interface that you are getting the ID of. Returns: interface_id(string): The interface ID that corresponds to the specified interface name """ node_id = self.get_node_id(node_name) interface_id = self.swis.query( "SELECT InterfaceID FROM Orion.NPM.Interfaces WHERE NodeID=@node_id AND " "Name = @interface_name", node_id=node_id, interface_name=interface_name) if interface_id['results']: return interface_id['results'][0]['InterfaceID'] else: return "" def does_interface_exist(self, node_name, interface_name): """ Checks to see if a SolarWinds interface, belonging to a given node, exists with the given name. Calls the get_interface_id method of the class and uses the returned value to determine whether or not the interface exists. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. interface_name(string): The name of the interface that you are getting the URI for Returns: True: The interface exists. False: THe interface does not exist. """ if self.get_interface_id(node_name, interface_name): return True else: return False def get_discovered_interfaces(self, node_name): """ Returns a dictionary of Discovered Interfaces on a node given that node's name. Uses a SWIS invoke for DiscoverInterfacesOnNode. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: discovered_interfaces(Dictionary): The set of discovered interfaces on the node. """ node_id = self.get_node_id(node_name) discovered_interfaces = self.swis.invoke('Orion.NPM.Interfaces', 'DiscoverInterfacesOnNode', node_id) return discovered_interfaces['DiscoveredInterfaces'] def add_interface(self, node_name, interface_name): """ For a given node, attach the given interface by that interface's name. The given interface must be a discovered interface to be attached to the node. Uses a SWIS invoke for AddInterfacesOnNode. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. interface_name(string): The name of the interface that will be added to the node. Returns: True: The interface was added to the node. False: The interface was not successfully added to the node. """ node_id = self.get_node_id(node_name) discovered_interfaces = self.get_discovered_interfaces(node_name) discovered_interface = [ x for x in discovered_interfaces if x['Caption'].startswith(interface_name) ] if discovered_interface: self.swis.invoke('Orion.NPM.Interfaces', 'AddInterfacesOnNode', node_id, discovered_interface, 'AddDefaultPollers') else: return False def ncm_download_nodes_running_config(self, node_name): """ For a given node, download the node's running configuration. Uses a SWIS query to find the Cirrus node I. Uses a Swis invoke of DownloadConfig. Args: node_name(string): A node name which should equal the caption used in SolarWinds for the node object. Returns: None. """ results = self.swis.query( 'SELECT NodeID FROM Cirrus.Nodes WHERE NodeCaption = @node_name', node_name=node_name)['results'] cirrus_node_id = results[0]['NodeID'] self.swis.invoke('Cirrus.ConfigArchive', 'DownloadConfig', [cirrus_node_id], 'Running') def ncm_run_compliance_report(self, report_name): """ For a given report name, run the Policy Report. Uses a SWIS query to get the policy report ID. Uses a SWIS invoke of StartCaching to run the policy report. Args: report_name(string): A report name which should equal the Name used in SolarWinds for a Policy Report object Returns: None. """ results = self.swis.query( 'SELECT PolicyReportID FROM Cirrus.PolicyReports WHERE Name = @report_name', report_name=report_name) report_id = results['results'][0]['PolicyReportID'] self.swis.invoke('Cirrus.PolicyReports', 'StartCaching', [report_id])