def commit(self, **kvargs): """ Commit a configuration. :param str comment: If provide logs this comment with the commit. :param int confirm: If provided activates confirm safeguard with provided value as timeout (minutes). :returns: * ``True`` when successful :raises CommitError: When errors detected in candidate configuration. You can use the Exception variable (XML) to identify the specific problems """ rpc_args = {} # if a comment is provided, then include that in the RPC comment = kvargs.get('comment') if comment: rpc_args['log'] = comment # if confirm is provided, then setup the RPC args # so that Junos will either use the default confirm # timeout (confirm=True) or a specific timeout # (confirm=<minutes>) confirm = kvargs.get('confirm') if confirm: rpc_args['confirmed'] = True confirm_val = str(confirm) if 'True' != confirm_val: rpc_args['confirm-timeout'] = confirm_val # dbl-splat the rpc_args since we want to pass key/value to metaexec # if there is a commit/check error, this will raise an execption try: self.rpc.commit_configuration(**rpc_args) except RpcError as err: # jnpr.junos exception if err.rsp.find('ok') is not None: # this means there are warnings, but no errors return True else: raise CommitError(cmd=err.cmd, rsp=err.rsp) except ConnectTimeoutError as err: # err is a TimeoutExpiredError from ncclient, # which has no such attribute as xml. raise except Exception as err: # so the ncclient gives us something I don't want. I'm going to # convert it and re-raise the commit error JXML.remove_namespaces(err.xml) raise CommitError(rsp=err.xml) return True
def commit(self, **kvargs): """ Commit a configuration. :param str comment: If provide logs this comment with the commit. :param int confirm: If provided activates confirm safeguard with provided value as timeout (minutes). :returns: * ``True`` when successful :raises CommitError: When errors detected in candidate configuraiton. You can use the Exception variable (XML) to identify the specific problems """ rpc_args = {} # if a comment is provided, then include that in the RPC comment = kvargs.get('comment') if comment: rpc_args['log'] = comment # if confirm is provided, then setup the RPC args # so that Junos will either use the default confirm # timeout (confirm=True) or a specific timeout # (confirm=<minutes>) confirm = kvargs.get('confirm') if confirm: rpc_args['confirmed'] = True confirm_val = str(confirm) if 'True' != confirm_val: rpc_args['confirm-timeout'] = confirm_val # dbl-splat the rpc_args since we want to pass key/value to metaexec # if there is a commit/check error, this will raise an execption try: self.rpc.commit_configuration(**rpc_args) except RpcError as err: # jnpr.junos exception if err.rsp.find('ok') is not None: # this means there are warnings, but no errors return True else: raise CommitError(cmd=err.cmd, rsp=err.rsp) except Exception as err: # so the ncclient gives us something I don't want. I'm going to # convert it and re-raise the commit error JXML.remove_namespaces(err.xml) raise CommitError(rsp=err.xml) return True
def __init__(self, cmd=None, rsp=None, errs=None, dev=None, timeout=None, re=None): """ :cmd: is the rpc command :rsp: is the rpc response (after <rpc-reply>) :errs: is a list of dictionaries of extracted <rpc-error> elements. :dev: is the device rpc was executed on :timeout: is the timeout value of the device :re: is the RE or member exception occured on """ self.cmd = cmd self.rsp = rsp self.dev = dev self.timeout = timeout self.re = re self.rpc_error = None self.xml = rsp # To handle errors coming from ncclient, Here errs is list of RPCError if isinstance(errs, RPCError) and hasattr(errs, 'errors'): self.errs = [JXML.rpc_error(error.xml) for error in errs.errors] for error in errs.errors: if error.severity == 'error': self.rsp = JXML.remove_namespaces(error.xml) break else: if errs.severity == 'warning': for error in errs.errors: if error.severity == 'warning': self.rsp = JXML.remove_namespaces(error.xml) break self.message = errs.message else: self.errs = errs self.message = "\n".join(["%s: %s" % (err['severity'].strip(), err['message'].strip()) for err in errs if err['message'] is not None and err['severity'] is not None]) \ if isinstance(errs, list) else '' if isinstance(self.rsp, _Element): self.rpc_error = jxml.rpc_error(self.rsp) self.message = self.message or self.rpc_error['message'] if self.errs is None or not isinstance(self.errs, list): self.errs = [self.rpc_error]
def commit(self, **kvargs): """ commit a configuration. returns either :True: or raises an RPCError exception kvargs confirm = [True | <timeout-minutes>] comment = <comment log string> """ rpc_args = {} # if a comment is provided, then include that in the RPC comment = kvargs.get('comment') if comment: rpc_args['log'] = comment # if confirm is provided, then setup the RPC args # so that Junos will either use the default confirm # timeout (confirm=True) or a specific timeout # (confirm=<minutes>) confirm = kvargs.get('confirm') if confirm: rpc_args['confirmed'] = True confirm_val = str(confirm) if 'True' != confirm_val: rpc_args['confirm-timeout'] = confirm_val # dbl-splat the rpc_args since we want to pass key/value to metaexec # if there is a commit/check error, this will raise an execption try: self.rpc.commit_configuration(**rpc_args) except RpcError as err: # jnpr.junos exception if err.rsp.find('ok') is not None: # this means there are warnings, but no errors return True else: raise CommitError(cmd=err.cmd, rsp=err.rsp) except Exception as err: # so the ncclient gives us something I don't want. I'm going to # convert it and re-raise the commit error JXML.remove_namespaces(err.xml) raise CommitError(rsp=err.xml) return True
def get(self, *vargs, **kvargs): """ Retrieve the XML table data from the Device instance and returns back the Table instance - for call-chaining purposes. If the Table was created with a :path: rather than a Device, then this method will load the XML from that file. In this case, the \*vargs, and \**kvargs are not used. ALIAS: __call__ :vargs: [0] is the table :arg_key: value. This is used so that the caller can retrieve just one item from the table without having to know the Junos RPC argument. :kvargs: these are the name/value pairs relating to the specific Junos XML command attached to the table. For example, if the RPC is 'get-route-information', there are parameters such as 'table' and 'destination'. Any valid RPC argument can be passed to :kvargs: to further filter the results of the :get(): operation. neato! NOTES: If you need to create a 'stub' for unit-testing purposes, you want to create a subclass of your table and overload this methods. """ self._clearkeys() if self._path is not None: # for loading from local file-path self.xml = remove_namespaces(etree.parse(self._path).getroot()) return self if self._lxml is not None: return self argkey = vargs[0] if len(vargs) else None rpc_args = {'normalize': True} # create default <dict> rpc_args.update(self.GET_ARGS) # copy default args rpc_args.update(kvargs) # copy caller provided args if hasattr(self, 'GET_KEY') and argkey is not None: rpc_args.update({self.GET_KEY: argkey}) # execute the Junos RPC to retrieve the table self.xml = getattr(self.RPC, self.GET_RPC)(**rpc_args) # returning self for call-chaining purposes, yo! return self
def unlock(self): """ unlocks the candidate configuration """ try: self.rpc.unlock_configuration() except Exception as err: if isinstance(err, RpcError): raise LockError(rsp=err.rsp) else: # :err: is from ncclient raise UnlockError(rsp=JXML.remove_namespaces(err.xml)) return True
def test_isis_dynamic(device): """isis with dynamic reply""" # read rpc-reply from file rpc_request = 'get-isis-adjacency-information' fname = os.path.join(os.path.dirname(__file__), 'rpc-reply', rpc_request + '.xml') with open(fname, 'r') as f: xml = etree.fromstring(f.read()) xml = JXML.remove_namespaces(xml) # updated rpc-reply (delete first adjacency) xml.find('.//isis-adjacency-information').remove(xml.find('.//isis-adjacency')) # store in rpc_reply_dict dict (fixture) rpc_reply_dict[rpc_request] = etree.tostring(xml) # get isis adjacencys and check if number has decreased from 3 to 2 isis = device.neighbors.isis assert len(isis) == 2
def test_remove_namespaces(self): xmldata = \ """<xsl:stylesheet xmlns:xsl="http://xml.juniper.net/junos"> <xsl:template> <xsl:attribute name="{myname}"> </xsl:attribute> </xsl:template> </xsl:stylesheet>""" import xml.etree.ElementTree as ET root = ET.fromstring(xmldata) test = remove_namespaces(root) for elem in test.getiterator(): i = elem.tag.find('}') if i > 0: i = i + 1 self.assertTrue(i <= 0)
def test_remove_namespaces(self): xmldata = \ """<xsl:stylesheet xmlns:xsl="http://xml.juniper.net/junos"> <xsl:template> <xsl:attribute name="{myname}"> </xsl:attribute> </xsl:template> </xsl:stylesheet>""" import xml.etree.ElementTree as ET root = ET.fromstring(xmldata) test = remove_namespaces(root) for elem in test.getiterator(): i = elem.tag.find('}') if i > 0: i = i + 1 self.assertLessEqual(i, 0)
def _get_software_information(device): # See if device understands "invoke-on all-routing-engines" try: return device.rpc.cli("show version invoke-on all-routing-engines", format='xml', normalize=True) except RpcError as err: # See if device is VC Capable if device.facts['vc_capable'] is True: try: return device.rpc.cli("show version all-members", format='xml', normalize=True) except Exception: pass # check if rpc-reply got 2 child element, one rpc-error and another # software information elif hasattr(err, 'rpc_error') and err.rpc_error is not None and \ 'Could not connect to ' in err.rpc_error.get('message'): logger.debug(err.rpc_error.get('message')) # getparent as rpc-reply got software-information in 2nd element # and dev.cli return just 1st element. rsp = err.xml.getparent() rsp = JXML.remove_namespaces(rsp) if rsp.xpath(".//software-information"): return rsp try: # JDM for Junos Node Slicing return device.rpc.get_software_information(all_servers=True, format='xml', normalize=True) except Exception: pass try: sw_info = device.rpc.get_software_information(normalize=True) except Exception: sw_info = True try: if sw_info is True: # Possibly an NFX which requires 'local' and 'detail' args. sw_info = device.rpc.get_software_information(local=True, detail=True, normalize=True) return sw_info except Exception: pass
def test_remove_namespaces(self): xmldata = \ u"""<xsl:stylesheet xmlns:xsl="http://xml.juniper.net/junos"> <xsl:template> <!-- Handle comments properly --> <xsl:attribute name="{myname}"> </xsl:attribute> </xsl:template> </xsl:stylesheet>""" import xml.etree.ElementTree as ET parser = ET.XMLParser() root = ET.parse(StringIO(xmldata), parser) test = remove_namespaces(root) for elem in test.getiterator(): i = elem.tag.find('}') if i > 0: i = i + 1 self.assertTrue(i <= 0)
def lock(self): """ Attempts an exclusive lock on the candidate configuration. This is a non-blocking call. :returns: ``True`` always when successful :raises LockError: When the lock cannot be obtained """ try: self.rpc.lock_configuration() except Exception as err: if isinstance(err, RpcError): raise LockError(rsp=err.rsp) else: # :err: is from ncclient raise LockError(rsp=JXML.remove_namespaces(err.xml)) return True
def unlock(self): """ Unlocks the candidate configuration. :returns: ``True`` always when successful :raises UnlockError: If you attempt to unlock a configuration when you do not own the lock """ try: self.rpc.unlock_configuration() except Exception as err: if isinstance(err, RpcError): raise UnlockError(rsp=err.rsp) else: # :err: is from ncclient raise UnlockError(rsp=JXML.remove_namespaces(err.xml)) return True
def rpc(self, cmd): """ Write the XML cmd and return the response as XML object. :cmd: <str> of the XML command. if the :cmd: is not XML, then this routine will perform the brackets; i.e. if given 'get-software-information', this routine will turn it into '<get-software-information/>' NOTES: The return XML object is the first child element after the <rpc-reply>. There is also no error-checking performing by this routine. """ if not cmd.startswith('<'): cmd = '<{0}/>'.format(cmd) rpc = six.b('<rpc>{0}</rpc>'.format(cmd)) logger.info('Calling rpc: %s' % rpc) self._tty.rawwrite(rpc) rsp = self._receive() try: rsp = remove_namespaces( rsp[0]) # return first child after the <rpc-reply> except IndexError: if rsp.text.strip() is not '': return rsp # no children, so assume it means we are OK return True except: return etree.XML('<error-in-receive/>') err_msg = rsp.findtext('error-message') if err_msg: if err_msg == 'permission denied': e = EzErrors.PermissionError else: e = EzErrors.RpcError raise e(cmd=cmd, rsp=rsp) return rsp
def wrapper(*args, **kwargs): if 'ignore_warning' in kwargs: ignore_warn = kwargs.pop('ignore_warning') try: result = function(*args, **kwargs) return result except RpcError as ex: ex.rpc_xml = JXML.remove_namespaces(ex.rpc_xml) if hasattr(ex, 'rpc_error') and\ ex.rpc_error['severity'] == 'warning': if ignore_warn is True: return ex.rpc_xml elif isinstance(ignore_warn, (str, unicode)): if re.search(ignore_warn, ex.message, re.I): return ex.rpc_xml elif isinstance(ignore_warn, list): for warn_msg in ignore_warn: if re.search(warn_msg, ex.message, re.I): return ex.rpc_xml raise ex else: raise ex else: return function(*args, **kwargs)
def execute(self, rpc_cmd, **kvargs): """ Executes an XML RPC and returns results as either XML or native python rpc_cmd can either be an XML Element or xml-as-string. In either case the command starts with the specific command element, i.e., not the <rpc> element itself kvargs['to_py'] is a caller provided function that takes the response and will convert the results to native python types. all kvargs will be passed to this function as well in the form: :to_py:( self, rpc_rsp, \**kvargs ) """ if isinstance(rpc_cmd, str): rpc_cmd_e = etree.XML(rpc_cmd) elif isinstance(rpc_cmd, etree._Element): rpc_cmd_e = rpc_cmd else: raise ValueError( "Dont know what to do with rpc of type %s" % rpc_cmd.__class__.__name__) # invoking a bad RPC will cause a connection object exception # will will be raised directly to the caller ... for now ... # @@@ need to trap this and re-raise accordingly. try: rpc_rsp_e = self._conn.rpc(rpc_cmd_e)._NCElement__doc except Exception as err: # err is an NCError from ncclient rsp = JXML.remove_namespaces(err.xml) # see if this is a permission error e = EzErrors.PermissionError if rsp.findtext('error-message') == 'permission denied' else EzErrors.RpcError raise e(cmd=rpc_cmd_e, rsp=rsp) # for RPCs that have embedded rpc-errors, need to check for those now rpc_errs = rpc_rsp_e.xpath('.//rpc-error') if len(rpc_errs): raise EzErrors.RpcError(rpc_cmd_e, rpc_rsp_e, rpc_errs) # skip the <rpc-reply> element and pass the caller first child element # generally speaking this is what they really want. If they want to # uplevel they can always call the getparent() method on it. try: ret_rpc_rsp = rpc_rsp_e[0] except IndexError: # no children, so assume it means we are OK return True # if the caller provided a "to Python" conversion function, then invoke # that now and return the results of that function. otherwise just # return the RPC results as XML if kvargs.get('to_py'): return kvargs['to_py'](self, ret_rpc_rsp, **kvargs) else: return ret_rpc_rsp
def commit(self, **kvargs): """ Commit a configuration. :param str comment: If provided logs this comment with the commit. :param int confirm: If provided activates confirm safeguard with provided value as timeout (minutes). :param int timeout: If provided the command will wait for completion using the provided value as timeout (seconds). By default the device timeout is used. :param bool sync: On dual control plane systems, requests that the candidate configuration on one control plane be copied to the other control plane, checked for correct syntax, and committed on both Routing Engines. :param bool force_sync: On dual control plane systems, forces the candidate configuration on one control plane to be copied to the other control plane. :param bool full: When true requires all the daemons to check and evaluate the new configuration. :param bool detail: When true return commit detail as XML :returns: * ``True`` when successful * Commit detail XML (when detail is True) :raises CommitError: When errors detected in candidate configuration. You can use the Exception errs variable to identify the specific problems .. warning:: If the function does not receive a reply prior to the timeout a RpcTimeoutError will be raised. It is possible the commit was successful. Manual verification may be required. """ rpc_args = {} # if a comment is provided, then include that in the RPC comment = kvargs.get('comment') if comment: rpc_args['log'] = comment # if confirm is provided, then setup the RPC args # so that Junos will either use the default confirm # timeout (confirm=True) or a specific timeout # (confirm=<minutes>) confirm = kvargs.get('confirm') if confirm: rpc_args['confirmed'] = True confirm_val = str(confirm) if 'True' != confirm_val: rpc_args['confirm-timeout'] = confirm_val # if a timeout is provided, then include that in the RPC timeout = kvargs.get('timeout') if timeout: rpc_args['dev_timeout'] = timeout # Check for force_sync and sync if kvargs.get('force_sync'): rpc_args['synchronize'] = True rpc_args['force-synchronize'] = True elif kvargs.get('sync'): rpc_args['synchronize'] = True # Check for full if kvargs.get('full'): rpc_args['full'] = True rpc_varg = [] detail = kvargs.get('detail') if detail: rpc_varg = [{'detail': 'detail'}] # dbl-splat the rpc_args since we want to pass key/value to metaexec # if there is a commit/check error, this will raise an execption try: rsp = self.rpc.commit_configuration(*rpc_varg, **rpc_args) except RpcTimeoutError: raise except RpcError as err: # jnpr.junos exception if err.rsp.find('ok') is not None: # this means there are warnings, but no errors return True else: raise CommitError(cmd=err.cmd, rsp=err.rsp) except Exception as err: # so the ncclient gives us something I don't want. I'm going to # convert it and re-raise the commit error JXML.remove_namespaces(err.xml) raise CommitError(rsp=err.xml) if detail: return rsp else: return True
def execute(self, rpc_cmd, **kvargs): """ Executes an XML RPC and returns results as either XML or native python :param rpc_cmd: can either be an XML Element or xml-as-string. In either case the command starts with the specific command element, i.e., not the <rpc> element itself :param func to_py: Is a caller provided function that takes the response and will convert the results to native python types. all kvargs will be passed to this function as well in the form:: to_py( self, rpc_rsp, **kvargs ) :raises ValueError: When the **rpc_cmd** is of unknown origin :raises PermissionError: When the requested RPC command is not allowed due to user-auth class privilege controls on Junos :raises RpcError: When an ``rpc-error`` element is contained in the RPC-reply :returns: RPC-reply as XML object. If **to_py** is provided, then that function is called, and return of that function is provided back to the caller; presumably to convert the XML to native python data-types (e.g. ``dict``). """ if self.connected is not True: raise EzErrors.ConnectClosedError(self) if isinstance(rpc_cmd, str): rpc_cmd_e = etree.XML(rpc_cmd) elif isinstance(rpc_cmd, etree._Element): rpc_cmd_e = rpc_cmd else: raise ValueError( "Dont know what to do with rpc of type %s" % rpc_cmd.__class__.__name__) # invoking a bad RPC will cause a connection object exception # will will be raised directly to the caller ... for now ... # @@@ need to trap this and re-raise accordingly. try: rpc_rsp_e = self._rpc_reply(rpc_cmd_e) except NcOpErrors.TimeoutExpiredError: # err is a TimeoutExpiredError from ncclient, # which has no such attribute as xml. raise EzErrors.RpcTimeoutError(self, rpc_cmd_e.tag, self.timeout) except NcErrors.TransportError: raise EzErrors.ConnectClosedError(self) except RPCError as err: rsp = JXML.remove_namespaces(err.xml) # see if this is a permission error e = EzErrors.PermissionError if rsp.findtext('error-message') == \ 'permission denied' \ else EzErrors.RpcError raise e(cmd=rpc_cmd_e, rsp=rsp, errs=err) # Something unexpected happened - raise it up except Exception as err: warnings.warn("An unknown exception occured - please report.", RuntimeWarning) raise # From 14.2 onward, junos supports JSON, so now code can be written as # dev.rpc.get_route_engine_information({'format': 'json'}) if rpc_cmd_e.attrib.get('format') in ['json', 'JSON']: ver_info = self.facts.get('version_info') if ver_info and ver_info.major[0] >= 15 or \ (ver_info.major[0] == 14 and ver_info.major[1] >= 2): try: return json.loads(rpc_rsp_e.text) except ValueError as ex: # when data is {}{.*} types if str(ex).startswith('Extra data'): return json.loads( re.sub('\s?{\s?}\s?', '', rpc_rsp_e.text)) else: warnings.warn("Native JSON support is only from 14.2 onwards", RuntimeWarning) # This section is here for the possible use of something other than # ncclient for RPCs that have embedded rpc-errors, need to check for # those now. # rpc_errs = rpc_rsp_e.xpath('.//rpc-error') # if len(rpc_errs): # raise EzErrors.RpcError(cmd=rpc_cmd_e, rsp=rpc_errs[0]) # skip the <rpc-reply> element and pass the caller first child element # generally speaking this is what they really want. If they want to # uplevel they can always call the getparent() method on it. try: ret_rpc_rsp = rpc_rsp_e[0] except IndexError: # For cases where reply are like # <rpc-reply> # protocol: operation-failed # error: device asdf not found # </rpc-reply> if rpc_rsp_e.text is not None and rpc_rsp_e.text.strip() is not '': return rpc_rsp_e # no children, so assume it means we are OK return True # if the caller provided a "to Python" conversion function, then invoke # that now and return the results of that function. otherwise just # return the RPC results as XML if kvargs.get('to_py'): return kvargs['to_py'](self, ret_rpc_rsp, **kvargs) else: return ret_rpc_rsp
def execute(self, rpc_cmd, **kvargs): """ Executes an XML RPC and returns results as either XML or native python :param rpc_cmd: can either be an XML Element or xml-as-string. In either case the command starts with the specific command element, i.e., not the <rpc> element itself :param func to_py': Is a caller provided function that takes the response and will convert the results to native python types. all kvargs will be passed to this function as well in the form:: to_py( self, rpc_rsp, **kvargs ) :raises ValueError: When the **rpc_cmd** is of unknown origin :raises PermissionError: When the requested RPC command is not allowed due to user-auth class privledge controls on Junos :raises RpcError: When an ``rpc-error`` element is contained in the RPC-reply :returns: RPC-reply as XML object. If **to_py** is provided, then that function is called, and return of that function is provided back to the caller; presuably to convert the XML to native python data-types (e.g. ``dict``). """ if isinstance(rpc_cmd, str): rpc_cmd_e = etree.XML(rpc_cmd) elif isinstance(rpc_cmd, etree._Element): rpc_cmd_e = rpc_cmd else: raise ValueError("Dont know what to do with rpc of type %s" % rpc_cmd.__class__.__name__) # invoking a bad RPC will cause a connection object exception # will will be raised directly to the caller ... for now ... # @@@ need to trap this and re-raise accordingly. try: rpc_rsp_e = self._conn.rpc(rpc_cmd_e)._NCElement__doc except Exception as err: # err is an NCError from ncclient rsp = JXML.remove_namespaces(err.xml) # see if this is a permission error e = EzErrors.PermissionError if rsp.findtext( 'error-message') == 'permission denied' else EzErrors.RpcError raise e(cmd=rpc_cmd_e, rsp=rsp) # for RPCs that have embedded rpc-errors, need to check for those now rpc_errs = rpc_rsp_e.xpath('.//rpc-error') if len(rpc_errs): raise EzErrors.RpcError(rpc_cmd_e, rpc_rsp_e, rpc_errs) # skip the <rpc-reply> element and pass the caller first child element # generally speaking this is what they really want. If they want to # uplevel they can always call the getparent() method on it. try: ret_rpc_rsp = rpc_rsp_e[0] except IndexError: # no children, so assume it means we are OK return True # if the caller provided a "to Python" conversion function, then invoke # that now and return the results of that function. otherwise just # return the RPC results as XML if kvargs.get('to_py'): return kvargs['to_py'](self, ret_rpc_rsp, **kvargs) else: return ret_rpc_rsp
def commit(self, **kvargs): """ Commit a configuration. :param str comment: If provide logs this comment with the commit. :param int confirm: If provided activates confirm safeguard with provided value as timeout (minutes). :param int timeout: If provided the command will wait for completion using the provided value as timeout (seconds). By default the device timeout is used. :returns: * ``True`` when successful :raises CommitError: When errors detected in candidate configuration. You can use the Exception errs variable to identify the specific problems .. warning:: If the function does not receive a reply prior to the timeout a RpcTimeoutError will be raised. It is possible the commit was successful. Manual verification may be required. """ rpc_args = {} # if a comment is provided, then include that in the RPC comment = kvargs.get('comment') if comment: rpc_args['log'] = comment # if confirm is provided, then setup the RPC args # so that Junos will either use the default confirm # timeout (confirm=True) or a specific timeout # (confirm=<minutes>) confirm = kvargs.get('confirm') if confirm: rpc_args['confirmed'] = True confirm_val = str(confirm) if 'True' != confirm_val: rpc_args['confirm-timeout'] = confirm_val # if a timeout is provided, then include that in the RPC timeout = kvargs.get('timeout') if timeout: rpc_args['dev_timeout'] = timeout # dbl-splat the rpc_args since we want to pass key/value to metaexec # if there is a commit/check error, this will raise an execption try: self.rpc.commit_configuration(**rpc_args) except RpcTimeoutError: raise except RpcError as err: # jnpr.junos exception if err.rsp.find('ok') is not None: # this means there are warnings, but no errors return True else: raise CommitError(cmd=err.cmd, rsp=err.rsp) except Exception as err: # so the ncclient gives us something I don't want. I'm going to # convert it and re-raise the commit error JXML.remove_namespaces(err.xml) raise CommitError(rsp=err.xml) return True
def _get_software_information(device): # See if device understands "invoke-on all-routing-engines" try: return device.rpc.cli("show version invoke-on all-routing-engines", format='xml', normalize=True) except RpcError as err: # See if device runs on a linux kernel if device.facts['_is_linux']: sw_info_all = device.rpc.get_software_information(normalize=True, node='all') sw_info_re0 = device.rpc.get_software_information(normalize=True, node='re0') sw_info_re1 = device.rpc.get_software_information(normalize=True, node='re1') re0_hostname = sw_info_re0.findtext('./host-name') re1_hostname = sw_info_re1.findtext('./host-name') for current_hostname in sw_info_all.findall( './multi-routing-engine-result/software-information/host-name'): if current_hostname.text == re0_hostname: current_hostname.getparent().getparent().append( JXML('<re-name>re0</re-name>')) elif current_hostname.text == re1_hostname: current_hostname.getparent().getparent().append( JXML('<re-name>re1</re-name>')) return sw_info_all # See if device is VC Capable if device.facts['vc_capable'] is True: try: return device.rpc.cli("show version all-members", format='xml', normalize=True) except Exception: pass # check if rpc-reply got 2 child element, one rpc-error and another # software information elif hasattr(err, 'rpc_error') and err.rpc_error is not None and \ 'Could not connect to ' in err.rpc_error.get('message'): logger.debug(err.rpc_error.get('message')) # getparent as rpc-reply got software-information in 2nd element # and dev.cli return just 1st element. rsp = err.xml.getparent() rsp = JXML.remove_namespaces(rsp) if rsp.xpath(".//software-information"): return rsp try: # JDM for Junos Node Slicing return device.rpc.get_software_information(all_servers=True, format='xml', normalize=True) except Exception: pass try: sw_info = device.rpc.get_software_information(normalize=True) except Exception: sw_info = True try: if sw_info is True: # Possibly an NFX which requires 'local' and 'detail' args. sw_info = device.rpc.get_software_information(local=True, detail=True, normalize=True) return sw_info except Exception: pass
def get(self, *vargs, **kvargs): """ Retrieve the XML table data from the Device instance and returns back the Table instance - for call-chaining purposes. If the Table was created with a :path: rather than a Device, then this method will load the XML from that file. In this case, the \*vargs, and \**kvargs are not used. ALIAS: __call__ :vargs: [0] is the table :arg_key: value. This is used so that the caller can retrieve just one item from the table without having to know the Junos RPC argument. :kvargs: these are the name/value pairs relating to the specific Junos XML command attached to the table. For example, if the RPC is 'get-route-information', there are parameters such as 'table' and 'destination'. Any valid RPC argument can be passed to :kvargs: to further filter the results of the :get(): operation. neato! NOTES: If you need to create a 'stub' for unit-testing purposes, you want to create a subclass of your table and overload this methods. """ self._clearkeys() if self._path is not None: # for loading from local file-path self.xml = remove_namespaces(etree.parse(self._path).getroot()) return self if self._lxml is not None: return self argkey = vargs[0] if len(vargs) else None rpc_args = {} if self._use_filter: try: filter_xml = generate_sax_parser_input(self) rpc_args["filter_xml"] = filter_xml except Exception as ex: logger.debug("Not able to create SAX parser input due to " "'%s'" % ex) self.D.transform = lambda: remove_namespaces_and_spaces rpc_args.update(self.GET_ARGS) # copy default args # saltstack get_table pass args as named keyword if "args" in kvargs and isinstance(kvargs["args"], dict): rpc_args.update(kvargs.pop("args")) rpc_args.update(kvargs) # copy caller provided args if hasattr(self, "GET_KEY") and argkey is not None: rpc_args.update({self.GET_KEY: argkey}) # execute the Junos RPC to retrieve the table self.xml = getattr(self.RPC, self.GET_RPC)(**rpc_args) # returning self for call-chaining purposes, yo! return self
def execute(self, rpc_cmd, **kvargs): """ Executes an XML RPC and returns results as either XML or native python :param rpc_cmd: can either be an XML Element or xml-as-string. In either case the command starts with the specific command element, i.e., not the <rpc> element itself :param func to_py': Is a caller provided function that takes the response and will convert the results to native python types. all kvargs will be passed to this function as well in the form:: to_py( self, rpc_rsp, **kvargs ) :raises ValueError: When the **rpc_cmd** is of unknown origin :raises PermissionError: When the requested RPC command is not allowed due to user-auth class privilege controls on Junos :raises RpcError: When an ``rpc-error`` element is contained in the RPC-reply :returns: RPC-reply as XML object. If **to_py** is provided, then that function is called, and return of that function is provided back to the caller; presumably to convert the XML to native python data-types (e.g. ``dict``). """ if self.connected is not True: raise EzErrors.ConnectClosedError(self) if isinstance(rpc_cmd, str): rpc_cmd_e = etree.XML(rpc_cmd) elif isinstance(rpc_cmd, etree._Element): rpc_cmd_e = rpc_cmd else: raise ValueError( "Dont know what to do with rpc of type %s" % rpc_cmd.__class__.__name__) # invoking a bad RPC will cause a connection object exception # will will be raised directly to the caller ... for now ... # @@@ need to trap this and re-raise accordingly. try: rpc_rsp_e = self._conn.rpc(rpc_cmd_e)._NCElement__doc except NcOpErrors.TimeoutExpiredError: # err is a TimeoutExpiredError from ncclient, # which has no such attribute as xml. raise EzErrors.RpcTimeoutError(self, rpc_cmd_e.tag, self.timeout) except NcErrors.TransportError: raise EzErrors.ConnectClosedError(self) except RPCError as err: # err is an NCError from ncclient rsp = JXML.remove_namespaces(err.xml) # see if this is a permission error e = EzErrors.PermissionError if rsp.findtext('error-message') == 'permission denied' else EzErrors.RpcError raise e(cmd=rpc_cmd_e, rsp=rsp) # Something unexpected happened - raise it up except Exception as err: warnings.warn("An unknown exception occured - please report.", RuntimeWarning) raise # This section is here for the possible use of something other than ncclient # for RPCs that have embedded rpc-errors, need to check for those now # rpc_errs = rpc_rsp_e.xpath('.//rpc-error') # if len(rpc_errs): # raise EzErrors.RpcError(cmd=rpc_cmd_e, rsp=rpc_errs[0]) # skip the <rpc-reply> element and pass the caller first child element # generally speaking this is what they really want. If they want to # uplevel they can always call the getparent() method on it. try: ret_rpc_rsp = rpc_rsp_e[0] except IndexError: # no children, so assume it means we are OK return True # if the caller provided a "to Python" conversion function, then invoke # that now and return the results of that function. otherwise just # return the RPC results as XML if kvargs.get('to_py'): return kvargs['to_py'](self, ret_rpc_rsp, **kvargs) else: return ret_rpc_rsp
def _get_software_information(device): # See if device understands "invoke-on all-routing-engines" try: return device.rpc.cli("show version invoke-on all-routing-engines", format='xml', normalize=True) except RpcError as err: # See if device runs on a linux kernel if device.facts['_is_linux']: sw_info_all = device.rpc.get_software_information(normalize=True, node='all') sw_info_re0 = device.rpc.get_software_information(normalize=True, node='re0') sw_info_re1 = device.rpc.get_software_information(normalize=True, node='re1') re0_hostname = sw_info_re0.findtext('./host-name') re1_hostname = sw_info_re1.findtext('./host-name') for current_hostname in sw_info_all.findall( './multi-routing-engine-result/software-information/host-name' ): if current_hostname.text == re0_hostname: current_hostname.getparent().getparent().append( JXML('<re-name>re0</re-name>')) elif current_hostname.text == re1_hostname: current_hostname.getparent().getparent().append( JXML('<re-name>re1</re-name>')) return sw_info_all # See if device is VC Capable if device.facts['vc_capable'] is True: try: return device.rpc.cli("show version all-members", format='xml', normalize=True) except Exception: pass # check if rpc-reply got 2 child element, one rpc-error and another # software information elif hasattr(err, 'rpc_error') and err.rpc_error is not None and \ 'Could not connect to ' in err.rpc_error.get('message'): logger.debug(err.rpc_error.get('message')) # getparent as rpc-reply got software-information in 2nd element # and dev.cli return just 1st element. rsp = err.xml.getparent() rsp = JXML.remove_namespaces(rsp) if rsp.xpath(".//software-information"): return rsp try: # JDM for Junos Node Slicing return device.rpc.get_software_information(all_servers=True, format='xml', normalize=True) except Exception: pass try: sw_info = device.rpc.get_software_information(normalize=True) except Exception: sw_info = True try: if sw_info is True: # Possibly an NFX which requires 'local' and 'detail' args. sw_info = device.rpc.get_software_information(local=True, detail=True, normalize=True) return sw_info except Exception: pass