def setUp(self): """ Prepare the mock objects for use with the tests """ self.session = AttrDict() self.context = AttrDict({ 'default_cred_name': 'default', 'credentials': Credentials({ 'default': {'username': '******', 'password': '******'}, 'mycred': {'username': '******', 'password': '******'}, 'enable': {'password': '******'}, }) }) class MockSpawn: pass def mock_sendline(*args, **kwargs): print("Sendline called with: %s %s" % (args, kwargs)) self.spawn = MockSpawn() self.spawn.spawn_command = 'ssh -l cisco@router' self.spawn.last_sent = 'ssh -l cisco@router' self.spawn.sendline = Mock(side_effect=mock_sendline) self.spawn.match = AttrDict() self.spawn.match.match_output = "" self.spawn.settings = Mock()
def test_add_to_testbed(self): testbed = AttrDict(servers=AttrDict()) with FileServer(protocol='ftp', subnet='127.0.0.1/32', testbed=testbed, name='myserver') as fs: self.assertEqual(testbed.servers.myserver, fs)
def setUp(self): """ Prepare the mock objects for use with the tests """ self.session = AttrDict() self.context = AttrDict({ 'username': '******', 'tacacs_password': '******', 'enable_password': "******", 'line_password': "******" }) class MockSpawn: pass def mock_sendline(*args, **kwargs): print("Sendline called with: %s %s" % (args, kwargs)) self.spawn = MockSpawn() self.spawn.buffer = '' self.spawn.spawn_command = 'ssh -l cisco@router' self.spawn.last_sent = 'ssh -l cisco@router' self.spawn.sendline = Mock(side_effect=mock_sendline) self.spawn.match = AttrDict() self.spawn.match.match_output = "" self.spawn.settings = Mock() self.spawn.settings.PASSWORD_ATTEMPTS = 3
def load_netbox(self, steps): with steps.start("Loading Devices and VLANs from Netbox."): from get_from_netbox import devices, vlans netbox = AttrDict() netbox.devices = devices netbox.vlans = vlans self.parent.parameters.update(netbox=netbox)
def __init__(self): '''Built-in __init__ Initializes GenieTelemetry object with default values required for the parser. ''' # collect environment information # ------------------------------- self.env = AttrDict(argv=' '.join(sys.argv), prefix=sys.prefix, user=getpass.getuser(), host=platform.node()) # enable double ctrl-c SIGINT handler # ----------------------------------- sig_handlers.enable_double_ctrl_c() # create command-line argv parser # ------------------------------- self.parser = Parser() self.manager = None self.liveview = None self.stream_logger = StreamToLogger() sys.stdout = self.stream_logger
def _parse(raw_cli_output, cmd, nos): # Boilerplate code to get the parser functional # tb = Testbed() device = Device("new_device", os=nos) device.custom.setdefault("abstraction", {})["order"] = ["os"] device.cli = AttrDict({"execute": None}) # User input checking of the command provided. Does the command have a Genie parser? try: get_parser(cmd, device) except Exception as e: raise AnsibleFilterError( "genie_parse: {0} - Available parsers: {1}".format( to_native(e), "https://pubhub.devnetcloud.com/media/pyats-packages/docs/genie/genie_libs/#/parsers" )) try: parsed_output = device.parse(cmd, output=raw_cli_output) return parsed_output except Exception as e: raise AnsibleFilterError( "genie_parse: {0} - Failed to parse command output.".format( to_native(e)))
def __enter__(self): # Start server, usually by spawning a process and get new info info = self.start_server() recursive_update(self.server_info, info) # Verify server is successfully running using a client try: self.verify_server() except Exception as e: self.stop_server() raise OSError('Failed to verify %s' % str(type(self))) from e # Log address of server address = self.server_info.get('address', '0.0.0.0') port = self.server_info.get('port') if port: address += ':%s' % port logger.info('%s File Server started on %s' % (self.protocol.upper(), address)) # Update testbed with new server info if self.testbed is not None and self.name: recursive_update( self.testbed.servers.setdefault(self.name, AttrDict()), self.server_info) return self.server_info
def genie_parser(self, cli_output, command, os): if not PY3: raise AnsibleFilterError("Genie requires Python 3") if not HAS_GENIE: raise AnsibleFilterError( "Genie not found. Run 'pip install genie'") if not HAS_PYATS: raise AnsibleFilterError( "pyATS not found. Run 'pip install pyats'") device = Device("new_device", os=os) device.custom.setdefault("abstraction", {})["order"] = ["os"] device.cli = AttrDict({"execute": None}) try: get_parser(command, device) except Exception as e: raise AnsibleFilterError( "Unable to find parser for command '{0}' ({1})".format( command, e)) try: parsed_output = device.parse(command, output=cli_output) except Exception as e: raise AnsibleFilterError( "Unable to parse output for command '{0}' ({1})".format( command, e)) if parsed_output: return parsed_output else: return None
def store_structure(device, feature): # get feature and attributes [(ft, attr)] = feature.items() log.info( banner("Learning '{n}' feature with " "attribues {a} on device {d}".format(n=ft, a=attr, d=device))) # perform lookup per device lib = Lookup.from_device(device) # attach ops and conf lib.conf = getattr(lib, 'conf', conf) lib.ops = getattr(lib, 'ops', ops) # create the ops/conf instance try: obj = attrgetter(ft)(lib) except Exception: raise AttributeError('Cannot load %s for ' 'device %s.' % (ft, device.name)) # conf learn_config if issubclass(obj, ConfBase): ret = obj.learn_config(device=device, attributes=attr) ret = _to_dict(ret[0]) # delete the non-used objects for pcall to retrun ret.pop('__testbed__') ret.pop('devices') ret.pop('interfaces') remove_parent_from_conf_dict(ret['device_attr'][device.name]) elif issubclass(obj, OpsBase): ret = obj(device, attributes=attr) ret.learn() temp = AttrDict() temp.info = getattr(ret, 'info', {}) ret = temp ret_dict = {} ret_dict.setdefault('lts', {}).\ setdefault(ft, {}).setdefault(device.name, ret) # return the dictionary return ret_dict
def test_fu_from_device_protocol(self): # Instantiate a filetransferutils instance for each os and protocol for os_ in ['ios', 'iosxe', 'iosxr', 'nxos']: device = AttrDict(os=os_) for proto in ['ftp', 'tftp', 'scp', 'sftp', 'http']: fu = FileUtils.from_device(device, protocol=proto) self.assertIn( 'genie.libs.filetransferutils.plugins.{os_}.{proto}.fileutils.FileUtils' .format(os_=os_, proto=proto), str(fu.__class__)) self.assertEqual(fu.protocol, proto) self.assertEqual(fu.os, os_)
def _check_commands_against_pyats(self): network_os = self._task.args.get('network_os') or self._network_os self._pyats_device = Device("uut", os=network_os) self._pyats_device.custom.setdefault("abstraction", {})["order"] = ["os"] self._pyats_device.cli = AttrDict({"execute": None}) for command in self._commands: try: get_parser(command['command'], self._pyats_device) except Exception: # pylint: disable=W0703 self._errors.append("PYATS: Unable to find parser for command " "'{}' for {}".format( command['command'], network_os)) self._check_for_errors()
def _ixia_add_on(self, objects, device): # disconnecting the device for ixia, incase user didint put subsection datafile if device.type == 'tgn' and device.is_connected(): device.type = 'ixia' device.disconnect() # transforming tgn input in testbed to hltapi device.connections['hltapi'] = AttrDict({ 'protocol': 'hltapi', 'ip': device.connections.tgn.ixia_chassis_ip, 'tcl_server': device.connections.tgn.ixia_chassis_ip, 'ixnetwork_tcl_server': device.connections.tgn.ixnetwork_api_server_ip + ':' + str(device.connections.tgn.ixnetwork_tcl_port), 'ixia_port_list': device.connections.tgn.ixia_port_list }) # saving the ixia saved variables (that is different than normal saved variables) # into the objects that would be the input to the plugin objects.setdefault('ixiaObjs', {}) objects['ixiaObjs'].setdefault('ixiaNet', {}) objects['ixiaObjs'].setdefault('ixiaNetSelf', {}) objects['ixiaObjs'].setdefault('ixiaNetStop', {}) # if an ixiaNet object was provided from previous ixia plugin calls it is stored # they should be passed into the plugin as input if 'save_variable_name' in self.parameters and 'ixiaNet' in self.parameters[ 'save_variable_name']: objects['ixiaObjs']['ixiaNet'].update( self.parameters['save_variable_name']['ixiaNet']) if 'save_variable_name' in self.parameters and 'ixiaNetSelf' in self.parameters[ 'save_variable_name']: objects['ixiaObjs']['ixiaNetSelf'].update( self.parameters['save_variable_name']['ixiaNetSelf']) if 'save_variable_name' in self.parameters and 'ixiaNetStop' in self.parameters[ 'save_variable_name']: objects['ixiaObjs']['ixiaNetStop'].update( self.parameters['save_variable_name']['ixiaNetStop']) return objects
def get_structured_data_genie(raw_output: str, platform: str, command: str) -> Union[str, Dict[str, Any]]: if not sys.version_info >= (3, 4): raise ValueError("Genie requires Python >= 3.4") if not GENIE_INSTALLED: msg = ( "\nGenie and PyATS are not installed. Please PIP install both Genie and PyATS:\n" "pip install genie\npip install pyats\n") raise ValueError(msg) if "cisco" not in platform: return raw_output genie_device_mapper = { "cisco_ios": "ios", "cisco_xe": "iosxe", "cisco_xr": "iosxr", "cisco_nxos": "nxos", "cisco_asa": "asa", } os = None # platform might be _ssh, _telnet, _serial strip that off if platform.count("_") > 1: base_list = platform.split("_")[:-1] base_platform = "_".join(base_list) else: base_platform = platform os = genie_device_mapper.get(base_platform) if os is None: return raw_output # Genie specific construct for doing parsing (based on Genie in Ansible) device = Device("new_device", os=os) device.custom.setdefault("abstraction", {}) device.custom["abstraction"]["order"] = ["os"] device.cli = AttrDict({"execute": None}) try: # Test whether there is a parser for given command (return Exception if fails) get_parser(command, device) parsed_output: Dict[str, Any] = device.parse(command, output=raw_output) return parsed_output except Exception: return raw_output
def test_pass_processor(self): [d.connect() for d in testbed.devices.values()] sys.argv = ['easypy', '--genietelemetry', config_file2] with self.assertLogs('', level='INFO') as cm: self.assertTrue(section.result) self.assertIsNone(section.message) # processors.runtime = Mock(side_effect=runtime) with patch.object(processors, 'runtime', new_callable=PropertyMock) as mock_runtime: mock_runtime.testbed = testbed mock_runtime.runinfo = AttrDict() mock_runtime.runinfo.runinfo_dir = runinfo_dir processors.genie_telemetry_processor(section) output = '\n'.join(cm.output) msg = "failed to load abstration on device P1 for plugin mockplugin" self.assertFalse(msg in output) self.assertTrue(section.result) self.assertIsNone(section.message)
def parse(self, *_args, **_kwargs): """Std entry point for a cli_parse parse execution :return: Errors or parsed text as structured data :rtype: dict :example: The parse function of a parser should return a dict: {"errors": [a list of errors]} or {"parsed": obj} """ errors = self._check_reqs() errors.extend(self._check_vars()) if errors: return {"errors": errors} command = self._task_args.get("parser").get("command") network_os = ( self._task_args.get("parser").get("os") or self._transform_ansible_network_os() ) cli_output = self._task_args.get("text") device = Device("new_device", os=network_os) device.custom.setdefault("abstraction", {})["order"] = ["os"] device.cli = AttrDict({"execute": None}) try: parsed = device.parse(command, output=cli_output) except Exception as exc: msg = "The pyats library return an error for '{cmd}' for '{os}'. Error: {err}." return { "errors": [ ( msg.format( cmd=command, os=network_os, err=to_native(exc) ) ) ] } return {"parsed": parsed}
def _parse_generic_tabular(cli_output, os, headers, key_index): # Boilerplate code to get the parser functional tb = Testbed() device = Device("new_device", os=os) device.custom.setdefault("abstraction", {})["order"] = ["os"] device.cli = AttrDict({"execute": None}) # Do the parsing # result = parsergen.oper_fill_tabular(device_output=cli_output, device_os=nos, header_ # fields=headers, index=[key]) result = parsergen.oper_fill_tabular(device_output=cli_output, device_os=os, header_fields=headers, index=key_index) # Structured data, but it has a blank entry because of the first line of the output # being blank under the headers. parsed_output = result.entries return parsed_output
class test_filetransferutils(unittest.TestCase): # Instantiate tesbed and device objects tb = Testbed(name='myTestbed') device = Device(testbed=tb, name='aDevice', os='junos') # Instantiate a filetransferutils instance for Junos device fu_device = FileUtils.from_device(device) # Add testbed servers for authentication device.testbed.servers = AttrDict(server_name=dict(username="******", password="******", address='1.1.1.1'), ) # Mock device output raw1 = ''' file copy golden_config ftp://[email protected]:/test/ Password for [email protected]: ftp://[email protected]:/test/golden_config 100% of 3040 B 11 MBps ''' outputs = {} outputs['file copy golden_config ftp://[email protected]:/test/']\ = raw1 def mapper(self, key, timeout=None, reply=None, prompt_recovery=False): return self.outputs[key] def test_copyfile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles self.fu_device.copyfile(source='golden_config', destination='ftp://1.1.1.1:/test/', timeout_seconds='300', device=self.device)
def pyats_parser(cli_output, command, os): if not PY3: raise AnsibleFilterError("Genie requires Python 3") if GENIE_IMPORT_ERROR: raise_from( AnsibleError('genie must be installed to use this plugin'), GENIE_IMPORT_ERROR) if PYATS_IMPORT_ERROR: raise_from( AnsibleError('pyats must be installed to use this plugin'), PYATS_IMPORT_ERROR) # Translate from ansible_network_os values to pyATS if os in ansible_os_map.keys(): os = ansible_os_map[os] device = Device("uut", os=os) device.custom.setdefault("abstraction", {})["order"] = ["os"] device.cli = AttrDict({"execute": None}) try: get_parser(command, device) except Exception as e: raise AnsibleFilterError("Unable to find parser for command '{0}' ({1})".format(command, e)) try: parsed_output = device.parse(command, output=cli_output) except Exception as e: raise AnsibleFilterError("Unable to parse output for command '{0}' ({1})".format(command, e)) if parsed_output: return parsed_output else: return None
def load_netbox(self, testbed, steps): # testbed = self.parameters["testbed"] netbox_token = os.getenv("NETBOX_TOKEN") netbox_url = os.getenv("NETBOX_URL") site_name = os.getenv("NETBOX_SITE") tenant_name = os.getenv("NETBOX_TENANT") netbox = pynetbox.api(netbox_url, token=netbox_token) tenant = netbox.tenancy.tenants.get(name=tenant_name) mgmt_tenant = netbox.tenancy.tenants.get(name="Management") site = netbox.dcim.sites.get(name=site_name) for device in testbed.devices: with steps.start( "Pulling Device and Interface Info from Netbox.") as step: testbed.devices[device].netbox = AttrDict() testbed.devices[ device].netbox.device = netbox.dcim.devices.get( site_id=site.id, tenant_id=tenant.id, name=device) if not testbed.devices[device].netbox.device is None: testbed.devices[ device].netbox.device.interfaces = netbox.dcim.interfaces.filter( device_id=testbed.devices[device].netbox.device.id) if (not testbed.devices[device].netbox.device.interfaces is None): for interface in testbed.devices[ device].netbox.device.interfaces: interface.ip_addresses = netbox.ipam.ip_addresses.filter( interface_id=interface.id) for ip_address in interface.ip_addresses: ip_address.ip = ipaddress.ip_address( ip_address.address.split("/")[0]) ip_address.network = ipaddress.ip_network( ip_address.address, strict=False)
def find_plugins(): plugin_path = os.path.dirname(unicon.plugins.__file__) plugin_list = [] for subdir, dirs, files in os.walk(plugin_path): for file in files: if file == '__init__.py': with open(os.path.join(subdir, file), 'rb') as f: content_length = len(f.read().strip()) if content_length: plugin = os.path.relpath(subdir, start=plugin_path) p = plugin.split('/') plugin_attributes = AttrDict() if len(p): plugin_attributes.os = p[0] if len(p) > 1: plugin_attributes.series = p[1] else: plugin_attributes.series = None if len(p) > 2: plugin_attributes.model = p[2] else: plugin_attributes.model = None plugin_list.append(plugin_attributes) return plugin_list
class test_filetransferutils(unittest.TestCase): # Instantiate tesbed and device objects tb = Testbed(name='myTestbed') device = Device(testbed=tb, name='aDevice', os='iosxe') # Instantiate a filetransferutils instance for IOSXE device fu_device = FileUtils.from_device(device) # Add testbed servers for authentication device.testbed.servers = AttrDict( server_name = dict( username="******", password="******", address='1.1.1.1'), ) dir_output = ['flash:/nvram_config', 'flash:/.rollback_timer', 'flash:/memleak.tcl', 'flash:/bootloader_evt_handle.log', 'flash:/ISSUCleanGolden', 'flash:/gs_script', 'flash:/.prst_sync', 'flash:/nvram_config_bkup', 'flash:/tech_support', 'flash:/dc_profile_dir', 'flash:/RestoreTue_Mar_20_12_19_11_2018-Mar-20-11-20-09.900-0', 'flash:/vlan.dat', 'flash:/core', 'flash:/tools', 'flash:/CRDU', 'flash:/.dbpersist', 'flash:/RestoreTue_Mar_20_12_13_39_2018-Mar-20-11-14-38.106-0', 'flash:/iox', 'flash:/onep', 'flash:/boothelper.log', 'flash:/stby-vlan.dat', 'flash:/.installer'] # Mock device output raw1 = ''' copy flash:/memleak.tcl ftp://1.1.1.1//auto/tftp-ssr/memleak.tcl Address or name of remote host [1.1.1.1]? Destination filename [/auto/tftp-ssr/memleak.tcl]? !! 104260 bytes copied in 0.396 secs (263283 bytes/sec) ''' raw2 = ''' Directory of flash:/ 69698 drwx 4096 Mar 20 2018 10:25:11 +00:00 .installer 69720 -rw- 2097152 Mar 20 2018 13:09:24 +00:00 nvram_config 69700 -rw- 90761 Mar 20 2018 10:25:27 +00:00 bootloader_evt_handle.log 69701 drwx 4096 Feb 1 2018 13:44:32 +00:00 core 15489 drwx 4096 Mar 20 2018 10:31:08 +00:00 .prst_sync 30977 drwx 4096 May 2 2016 07:58:53 +00:00 .rollback_timer 38722 drwx 4096 Mar 20 2018 10:25:43 +00:00 dc_profile_dir 69699 -rw- 76 Mar 20 2018 10:25:46 +00:00 boothelper.log 69705 -rw- 104260 Mar 20 2018 10:26:01 +00:00 memleak.tcl 69706 drwx 4096 May 2 2016 08:11:23 +00:00 onep 69714 drwx 4096 Aug 13 2016 08:55:12 +00:00 iox 69734 -rw- 3496 Mar 11 2018 17:40:26 +00:00 vlan.dat 69708 -rw- 617329255 Sep 27 2017 09:11:39 +00:00 ISSUCleanGolden 69709 drwx 4096 Aug 3 2016 08:07:47 +00:00 gs_script 69712 drwx 4096 Mar 19 2017 09:26:23 +00:00 tools 69719 drwx 4096 Feb 12 2018 11:20:01 +00:00 .dbpersist 69703 -rw- 2097152 Mar 20 2018 13:09:25 +00:00 nvram_config_bkup 69729 -rw- 3496 Feb 12 2018 12:51:01 +00:00 stby-vlan.dat 69735 -rw- 27145 Mar 20 2018 11:14:45 +00:00 RestoreTue_Mar_20_12_13_39_2018-Mar-20-11-14-38.106-0 69721 drwx 4096 Sep 25 2017 07:59:54 +00:00 CRDU 69727 drwx 4096 Oct 23 2017 13:40:11 +00:00 tech_support 69736 -rw- 27145 Mar 20 2018 11:20:16 +00:00 RestoreTue_Mar_20_12_19_11_2018-Mar-20-11-20-09.900-0 1621966848 bytes total (906104832 bytes free) ''' raw3 =''' delete flash:memleak.tcl Delete filename [memleak.tcl]? Delete flash:/memleak.tcl? [confirm] ''' raw4 = ''' rename flash:memleak.tcl new_file.tcl Destination filename [new_file.tcl]? ''' raw5 = ''' show clock | redirect ftp://1.1.1.1//auto/tftp-ssr/show_clock Writing /auto/tftp-ssr/show_clock ''' raw6 = {'futlinux.check_file.return_value': '', 'futlinux.deletefile.return_value': ''} raw7 = ''' copy running-config tftp://10.1.7.250//auto/tftp-ssr/test_config.py Address or name of remote host [10.1.7.250]? Destination filename [/auto/tftp-ssr/test_config.py]? !! 27092 bytes copied in 6.764 secs (4005 bytes/sec) ''' outputs = {} outputs['copy flash:/memleak.tcl ' 'ftp://*****:*****@1.1.1.1//auto/tftp-ssr/memleak.tcl']\ = raw1 outputs['dir'] = raw2 outputs['delete flash:memleak.tcl'] = raw3 outputs['rename flash:memleak.tcl new_file.tcl'] = raw4 outputs['show clock | redirect ftp://1.1.1.1//auto/tftp-ssr/show_clock'] = \ raw5 outputs['copy running-config tftp://10.1.7.250//auto/tftp-ssr/test_config.py'] = \ raw7 def mapper(self, key, timeout=None, reply= None, prompt_recovery=False, error_pattern=None): return self.outputs[key] def test_copyfile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles self.fu_device.copyfile(source='flash:/memleak.tcl', destination='ftp://1.1.1.1//auto/tftp-ssr/memleak.tcl', timeout_seconds='300', device=self.device) def test_dir(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper directory_output = self.fu_device.dir(target='flash:', timeout_seconds=300, device=self.device) self.assertEqual(sorted(directory_output), sorted(self.dir_output)) def test_stat(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper file_details = self.fu_device.stat(target='flash:memleak.tcl', timeout_seconds=300, device=self.device) self.assertEqual(file_details['last_modified_date'], 'Mar 20 2018 10:26:01 +00:00') self.assertEqual(file_details['permissions'], '-rw-') self.assertEqual(file_details['index'], '69705') self.assertEqual(file_details['size'], '104260') def test_deletefile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.deletefile(target='flash:memleak.tcl', timeout_seconds=300, device=self.device) def test_renamefile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.renamefile(source='flash:memleak.tcl', destination='new_file.tcl', timeout_seconds=300, device=self.device) @patch('genie.libs.filetransferutils.plugins.fileutils.FileUtils.validateserver', return_value=raw6) def test_validateserver(self, raw6): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.validateserver( target='ftp://1.1.1.1//auto/tftp-ssr/show_clock', timeout_seconds=300, device=self.device) def test_copyconfiguration(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.copyconfiguration(source='running-config', destination='tftp://10.1.7.250//auto/tftp-ssr/test_config.py', timeout_seconds=300, device=self.device)
def save_variable(self, section, save_variable_name, output=None, append=None, append_in_list=None): # for pyATS Health Check # use section.parent for self.parent if not hasattr(self.parent, 'parameters'): self.parent = section.parent # Save output variable self.parameters.setdefault('save_variable_name', {}) self.parent.parameters.setdefault('save_variable_name', AttrDict({'testscript': AttrDict({})})) save_variable_name_str = save_variable_name # if testscript. lets save in a global level if 'testscript.' in save_variable_name and \ save_variable_name != 'testscript.name': # using AttrDict to save in parent and to retrieve save_variable_name = save_variable_name.replace('testscript.', '') saved_vars = self.parent.parameters['save_variable_name']['testscript'] else: saved_vars = self.parameters['save_variable_name'] if save_variable_name in saved_vars: saved_val = saved_vars[save_variable_name] if append: if output: saved_vars.update( {save_variable_name: saved_val + '\n' + output}) log.info( 'Appended the following into the variable {}, {}'.format( save_variable_name_str, str(output))) elif append_in_list: if isinstance(saved_val, list): saved_val.append(output) saved_vars.update({save_variable_name: saved_val}) log.info('Appended {} to list variable {}'.format( str(output), save_variable_name_str)) else: saved_vars.update( {save_variable_name: output if output is not None else ''}) log.info('Saved {} in variable {}'.format(str(output), save_variable_name_str)) else: if append_in_list: saved_vars.update({save_variable_name: [output]}) log.info('Saved {} in list variable {}'.format( str(output), save_variable_name_str)) else: saved_vars.update( {save_variable_name: output if output is not None else ''}) log.info('Saved {} in variable {}'.format(str(output), save_variable_name_str))
def main(): argument_spec = dict(command=dict(type='str', required=True), prompt=dict(type='list', required=False), answer=dict(type='list', required=False), compare=dict(type='dict', required=False), sendonly=dict(type='bool', default=False, required=False), # newline=dict(type='bool', default=True, required=False), # check_all=dict(type='bool', default=False, required=False), ) required_together = [['prompt', 'answer']] module = AnsibleModule(argument_spec=argument_spec, required_together=required_together, supports_check_mode=True) if not PY3: module.fail_json(msg="pyATS/Genie requires Python 3") if not HAS_GENIE: module.fail_json(msg="Genie not found. Run 'pip install genie'") if not HAS_PYATS: module.fail_json(msg="pyATS not found. Run 'pip install pyats'") if module.check_mode and not module.params['command'].startswith('show'): module.fail_json( msg='Only show commands are supported when using check_mode, not ' 'executing %s' % module.params['command'] ) warnings = list() result = {'changed': False, 'warnings': warnings} connection = Connection(module._socket_path) capabilities = json.loads(connection.get_capabilities()) if capabilities['device_info']['network_os'] == 'ios': genie_os = 'iosxe' else: genie_os = capabilities['device_info']['network_os'] compare = module.params.pop('compare') response = '' try: response = connection.get(**module.params) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) device = Device("uut", os=genie_os) device.custom.setdefault("abstraction", {})["order"] = ["os"] device.cli = AttrDict({"execute": None}) try: get_parser(module.params['command'], device) except Exception as e: module.fail_json(msg="Unable to find parser for command '{0}' ({1})".format(module.params['command'], e)) try: parsed_output = device.parse(module.params['command'], output=response) except Exception as e: module.fail_json(msg="Unable to parse output for command '{0}' ({1})".format(module.params['command'], e)) # import sys; # sys.stdin = open('/dev/tty') # import pdb; # pdb.set_trace() if compare: diff = Diff(parsed_output, compare, exclude=get_parser_exclude(module.params['command'], device)) diff.findDiff() else: diff = None if not module.params['sendonly']: try: result['json'] = module.from_json(response) except ValueError: pass result.update({ 'stdout': response, 'structured': parsed_output, 'diff': "{0}".format(diff), 'exclude': get_parser_exclude(module.params['command'], device), }) module.exit_json(**result)
def test_passx_processor(self): [d.connect() for d in testbed.devices.values()] sys.argv = ['easypy', '--genietelemetry', config_file] with self.assertLogs('', level='INFO') as cm: # processors.runtime = Mock(side_effect=runtime) with patch.object(processors, 'runtime', new_callable=PropertyMock) as mock_runtime: mock_runtime.testbed = testbed mock_runtime.runinfo = AttrDict() mock_runtime.runinfo.runinfo_dir = runinfo_dir processors.genie_telemetry_processor(section) output = '\n'.join(cm.output) msg = "failed to load abstration on device P1 for plugin mockplugin" self.assertTrue(msg in output) self.assertEqual(section.result, Passx) self.assertIsNotNone(section.message) msg = ( "'genie.telemetry' caught anomalies: \n" "genie.telemetry.tests.scripts.mockplugin\n\tP1\n\t\tpartial") self.assertEqual(msg, section.message) with patch.object(processors, 'runtime', new_callable=PropertyMock) as mock_runtime: mock_runtime.testbed = testbed mock_runtime.runinfo = AttrDict() mock_runtime.runinfo.runinfo_dir = runinfo_dir with self.assertRaises(AEtestPassxSignal) as cm: processors.genie_telemetry_processor(clean_up) self.assertEqual(cm.exception.reason, msg) fname = os.path.join(runinfo_dir, 'telemetry.yaml') self.assertTrue(os.path.isfile(fname)) with open(fname, 'r') as tempfile: content = yaml.safe_load(tempfile) expected = { 'common cleanup': { 'genie.telemetry.tests.scripts.mockplugin': { 'P1': { 'status': 'Partial' } }, 'Crash Dumps Plugin': { 'P1': { 'status': 'Ok' } }, 'Traceback Check Plugin': { 'P1': { 'status': 'Ok' } } }, 'MockSection': { 'genie.telemetry.tests.scripts.mockplugin': { 'P1': { 'status': 'Partial' } }, 'Crash Dumps Plugin': { 'P1': { 'status': 'Ok' } }, 'Traceback Check Plugin': { 'P1': { 'status': 'Ok', } } } } self.assertEqual(sorted(content.keys()), sorted(expected.keys())) for key, value in expected.items(): self.assertTrue(key in content) for plugin, devices in value.items(): content_devices = content[key].get(plugin, None) self.assertIsNotNone(content_devices) self.assertEqual(devices['P1']['status'], content_devices['P1']['status'])
def __init__(self, plugins=None): self._plugins = AttrDict() self.connections = AttrDict() self._loader = ConfigLoader() self.plugins = (plugins or PluginManager)()
class test_filetransferutils(unittest.TestCase): # Instantiate tesbed and device objects tb = Testbed(name='myTestbed') device = Device(testbed=tb, name='aDevice', os='nxos') # Instantiate a filetransferutils instance for NXOS device fu_device = FileUtils.from_device(device) # Add testbed servers for authentication device.testbed.servers = AttrDict(server_name=dict(username="******", password="******", address='1.1.1.1'), ) dir_output = [ 'bootflash:/ISSUCleanGolden.system.gbin', 'bootflash:/ISSUCleanGolden.cfg', 'bootflash:/platform-sdk.cmd', 'bootflash:/virt_strg_pool_bf_vdc_1/', 'bootflash:/virtual-instance/', 'bootflash:/virtual-instance.conf', 'bootflash:/.rpmstore/', 'bootflash:/.swtam/', 'bootflash:/scripts/' ] # Mock device output raw1 = ''' copy bootflash:/virtual-instance.conf ftp://10.1.0.213//auto/tftp-ssr/virtual-instance.conf Enter username: rcpuser Password: ***** Transfer of file Completed Successfully ***** Copy complete. ''' raw2 = ''' dir 4096 Jan 25 21:00:53 2017 .rpmstore/ 4096 Jan 25 21:01:08 2017 .swtam/ 390 Jan 25 21:36:20 2017 ISSUCleanGolden.cfg 752699904 Jan 25 21:36:26 2017 ISSUCleanGolden.system.gbin 0 Jan 25 21:35:55 2017 platform-sdk.cmd 4096 Jan 25 21:01:57 2017 scripts/ 4096 Jan 25 21:02:02 2017 virt_strg_pool_bf_vdc_1/ 4096 Jan 25 21:01:21 2017 virtual-instance/ 59 Jan 25 21:01:11 2017 virtual-instance.conf Usage for bootflash:// 1150812160 bytes used 2386407424 bytes free 3537219584 bytes total ''' raw3 = ''' delete bootflash:new_file.tcl Do you want to delete "/new_file.tcl" ? (yes/no/abort) [y] ''' raw4 = ''' move bootflash:mem_leak.tcl new_file.tcl ''' raw5 = ''' show clock > ftp://10.1.7.250//auto/tftp-ssr/show_clock vrf management Enter username: rcpuser Password: ***** Transfer of file Completed Successfully ***** ''' raw6 = { 'futlinux.check_file.return_value': '', 'futlinux.deletefile.return_value': '' } raw7 = ''' copy running-config tftp://10.1.7.250//auto/tftp-ssr/test_config.py vrf management Trying to connect to tftp server...... Connection to Server Established. [ ] 0.50KB[# ] 4.50KB[## ] 8.50KB[### ] 12.50KB TFTP put operation was successful Copy complete, now saving to disk (please wait)... Copy complete. ''' raw8 = ''' copy running-config sftp://1.1.1.1//home/virl vrf management Enter username: myuser The authenticity of host '1.1.1.1 (1.1.1.1)' can't be established. ECDSA key fingerprint is SHA256:Q37/fav3nPJT5Y+7IsgST4uN0c2tyToJiDF/gp+wItA. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '1.1.1.1' (ECDSA) to the list of known hosts. Outbound-ReKey for 1.1.1.1:22 Inbound-ReKey for 1.1.1.1:22 [email protected]'s password: Connected to 1.1.1.1. sftp> put /var/tmp/vsh/R3_nx-running-config /home/virl Uploading /var/tmp/vsh/R3_nx-running-config to /home/virl/R3_nx-running-config /var/tmp/vsh/R3_nx-running-config /var/tmp/vsh/R3_nx-running-config 100% 14KB 355.1KB/s 00:00 ''' raw9 = ''' copy bootflash:/virtual-instance.conf ftp://10.1.0.213//auto/tftp-ssr/virtual-instance.conf Enter username: rcpuser ftp: connect: No route to host ***** Transfer of file aborted, server not connected ***** Error during copy ***** Transfer of file aborted ***** ''' outputs = {} outputs['copy bootflash:/virtual-instance.conf ' 'ftp://10.1.0.213//auto/tftp-ssr/virtual-instance.conf'] = raw1 outputs['dir'] = raw2 outputs['delete bootflash:new_file.tcl'] = raw3 outputs['move bootflash:mem_leak.tcl new_file.tcl'] = raw4 outputs[ 'show clock > ftp://1.1.1.1//auto/tftp-ssr/show_clock vrf management'] = raw5 outputs[ 'copy running-config tftp://10.1.7.250//auto/tftp-ssr/test_config.py vrf management'] = raw7 outputs[ 'copy running-config sftp://[email protected]//home/virl vrf management'] = raw8 outputs['copy bootflash:/virtual-instance.conf ' 'ftp://10.1.0.214//auto/tftp-ssr/virtual-instance.conf'] = raw9 def mapper(self, key, timeout=None, reply=None, prompt_recovery=False): return self.outputs[key] def is_valid_ip_mapper(self, ip, device=None, vrf=None, cache_ip=None): return ip != '2.2.2.2' def test_copyfile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles self.fu_device.copyfile( source='bootflash:/virtual-instance.conf', destination='ftp://10.1.0.213//auto/tftp-ssr/virtual-instance.conf', timeout_seconds='300', device=self.device) def test_copyfile_exception(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles with self.assertRaises(SubCommandFailure): self.fu_device.copyfile( source='bootflash:/virtual-instance.conf', destination= 'ftp://10.1.0.214//auto/tftp-ssr/virtual-instance.conf', timeout_seconds='300', device=self.device) def test_copyfile_sftp(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles self.fu_device.copyfile(source='running-config', destination='sftp://1.1.1.1//home/virl', vrf='management', device=self.device) def test_validate_and_update_url(self): self.fu_device.is_valid_ip = Mock() self.fu_device.is_valid_ip.side_effect = self.is_valid_ip_mapper # set multiple ip for the server self.device.testbed.servers.server_name['address'] = [ '2.2.2.2', '1.1.1.1' ] not_reachable_url = self.fu_device.validate_and_update_url( 'sftp://2.2.2.2//home/virl', device=self.device) reachable_url = self.fu_device.validate_and_update_url( 'sftp://1.1.1.1//home/virl', device=self.device) servername_url = self.fu_device.validate_and_update_url( 'sftp://server_name//home/virl', device=self.device) self.assertEqual(not_reachable_url, 'sftp://[email protected]//home/virl') self.assertEqual(reachable_url, 'sftp://[email protected]//home/virl') self.assertEqual(servername_url, 'sftp://[email protected]//home/virl') self.device.testbed.servers.server_name['address'] = '1.1.1.1' def test_dir(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper directory_output = self.fu_device.dir(target='bootflash:', timeout_seconds=300, device=self.device) self.assertEqual(sorted(directory_output), sorted(self.dir_output)) def test_stat(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper file_details = self.fu_device.stat( target='bootflash:virtual-instance.conf', timeout_seconds=300, device=self.device) self.assertEqual(file_details['time'], '21:01:11') self.assertEqual(file_details['date'], 'Jan 25 2017') self.assertEqual(file_details['size'], '59') def test_deletefile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.deletefile(target='bootflash:new_file.tcl', timeout_seconds=300, device=self.device) def test_renamefile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.renamefile(source='bootflash:mem_leak.tcl', destination='new_file.tcl', timeout_seconds=300, device=self.device) @patch( 'genie.libs.filetransferutils.plugins.fileutils.FileUtils.validateserver', return_value=raw6) def test_validateserver(self, raw6): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.validateserver( target='ftp://1.1.1.1//auto/tftp-ssr/show_clock', timeout_seconds=300, device=self.device) def test_copyconfiguration(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.copyconfiguration( source='running-config', destination='tftp://10.1.7.250//auto/tftp-ssr/test_config.py', timeout_seconds=300, device=self.device)
class test_filetransferutils(unittest.TestCase): # Instantiate tesbed and device objects tb = Testbed(name='myTestbed') device = Device(testbed=tb, name='aDevice', os='iosxr') # Instantiate a filetransferutils instance for IOSXE device fu_device = FileUtils.from_device(device) # Add testbed servers for authentication device.testbed.servers = AttrDict( server_name = dict( username="******", password="******", address='1.1.1.1'), ) dir_output = ['disk0:/status_file', 'disk0:/clihistory', 'disk0:/cvac', 'disk0:/core', 'disk0:/envoke_log', 'disk0:/lost+found', 'disk0:/pnet_cfg.log', 'disk0:/oor_aware_process', 'disk0:/.python-history', 'disk0:/cvac.log', 'disk0:/nvgen_traces', 'disk0:/fake_config_2.tcl', 'disk0:/ztp', 'disk0:/config -> /misc/config', 'disk0:/memleak.tcl'] # Mock device output raw1 = ''' copy disk0:/memleak.tcl ftp://1.1.1.1//auto/tftp-ssr/memleak.tcl Address or name of remote host [1.1.1.1]? Destination filename [/auto/tftp-ssr/memleak.tcl]? !! 104260 bytes copied in 0.396 secs (263283 bytes/sec) ''' raw2 = ''' dir Directory of /misc/scratch 32 -rw-rw-rw- 1 824 Mar 7 06:29 cvac.log 43 -rwxr--r-- 1 0 Mar 22 08:58 fake_config_2.tcl 41 -rw-r--r-- 1 1985 Mar 12 14:35 status_file 13 -rw-r--r-- 1 1438 Mar 7 14:26 envoke_log 16 -rw-r--r-- 1 98 Mar 7 06:34 oor_aware_process 8178 drwxr-xr-x 2 4096 Mar 7 14:27 memleak.tcl 8177 drwx---r-x 2 4096 Mar 7 14:27 clihistory 15 lrwxrwxrwx 1 12 Mar 7 14:26 config -> /misc/config 12 drwxr-xr-x 2 4096 Mar 7 14:26 core 14 -rw-r--r-- 1 10429 Mar 7 14:26 pnet_cfg.log 11 drwx------ 2 16384 Mar 7 14:26 lost+found 8179 drwxr-xr-x 8 4096 Mar 7 07:01 ztp 42 -rw------- 1 0 Mar 20 11:08 .python-history 16354 drwxr-xr-x 2 4096 Mar 7 07:22 nvgen_traces 16353 drwxrwxrwx 3 4096 Mar 7 14:29 cvac 1012660 kbytes total (938376 kbytes free) ''' raw3 = ''' delete disk0:fake_config_2.tcl Delete disk0:fake_config_2.tcl[confirm] ''' raw4 = ''' show clock | redirect ftp://1.1.1.1//auto/tftp-ssr/show_clock Writing /auto/tftp-ssr/show_clock ''' raw5 = {'futlinux.check_file.return_value': '', 'futlinux.deletefile.return_value': ''} raw6 = ''' copy running-config ftp://10.1.6.242//auto/tftp-ssr/fake_config_2.tcl Host name or IP address (control-c to abort): [10.1.6.242;default]? Destination username: []?rcpuser Destination password: Destination file name (control-c to abort): [/auto/tftp-ssr/fake_config_2.tcl]? Building configuration. 349 lines built in 1 second [OK] ''' raw7 = ''' sftp running-config [email protected]:/home/virl vrf management Thu Oct 10 15:45:18.989 UTC Connecting to 172.16.1.250... Password: /misc/disk1/running-config Overwrite /home/virl/running-config on host 172.16.1.250, continu? [ yes/no]: yes Transferred 11332 Bytes 11332 bytes copied in 0 sec (251822)bytes/sec ''' outputs = {} outputs['copy disk0:/fake_config_2.tcl ' 'ftp://1.1.1.1//auto/tftp-ssr/fake_config_2.tcl'] = raw1 outputs['dir'] = raw2 outputs['delete disk0:fake_config.tcl'] = raw3 outputs['show clock | redirect ftp://1.1.1.1//auto/tftp-ssr/show_clock'] = \ raw4 outputs['copy running-config ftp://10.1.6.242//auto/tftp-ssr/fake_config_2.tcl'] = \ raw6 outputs['sftp running-config [email protected]:/home/virl'] = raw7 def mapper(self, key, timeout=None, reply= None, prompt_recovery=False): return self.outputs[key] def test_copyfile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles self.fu_device.copyfile(source='disk0:/fake_config_2.tcl', destination='ftp://1.1.1.1//auto/tftp-ssr/fake_config_2.tcl', timeout_seconds='300', device=self.device) def test_copyfile_sftp(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper # Call copyfiles self.fu_device.copyfile(source='running-config', destination='sftp://1.1.1.1//home/virl', device=self.device) def test_dir(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper directory_output = self.fu_device.dir(target='disk0:', timeout_seconds=300, device=self.device) self.assertEqual(sorted(directory_output), sorted(self.dir_output)) def test_stat(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper file_details = self.fu_device.stat(target='disk0:memleak.tcl', timeout_seconds=300, device=self.device) self.assertEqual(file_details['index'], '8178') self.assertEqual(file_details['date'], 'Mar 7 14:27') self.assertEqual(file_details['permission'], 'drwxr-xr-x') self.assertEqual(file_details['size'], '4096') def test_deletefile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.deletefile(target='disk0:fake_config.tcl', timeout_seconds=300, device=self.device) def test_renamefile(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper with self.assertRaisesRegex( NotImplementedError, "The fileutils module genie.libs." "filetransferutils.plugins.iosxr.fileutils does not implement " "renamefile."): self.fu_device.renamefile(source='disk0:fake_config.tcl', destination='memleak.tcl', timeout_seconds=300, device=self.device) @patch('genie.libs.filetransferutils.plugins.fileutils.FileUtils.validateserver', return_value=raw5) def test_validateserver(self, raw5): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.validateserver( target='ftp://1.1.1.1//auto/tftp-ssr/show_clock', timeout_seconds=300, device=self.device) def test_copyconfiguration(self): self.device.execute = Mock() self.device.execute.side_effect = self.mapper self.fu_device.copyconfiguration(source='running-config', destination='ftp://10.1.6.242//auto/tftp-ssr/fake_config_2.tcl', timeout_seconds=300, device=self.device)