def connect(host, port, user, password): conn = manager.connect(host=host, port=port, username=user, password=password, timeout=60, device_params={'name': 'junos'}, hostkey_verify=False) junos_dev_handler = JunosDeviceHandler(device_params={ 'name': 'junos', 'local': False }) conn.async_mode = True rpc = new_ele('get-software-information') obj = conn.rpc(rpc) # for demo purposes, we just wait for the result while not obj.event.is_set(): logging.info('waiting for answer ...') time.sleep(.3) result = NCElement(obj.reply, junos_dev_handler.transform_reply()).remove_namespaces( obj.reply.xml) logging.info('Hostname: %s', result.findtext('.//host-name'))
def connect(host, port, user, password): conn = manager.connect(host=host, port=port, username=user, password=password, timeout=10, device_params={'name': 'junos'}, hostkey_verify=False) junos_dev_handler = JunosDeviceHandler( device_params={'name': 'junos', 'local': False}) conn.async_mode = True rpc = new_ele('get-software-information') obj = conn.rpc(rpc) # for demo purposes, we just wait for the result while not obj.event.is_set(): print('waiting for answer ...') time.sleep(.3) result = NCElement(obj.reply, junos_dev_handler.transform_reply() ).remove_namespaces(obj.reply.xml) print 'Hostname: ', result.findtext('.//host-name')
class Console(_Connection): def __init__(self, **kvargs): """ NoobDevice object constructor. :param str host: **REQUIRED** host-name or ipaddress of target device :param str user: *OPTIONAL* login user-name, uses root if not provided :param str passwd: *OPTIONAL* in console connection for device at zeroized state password is not required :param int port: *OPTIONAL* port, defaults to '23' for telnet mode and '/dev/ttyUSB0' for serial. :param int baud: *OPTIONAL* baud, default baud rate is 9600 :param str mode: *OPTIONAL* mode, mode of connection (telnet/serial) default is telnet :param int timeout: *OPTIONAL* timeout, default is 0.5 :param int attempts: *OPTIONAL* attempts, default is 10 :param str ssh_config: *OPTIONAL* The path to the SSH configuration file. This can be used to load SSH information from a configuration file. By default ~/.ssh/config is queried it will be used by SCP class. So its assumed ssh is enabled by the time we use SCP functionality. :param bool gather_facts: *OPTIONAL* Defaults to ``False``. If ``False`` and old-style fact gathering is in use then facts are not gathered on call to :meth:`open`. This argument is a no-op when new-style fact gathering is in use (the default.) :param str fact_style: *OPTIONAL* The style of fact gathering to use. Valid values are: 'new', 'old', or 'both'. The default is 'new'. The value 'both' is only present for debugging purposes. It will be removed in a future release. The value 'old' is only present to workaround bugs in new-style fact gathering. It will be removed in a future release. :param bool console_has_banner: *OPTIONAL* default is ``False``. If ``False`` then in case of a hung state, <close-session/> rpc is sent to the console. If ``True``, after sleep(5), a new-line is sent """ # ---------------------------------------- # setup instance connection/open variables # ---------------------------------------- self._tty = None self._ofacts = {} self.connected = False self._skip_logout = False self.results = dict(changed=False, failed=False, errmsg=None) # hostname is not required in serial mode connection self._hostname = kvargs.get('host') self._auth_user = kvargs.get('user', 'root') self._auth_password = kvargs.get( 'password', '') or kvargs.get( 'passwd', '') self._port = kvargs.get('port', '23') self._baud = kvargs.get('baud', '9600') self._mode = kvargs.get('mode', 'telnet') self._timeout = kvargs.get('timeout', '0.5') self._normalize = kvargs.get('normalize', False) self._norm_transform = lambda: JXML.normalize_xslt.encode('UTF-8') self.transform = self._norm_transform # self.timeout needed by PyEZ utils #self.timeout = self._timeout self._attempts = kvargs.get('attempts', 10) self._gather_facts = kvargs.get('gather_facts', False) self._fact_style = kvargs.get('fact_style', 'new') if self._fact_style != 'new': warnings.warn('fact-style %s will be removed in a future release.' % (self._fact_style), RuntimeWarning) self.console_has_banner = kvargs.get('console_has_banner', False) self.rpc = _RpcMetaExec(self) self._ssh_config = kvargs.get('ssh_config') self._manages = [] self.junos_dev_handler = JunosDeviceHandler(device_params= {'name': 'junos', 'local': False}) if self._fact_style == 'old': self.facts = self.ofacts else: self.facts = _FactCache(self) @property def timeout(self): """ :returns: current console connection timeout value (int) in seconds. """ return self._timeout @timeout.setter def timeout(self, value): """ Used to change the console connection timeout value (default=0.5 sec). :param int value: New timeout value in seconds """ self._timeout = value def open(self, *vargs, **kvargs): """ Opens a connection to the device using existing login/auth information. :param bool gather_facts: If set to ``True``/``False`` will override the device instance value for only this open process """ # --------------------------------------------------------------- # validate device hostname or IP address # --------------------------------------------------------------- if self._mode.upper() == 'TELNET' and self._hostname is None: self.results['failed'] = True self.results[ 'errmsg'] = 'ERROR: Device hostname/IP not specified !!!' return self.results # -------------------- # login to the CONSOLE # -------------------- try: self._tty_login() except RuntimeError as err: logger.error("ERROR: {0}:{1}\n".format('login', str(err))) logger.error( "\nComplete traceback message: {0}".format( traceback.format_exc())) raise err except Exception as ex: logger.error("Exception occurred: {0}:{1}\n".format('login', str(ex))) raise ex self.connected = True gather_facts = kvargs.get('gather_facts', self._gather_facts) if gather_facts is True: logger.info('facts: retrieving device facts...') self.facts_refresh() self.results['facts'] = self.facts return self def close(self, skip_logout=False): """ Closes the connection to the device. """ if skip_logout is False and self.connected is True: try: self._tty_logout() except Exception as err: logger.error("ERROR {0}:{1}\n".format('logout', str(err))) raise err self.connected = False elif self.connected is True: try: self._tty._tty_close() except Exception as err: logger.error("ERROR {0}:{1}\n".format('close', str(err))) logger.error( "\nComplete traceback message: {0}".format( traceback.format_exc())) raise err self.connected = False def _rpc_reply(self, rpc_cmd_e): encode = None if sys.version < '3' else 'unicode' rpc_cmd = etree.tostring(rpc_cmd_e, encoding=encode) if \ isinstance(rpc_cmd_e, etree._Element) else rpc_cmd_e reply = self._tty.nc.rpc(rpc_cmd) rpc_rsp_e = NCElement(reply, self.junos_dev_handler.transform_reply())._NCElement__doc return rpc_rsp_e # ------------------------------------------------------------------------- # LOGIN/LOGOUT # ------------------------------------------------------------------------- def _tty_login(self): tty_args = dict() tty_args['user'] = self._auth_user tty_args['passwd'] = self._auth_password tty_args['timeout'] = float(self._timeout) tty_args['attempts'] = int(self._attempts) tty_args['baud'] = self._baud if self._mode.upper() == 'TELNET': tty_args['host'] = self._hostname tty_args['port'] = self._port tty_args['console_has_banner'] = self.console_has_banner self.console = ('telnet', self._hostname, self.port) self._tty = Telnet(**tty_args) elif self._mode.upper() == 'SERIAL': tty_args['port'] = self._port self.console = ('serial', self._port) self._tty = Serial(**tty_args) else: logger.error('Mode should be either telnet or serial') raise AttributeError('Mode to be telnet/serial') self._tty.login() def _tty_logout(self): self._tty.logout() def zeroize(self): """ perform device ZEROIZE actions """ logger.info("zeroize : ZEROIZE device, rebooting") self._tty.nc.zeroize() self._skip_logout = True self.results['changed'] = True # ----------------------------------------------------------------------- # Context Manager # ----------------------------------------------------------------------- def __enter__(self): self._conn = self.open() return self def __exit__(self, exc_type, exc_val, exc_tb): if self.connected: self.close()
class Console(_Connection): def __init__(self, **kvargs): """ NoobDevice object constructor. :param str host: **REQUIRED** host-name or ipaddress of target device :param str user: *OPTIONAL* login user-name, uses root if not provided :param str passwd: *OPTIONAL* in console connection for device at zeroized state password is not required :param int port: *OPTIONAL* port, defaults to '23' for telnet mode and '/dev/ttyUSB0' for serial. :param int baud: *OPTIONAL* baud, default baud rate is 9600 :param str mode: *OPTIONAL* mode, mode of connection (telnet/serial) default is telnet :param int timeout: *OPTIONAL* timeout, default is 0.5 :param int attempts: *OPTIONAL* attempts, default is 10 :param str ssh_config: *OPTIONAL* The path to the SSH configuration file. This can be used to load SSH information from a configuration file. By default ~/.ssh/config is queried it will be used by SCP class. So its assumed ssh is enabled by the time we use SCP functionality. :param bool gather_facts: *OPTIONAL* Defaults to ``False``. If ``False`` and old-style fact gathering is in use then facts are not gathered on call to :meth:`open`. This argument is a no-op when new-style fact gathering is in use (the default.) :param str fact_style: *OPTIONAL* The style of fact gathering to use. Valid values are: 'new', 'old', or 'both'. The default is 'new'. The value 'both' is only present for debugging purposes. It will be removed in a future release. The value 'old' is only present to workaround bugs in new-style fact gathering. It will be removed in a future release. :param bool console_has_banner: *OPTIONAL* default is ``False``. If ``False`` then in case of a hung state, <close-session/> rpc is sent to the console. If ``True``, after sleep(5), a new-line is sent """ # ---------------------------------------- # setup instance connection/open variables # ---------------------------------------- self._tty = None self._ofacts = {} self.connected = False self._skip_logout = False self.results = dict(changed=False, failed=False, errmsg=None) # hostname is not required in serial mode connection self._hostname = kvargs.get('host') self._auth_user = kvargs.get('user', 'root') self._auth_password = kvargs.get('password', '') or kvargs.get( 'passwd', '') self._port = kvargs.get('port', '23') self._baud = kvargs.get('baud', '9600') self._mode = kvargs.get('mode', 'telnet') self._timeout = kvargs.get('timeout', '0.5') self._normalize = kvargs.get('normalize', False) self._norm_transform = lambda: JXML.normalize_xslt.encode('UTF-8') self.transform = self._norm_transform # self.timeout needed by PyEZ utils #self.timeout = self._timeout self._attempts = kvargs.get('attempts', 10) self._gather_facts = kvargs.get('gather_facts', False) self._fact_style = kvargs.get('fact_style', 'new') if self._fact_style != 'new': warnings.warn( 'fact-style %s will be removed in a future release.' % (self._fact_style), RuntimeWarning) self.console_has_banner = kvargs.get('console_has_banner', False) self.rpc = _RpcMetaExec(self) self._ssh_config = kvargs.get('ssh_config') self._manages = [] self.junos_dev_handler = JunosDeviceHandler(device_params={ 'name': 'junos', 'local': False }) if self._fact_style == 'old': self.facts = self.ofacts else: self.facts = _FactCache(self) @property def timeout(self): """ :returns: current console connection timeout value (int) in seconds. """ return self._timeout @timeout.setter def timeout(self, value): """ Used to change the console connection timeout value (default=0.5 sec). :param int value: New timeout value in seconds """ self._timeout = value def open(self, *vargs, **kvargs): """ Opens a connection to the device using existing login/auth information. :param bool gather_facts: If set to ``True``/``False`` will override the device instance value for only this open process """ # --------------------------------------------------------------- # validate device hostname or IP address # --------------------------------------------------------------- if self._mode.upper() == 'TELNET' and self._hostname is None: self.results['failed'] = True self.results[ 'errmsg'] = 'ERROR: Device hostname/IP not specified !!!' return self.results # -------------------- # login to the CONSOLE # -------------------- try: self._tty_login() except RuntimeError as err: logger.error("ERROR: {0}:{1}\n".format('login', str(err))) logger.error("\nComplete traceback message: {0}".format( traceback.format_exc())) raise err except Exception as ex: logger.error("Exception occurred: {0}:{1}\n".format( 'login', str(ex))) raise ex self.connected = True gather_facts = kvargs.get('gather_facts', self._gather_facts) if gather_facts is True: logger.info('facts: retrieving device facts...') self.facts_refresh() self.results['facts'] = self.facts return self def close(self, skip_logout=False): """ Closes the connection to the device. """ if skip_logout is False and self.connected is True: try: self._tty_logout() except Exception as err: logger.error("ERROR {0}:{1}\n".format('logout', str(err))) raise err self.connected = False elif self.connected is True: try: self._tty._tty_close() except Exception as err: logger.error("ERROR {0}:{1}\n".format('close', str(err))) logger.error("\nComplete traceback message: {0}".format( traceback.format_exc())) raise err self.connected = False def _rpc_reply(self, rpc_cmd_e): encode = None if sys.version < '3' else 'unicode' rpc_cmd = etree.tostring(rpc_cmd_e, encoding=encode) if \ isinstance(rpc_cmd_e, etree._Element) else rpc_cmd_e reply = self._tty.nc.rpc(rpc_cmd) rpc_rsp_e = NCElement( reply, self.junos_dev_handler.transform_reply())._NCElement__doc return rpc_rsp_e # ------------------------------------------------------------------------- # LOGIN/LOGOUT # ------------------------------------------------------------------------- def _tty_login(self): tty_args = dict() tty_args['user'] = self._auth_user tty_args['passwd'] = self._auth_password tty_args['timeout'] = float(self._timeout) tty_args['attempts'] = int(self._attempts) tty_args['baud'] = self._baud if self._mode.upper() == 'TELNET': tty_args['host'] = self._hostname tty_args['port'] = self._port tty_args['console_has_banner'] = self.console_has_banner self.console = ('telnet', self._hostname, self.port) self._tty = Telnet(**tty_args) elif self._mode.upper() == 'SERIAL': tty_args['port'] = self._port self.console = ('serial', self._port) self._tty = Serial(**tty_args) else: logger.error('Mode should be either telnet or serial') raise AttributeError('Mode to be telnet/serial') self._tty.login() def _tty_logout(self): self._tty.logout() def zeroize(self): """ perform device ZEROIZE actions """ logger.info("zeroize : ZEROIZE device, rebooting") self._tty.nc.zeroize() self._skip_logout = True self.results['changed'] = True # ----------------------------------------------------------------------- # Context Manager # ----------------------------------------------------------------------- def __enter__(self): self._conn = self.open() return self def __exit__(self, exc_type, exc_val, exc_tb): if self.connected: self.close()
class TestFactoryTable(unittest.TestCase): @patch('ncclient.manager.connect') def setUp(self, mock_connect): mock_connect.side_effect = self._mock_manager self.dev = Device(host='1.1.1.1', user='******', password='******', gather_facts=False) self.device_handler = JunosDeviceHandler(device_params={ 'name': 'junos', 'local': False }) self.dev.open() self.table = Table(dev=self.dev) self.ppt = PhyPortTable(self.dev) def test_config_constructor(self): self.assertTrue(isinstance(self.table.D, Device)) def test_table_hostname(self): self.assertEqual(self.table.hostname, '1.1.1.1') def test_table_is_container(self): self.assertTrue(self.table.is_container) def test_table_repr_xml_none(self): self.assertEqual(repr(self.table), 'Table:1.1.1.1 - Table empty') def test_table_view_setter_ValueError(self): try: self.table.view = 'test' except Exception as ex: self.assertEqual(ex.__class__, ValueError) @patch('jnpr.junos.Device.execute') def test_keys_RuntimeError(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.ppt.ITEM_NAME_XPATH = 1 self.assertRaises(RuntimeError, self.ppt.keys) @patch('jnpr.junos.Device.execute') def test_keys__keys_composite(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.ppt.ITEM_NAME_XPATH = ['name', 'missing', 'mtu'] self.assertEqual(self.ppt.keys(), [('ge-0/0/0', None, '1514'), ('ge-0/0/1', None, '1514')]) @patch('jnpr.junos.Device.execute') def test_keys__keys_pipe(self, mock_execute): from jnpr.junos.op.lldp import LLDPNeighborTable mock_execute.side_effect = self._mock_manager self.lldp = LLDPNeighborTable(self.dev) self.lldp.get() self.assertEqual(self.lldp.keys(), ['et-0/0/48', 'et-0/0/49', 'xe-0/0/13']) @patch('jnpr.junos.Device.execute') def test_table_repr_xml_not_none(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.table.xml = self.ppt.xml self.table.ITEM_XPATH = self.ppt.ITEM_XPATH self.assertEqual(repr(self.table), 'Table:1.1.1.1: 2 items') @patch('jnpr.junos.Device.execute') def test_table_get_keys_values(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.assertEqual(self.ppt.keys(), ['ge-0/0/0', 'ge-0/0/1']) self.assertEqual(len(self.ppt.values()), 2) self.ppt.view = None self.assertEqual(len(self.ppt.values()), 2) @patch('jnpr.junos.Device.execute') def test_table__getitem__(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.assertEqual(self.ppt[0].ITEM_NAME_XPATH, 'name') @patch('jnpr.junos.Device.execute') def test_table__getitem__slice(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.assertEqual(self.ppt[:1][0].__class__.__name__, 'PhyPortView') @patch('jnpr.junos.Device.execute') def test_table__getitem__tuple(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.assertEqual(self.ppt[('ge-0/0/0', )], None) @patch('jnpr.junos.Device.execute') def test_table__contains__(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') self.assertTrue('ge-0/0/0' in self.ppt) @patch('jnpr.junos.Device.execute') def test_table_items(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get('ge-0/0/0') print(self.ppt.items()) self.assertEqual(len(self.ppt.items()[1][1]), 8) def test_table_get_return_none(self): self.assertEqual(self.table.get('ge-0/0/0'), None) def test_table_get_RuntimeError(self): self.assertRaises(RuntimeError, self.table._keys) @patch('jnpr.junos.Device.execute') @patch(builtin_string + '.open') def test_table_savexml(self, mock_file, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.xml = etree.XML('<root><a>test</a></root>') self.ppt.savexml('/vasr/tmssp/foo.xml', hostname=True, append='test') mock_file.assert_called_once_with('/vasr/tmssp/foo_1.1.1.1_test.xml', 'wb') self.ppt.savexml('/vasr/tmssp/foo.xml', hostname=True, timestamp=True) self.assertEqual(mock_file.call_count, 2) def _read_file(self, fname): from ncclient.xml_ import NCElement fpath = os.path.join(os.path.dirname(__file__), 'rpc-reply', fname) foo = open(fpath).read() rpc_reply = NCElement(foo, self.device_handler.transform_reply())\ ._NCElement__doc[0] return rpc_reply def _mock_manager(self, *args, **kwargs): if kwargs: if args and ('normalize' in kwargs or 'filter_xml' in kwargs): return self._read_file(args[0].tag + '.xml') device_params = kwargs['device_params'] device_handler = make_device_handler(device_params) session = SSHSession(device_handler) return Manager(session, device_handler) if args: return self._read_file(args[0].tag + '.xml')
class Console(_Connection): def __init__(self, **kvargs): """ NoobDevice object constructor. :param str host: **REQUIRED** host-name or ipaddress of target device :param str user: *OPTIONAL* login user-name, uses root if not provided :param str passwd: *OPTIONAL* in console connection for device at zeroized state password is not required :param int port: *OPTIONAL* port, defaults to '23' for telnet mode and '/dev/ttyUSB0' for serial. :param int baud: *OPTIONAL* baud, default baud rate is 9600 :param str mode: *OPTIONAL* mode, mode of connection (telnet/serial) default is telnet :param int timeout: *OPTIONAL* timeout, default is 0.5 :param int attempts: *OPTIONAL* attempts, default is 10 :param str ssh_config: *OPTIONAL* The path to the SSH configuration file. This can be used to load SSH information from a configuration file. By default ~/.ssh/config is queried it will be used by SCP class. So its assumed ssh is enabled by the time we use SCP functionality. :param bool gather_facts: *OPTIONAL* Defaults to ``False``. If ``False`` and old-style fact gathering is in use then facts are not gathered on call to :meth:`open`. This argument is a no-op when new-style fact gathering is in use (the default.) :param str fact_style: *OPTIONAL* The style of fact gathering to use. Valid values are: 'new', 'old', or 'both'. The default is 'new'. The value 'both' is only present for debugging purposes. It will be removed in a future release. The value 'old' is only present to workaround bugs in new-style fact gathering. It will be removed in a future release. :param bool console_has_banner: *OPTIONAL* default is ``False``. If ``False`` then in case of a hung state, <close-session/> rpc is sent to the console. If ``True``, after sleep(5), a new-line is sent """ # ---------------------------------------- # setup instance connection/open variables # ---------------------------------------- self._tty = None self._ofacts = {} self.connected = False self._skip_logout = False self.results = dict(changed=False, failed=False, errmsg=None) # hostname is not required in serial mode connection self._hostname = kvargs.get("host") self._auth_user = kvargs.get("user", "root") self._conf_auth_user = None self._conf_ssh_private_key_file = None self._auth_password = kvargs.get("password", "") or kvargs.get( "passwd", "") self.cs_user = kvargs.get("cs_user") self.cs_passwd = kvargs.get("cs_passwd") self._port = kvargs.get("port", "22" if self.cs_user else "23") self._mode = kvargs.get("mode", None if self.cs_user else "telnet") self._baud = kvargs.get("baud", "9600") if self._hostname: self._ssh_config = kvargs.get("ssh_config") self._sshconf_lkup() self._ssh_private_key_file = (kvargs.get("ssh_private_key_file") or self._conf_ssh_private_key_file) self._timeout = kvargs.get("timeout", "0.5") self._normalize = kvargs.get("normalize", False) self._attempts = kvargs.get("attempts", 10) self._gather_facts = kvargs.get("gather_facts", False) self._fact_style = kvargs.get("fact_style", "new") self._huge_tree = kvargs.get("huge_tree", False) if self._fact_style != "new": warnings.warn( "fact-style %s will be removed in " "a future release." % (self._fact_style), RuntimeWarning, ) self.console_has_banner = kvargs.get("console_has_banner", False) self.rpc = _RpcMetaExec(self) self._manages = [] self.junos_dev_handler = JunosDeviceHandler(device_params={ "name": "junos", "local": False }) self._conn = None self._j2ldr = _Jinja2ldr if self._fact_style == "old": self.facts = self.ofacts else: self.facts = _FactCache(self) @property def timeout(self): """ :returns: current console connection timeout value (int) in seconds. """ return self._timeout @timeout.setter def timeout(self, value): """ Used to change the console connection timeout value (default=0.5 sec). :param int value: New timeout value in seconds """ self._timeout = value @property def transform(self): """ :returns: the current RPC XML Transformation. """ return self.junos_dev_handler.transform_reply @transform.setter def transform(self, func): """ Used to change the RPC XML Transformation. :param lambda value: New transform lambda """ self.junos_dev_handler.transform_reply = func def open(self, *vargs, **kvargs): """ Opens a connection to the device using existing login/auth information. :param bool gather_facts: If set to ``True``/``False`` will override the device instance value for only this open process """ # --------------------------------------------------------------- # validate device hostname or IP address # --------------------------------------------------------------- if ((self._mode and self._mode.upper() == "TELNET") or self.cs_user is not None) and self._hostname is None: self.results["failed"] = True self.results[ "errmsg"] = "ERROR: Device hostname/IP not specified !!!" return self.results # --------------------------------------------------------------- # validate console server and password. Password-less connection # is not supported # --------------------------------------------------------------- if self.cs_user is not None and self.cs_passwd is None: self.results["failed"] = True self.results["errmsg"] = ( "ERROR: Console SSH, Password-less connection is " "not supported !!!") logger.error(self.results["errmsg"]) return self.results # -------------------- # login to the CONSOLE # -------------------- try: self._tty_login() except RuntimeError as err: logger.error("ERROR: {}:{}\n".format("login", str(err))) logger.error("\nComplete traceback message: {}".format( traceback.format_exc())) raise err except Exception as ex: logger.error("Exception occurred: {}:{}\n".format( "login", str(ex))) raise ex self.connected = True self._nc_transform = self.transform self._norm_transform = lambda: JXML.normalize_xslt.encode("UTF-8") # normalize argument to open() overrides normalize argument value # to __init__(). Save value to self._normalize where it is used by # normalizeDecorator() self._normalize = kvargs.get("normalize", self._normalize) if self._normalize is True: self.transform = self._norm_transform gather_facts = kvargs.get("gather_facts", self._gather_facts) if gather_facts is True: logger.info("facts: retrieving device facts...") self.facts_refresh() self.results["facts"] = self.facts self._conn = self._tty return self def close(self, skip_logout=False): """ Closes the connection to the device. """ if skip_logout is False and self.connected is True: try: self._tty_logout() except socket.error as err: # if err contains "Connection reset by peer" connection to the # device got closed if "Connection reset by peer" not in str(err): raise err except EOFError as err: if "telnet connection closed" not in str(err): raise err except Exception as err: logger.error("ERROR {}:{}\n".format("logout", str(err))) raise err self.connected = False elif self.connected is True: try: self._tty._tty_close() except Exception as err: logger.error("ERROR {}:{}\n".format("close", str(err))) logger.error("\nComplete traceback message: {}".format( traceback.format_exc())) raise err self.connected = False @ignoreWarnDecorator def _rpc_reply(self, rpc_cmd_e, *args, **kwargs): encode = None if sys.version < "3" else "unicode" rpc_cmd = (etree.tostring(rpc_cmd_e, encoding=encode) if isinstance( rpc_cmd_e, etree._Element) else rpc_cmd_e) reply = self._tty.nc.rpc(rpc_cmd) rpc_rsp_e = NCElement(reply, self.junos_dev_handler.transform_reply(), self._huge_tree)._NCElement__doc return rpc_rsp_e # ------------------------------------------------------------------------- # LOGIN/LOGOUT # ------------------------------------------------------------------------- def _tty_login(self): tty_args = dict() tty_args["user"] = self._auth_user tty_args["passwd"] = self._auth_password tty_args["timeout"] = float(self._timeout) tty_args["attempts"] = int(self._attempts) tty_args["baud"] = self._baud tty_args["huge_tree"] = self._huge_tree if self._mode and self._mode.upper() == "TELNET": tty_args["host"] = self._hostname tty_args["port"] = self._port tty_args["console_has_banner"] = self.console_has_banner self.console = ("telnet", self._hostname, self.port) self._tty = Telnet(**tty_args) elif self.cs_user is not None: tty_args["cs_user"] = self.cs_user tty_args["cs_passwd"] = self.cs_passwd tty_args["host"] = self._hostname tty_args["port"] = self._port tty_args["console_has_banner"] = self.console_has_banner tty_args["ssh_private_key_file"] = self._ssh_private_key_file self.console = ("ssh", self._hostname, self.port) self._tty = SSH(**tty_args) elif self._mode.upper() == "SERIAL": tty_args["port"] = self._port self.console = ("serial", self._port) self._tty = Serial(**tty_args) else: logger.error("Mode should be either telnet or serial") raise AttributeError("Mode to be telnet/serial") self._tty.login() def _tty_logout(self): self._tty.logout() def zeroize(self): """ perform device ZEROIZE actions """ logger.info("zeroize : ZEROIZE device, rebooting") self._tty.nc.zeroize() self._skip_logout = True self.results["changed"] = True # ----------------------------------------------------------------------- # Context Manager # ----------------------------------------------------------------------- def __enter__(self): self.open() return self def __exit__(self, exc_type, exc_val, exc_tb): if self.connected: self.close()
class TestFactoryTable(unittest.TestCase): @patch("ncclient.manager.connect") def setUp(self, mock_connect): mock_connect.side_effect = self._mock_manager self.dev = Device(host="1.1.1.1", user="******", password="******", gather_facts=False) self.device_handler = JunosDeviceHandler(device_params={ "name": "junos", "local": False }) self.dev.open() self.table = Table(dev=self.dev) self.ppt = PhyPortTable(self.dev) def test_config_constructor(self): self.assertTrue(isinstance(self.table.D, Device)) def test_table_hostname(self): self.assertEqual(self.table.hostname, "1.1.1.1") def test_table_is_container(self): self.assertTrue(self.table.is_container) def test_table_repr_xml_none(self): self.assertEqual(repr(self.table), "Table:1.1.1.1 - Table empty") def test_table_view_setter_ValueError(self): try: self.table.view = "test" except Exception as ex: self.assertEqual(ex.__class__, ValueError) @patch("jnpr.junos.Device.execute") def test_keys_RuntimeError(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.ppt.ITEM_NAME_XPATH = 1 self.assertRaises(RuntimeError, self.ppt.keys) @patch("jnpr.junos.Device.execute") def test_keys__keys_composite(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.ppt.ITEM_NAME_XPATH = ["name", "missing", "mtu"] self.assertEqual(self.ppt.keys(), [("ge-0/0/0", None, "1514"), ("ge-0/0/1", None, "1514")]) @patch("jnpr.junos.Device.execute") def test_keys__keys_pipe(self, mock_execute): from jnpr.junos.op.lldp import LLDPNeighborTable mock_execute.side_effect = self._mock_manager self.lldp = LLDPNeighborTable(self.dev) self.lldp.get() self.assertEqual(self.lldp.keys(), ["et-0/0/48", "et-0/0/49", "xe-0/0/13"]) @patch("jnpr.junos.Device.execute") def test_table_repr_xml_not_none(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.table.xml = self.ppt.xml self.table.ITEM_XPATH = self.ppt.ITEM_XPATH self.assertEqual(repr(self.table), "Table:1.1.1.1: 2 items") @patch("jnpr.junos.Device.execute") def test_table_get_keys_values(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.assertEqual(self.ppt.keys(), ["ge-0/0/0", "ge-0/0/1"]) self.assertEqual(len(self.ppt.values()), 2) self.ppt.view = None self.assertEqual(len(self.ppt.values()), 2) @patch("jnpr.junos.Device.execute") def test_table__getitem__(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.assertEqual(self.ppt[0].ITEM_NAME_XPATH, "name") @patch("jnpr.junos.Device.execute") def test_table__getitem__slice(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.assertEqual(self.ppt[:1][0].__class__.__name__, "PhyPortView") @patch("jnpr.junos.Device.execute") def test_table__getitem__tuple(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.assertEqual(self.ppt[("ge-0/0/0", )], None) @patch("jnpr.junos.Device.execute") def test_table__contains__(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.assertTrue("ge-0/0/0" in self.ppt) @patch("jnpr.junos.Device.execute") def test_table_items(self, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.get("ge-0/0/0") self.assertEqual(len(self.ppt.items()[1][1]), 8) def test_table_get_return_none(self): self.assertEqual(self.table.get("ge-0/0/0"), None) def test_table_get_RuntimeError(self): self.assertRaises(RuntimeError, self.table._keys) @patch("jnpr.junos.Device.execute") @patch(builtin_string + ".open") def test_table_savexml(self, mock_file, mock_execute): mock_execute.side_effect = self._mock_manager self.ppt.xml = etree.XML("<root><a>test</a></root>") self.ppt.savexml("/vasr/tmssp/foo.xml", hostname=True, append="test") mock_file.assert_called_once_with("/vasr/tmssp/foo_1.1.1.1_test.xml", "wb") self.ppt.savexml("/vasr/tmssp/foo.xml", hostname=True, timestamp=True) self.assertEqual(mock_file.call_count, 2) def _read_file(self, fname): from ncclient.xml_ import NCElement fpath = os.path.join(os.path.dirname(__file__), "rpc-reply", fname) foo = open(fpath).read() rpc_reply = NCElement( foo, self.device_handler.transform_reply())._NCElement__doc[0] return rpc_reply def _mock_manager(self, *args, **kwargs): if kwargs: if args and ("normalize" in kwargs or "filter_xml" in kwargs): return self._read_file(args[0].tag + ".xml") device_params = kwargs["device_params"] device_handler = make_device_handler(device_params) session = SSHSession(device_handler) return Manager(session, device_handler) if args: return self._read_file(args[0].tag + ".xml")