def basic_demo(): """ Enable this to be run as a CLI script, as well as used as a library. Mostly intended for testing or a basic demo. """ # Get the command-line arguments parser = argparse.ArgumentParser( description='Perform SNMP discovery on a host, \ returning its data in a single structure.') parser.add_argument( '--hostname', type=str, action='store', default='localhost', help= 'The hostname or address to perform discovery on. Default: localhost') parser.add_argument('--community', type=str, action='store', dest='community', default='public', help='SNMP v2 community string. Default: public') parser.add_argument( '--file', type=str, action='store', dest='filepath', default=None, help='Filepath to write the results to. Default: STDOUT.') parser.add_argument('--debug', action='store_true', help='Enable debug logging') args = parser.parse_args() # Set debug logging, if requested if args.debug: logger = create_logger(loglevel="debug") # Normal logging if we're writing to a file elif args.filepath: logger = create_logger() # Suppress INFO output if we're returning it to STDOUT: # don't require the user to filter the output to make it useful. else: logger = create_logger(loglevel="warning") # Perform SNMP discovery on a device, # sending the result to STDOUT or a file, depending on what the user told us. if args.filepath: netdescribe.files.snmp_to_json(args.hostname, args.community, args.filepath, logger) else: netdescribe.stdout.snmp_to_json(args.hostname, args.community, logger)
def explore_device(hostname: str, logger: Optional[logging.Logger] = None, community: str = 'public', port: int = 161) -> Union[None, Brocade, Linux, Mib2]: ''' Build up a picture of a device via SNMP queries. Return the results as a nest of dicts: - sysinfo: output of identify_host() - network: output of discover_host_networking() ''' # Ensure we have a logger if not logger: logger = create_logger() # Now get to work logger.info('Performing discovery on %s', hostname) # Create an object to represent this device, # taking its SNMP capabilities into account try: device: Union[None, Brocade, Linux, Mib2] = create_device(hostname, logger, community, port) if device: # Perform discovery as appropriate to this device type device.discover() # Return the device object, complete with its discovered data return device return None except RuntimeError as err: logger.error('Error caught: %s', str(err)) return None
def main(args): "Replicate ThousandEyes data to Syscat" # Setup and configuration logger = create_logger(loglevel=args.loglevel) te_api = ApiServer(username=args.te_user, api_key=args.te_key) syscat = Syscat(fqdn=args.syscat_fqdn, port=args.syscat_port) ## Agents logger.info('Retrieving a list of agents from ThousandEyes') agents = map(lambda x: make_agent(x, logger), te_api.get_list('agents')['agents']) # Individual agents for agent in agents: logger.info('Adding {} {} with agent ID {}'.format( agent.syscat_type, agent.agentname, agent.agentid)) agent.store_in_syscat(syscat) if agent.syscat_type == 'thousandeyesEnterpriseClusters': # Add cluster members details = te_api.get_details('agents', agent.agentid)['agents'][0] logger.debug('agent details: {}'.format(details)) logger.info('Enumerating members') for member in details['clusterMembers']: logger.debug('clusterMember details: {}'.format(member)) logger.info('Adding clusterMember {}'.format(member['name'])) EnterpriseClusterMember( name=member['name'], ipaddresses=member['ipAddresses'], publicipaddresses=member['publicIpAddresses'], prefix=member['prefix'], network=member['network'], agentstate=member['agentState']).store_in_syscat( syscat, agent) # Add tests logger.info('Enumerating tests') for test in details['tests']: logger.debug('Test details: {}'.format(test)) logger.info('Adding test {}'.format(test['testName'])) if 'modifiedBy' not in test: test['modifiedBy'] = '' if 'modifiedDate' not in test: test['modifiedDate'] = '' GenericTest( testname=test['testName'], testid=test['testId'], test_type=test['type'], enabled=test['enabled'], savedevent=test['savedEvent'], interval=test['interval'], modifiedby=test['modifiedBy'], modifieddate=test['modifiedDate'], createdby=test['createdBy'], createddate=test['createdDate']).store_in_syscat(syscat) syscat.link_resources( '/{ttype}/{tuid}/Agents'.format( ttype='thousandeyesTests', tuid=syscat_utils.sanitise_uid(test['testName'])), '/{atype}/{auid}'.format(atype=agent.syscat_type, auid=syscat_utils.sanitise_uid( agent.agentname)))
def main(syscat_url, community, loglevel=logging.INFO): """ Wrapper around discover_into_syscat. """ logger = create_logger(loglevel=loglevel) logger.info('Beginning discovery') response = requests.get('{}/raw/v1/devices'.format(syscat_url)) if response.status_code == 200: for device in response.json(): discover_device(syscat_url, community, logger, device) else: logger.error('Initial query failed: {} - {}'.format(response.status_code, response.text))
def snmp_to_json(target, community, logger=None): """ Explore a device via SNMP, and return the results to STDOUT in JSON. """ # Ensure we have a logging object. # Normally I'd default the loglevel to INFO, but this function will be more # useful if its output can be consumed directly, without having to filter # out any log messages. if logger: slogger = logger else: slogger = create_logger(loglevel="warn") # Perform SNMP discovery on a device and print the result to STDOUT. # Do basic pretty-printing of the output, for human-readability. response = device_discovery.explore_device(target, slogger, community=community) print(response)
def discover_into_syscat( address, # IP address, FQDN or otherwise resolvable address name=None, # Name of target device, to override the discovered one use_sysname=False, # Use the discovered sysName as the Syscat UID snmpcommunity="public", syscat_url="http://localhost:4950", # Default base URL for Syscat loglevel="info", # Default loglevel logger_arg=None): """ Ensure that there's an entry in Syscat for the device we just discovered. Update existing instances, and return a dict describing any updates. Return True if the result was a new entry; otherwise, return a dict describing the updates. Assumes version 1 of the Syscat API. Structure of the return value: - system - <attribute-name> - existing: <value currently in Syscat> - discovered: <value discovered just now> """ # Establish logging if logger_arg: logger = logger_arg else: logger = create_logger(loglevel=loglevel) logger.info("Performing discovery on device at %s", address) # Perform discovery response = netdescribe.snmp.device_discovery.explore_device( address, logger, snmpcommunity) if not response: logger.error('Failed to perform discovery on device at address %s', address) return False logger.debug("Result of discovery was:\n%s", response.as_json()) # Get the "raw" dict of namedtuples, not the JSON transformation device = response.as_dict() # Resolve the device's UID if name: uid = name elif use_sysname and device['system'][ 'sysName'] and device['system']['sysName'] != "": uid = device['system']['sysName'] else: uid = address logger.debug("Using name '%s' for device", uid) # Is it already there? existing_response = requests.get("%s/raw/v1/devices/%s" % (syscat_url, uid)) # No existing entry; create one if existing_response.json() == []: logger.debug("%s is not present in Syscat; creating it.", uid) # Create the device entry itself add_device(uid, device['system']['sysName'], device['system']['sysDescr'], syscat_url, logger) created_new_device = True # We already have one of these; log the fact and ensure it's up to date elif existing_response.json() != []: logger.debug( "%s is already present in Syscat. Ensuring it's up to date...", uid) created_new_device = False # Compare the system attributes diffs = compare_discovered_device_to_syscat(device, existing_response.json(), logger) # Perform any necessary updates to the device's own attributes if diffs and 'system' in diffs: devices_url = "%s/raw/v1/devices" % syscat_url payload = {} for attr, vals in diffs['system'].items(): payload[attr] = vals['discovered'] logger.info('Updating system for %s with details %s', uid, payload) requests.put('%s/%s' % (devices_url, uid), data=payload) # No updates needed. Do mention this, so the user knows where we're up to else: logger.debug('No system updates needed.') # Something else happened. else: logger.critical("Syscat returned an unexpected result: %s %s", existing_response.status_code, existing_response.text) sys.exit(1) # Now ensure its interfaces are correctly described populate_interfaces_flat(uid, device['interfaces'], syscat_url, logger, newdevice=created_new_device) # Return a report on what we updated. if created_new_device: return True return diffs