def test_parse_response(self): """check parse response code""" xml = self.CORRECT_REPLY netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(xmlns, netconf_namespace, xml) self.assertEqual(response, { '_@rfc6020@message-id': '57380', 'rfc6020@ok': None }) self.assertEqual(xmlns, {netconf_namespace: utils.NETCONF_NAMESPACE}) # no namespace in reply xml = """ <rpc-reply message-id="57380"> <ok/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(xmlns, netconf_namespace, xml) self.assertEqual(response, {'_@@message-id': '57380', 'ok': None}) self.assertEqual(xmlns, {netconf_namespace: utils.NETCONF_NAMESPACE}) # dont have reply xml = """ <rpc message-id="57380"> <ok/> </rpc> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(xmlns, netconf_namespace, xml) # error in reply xml = """ <rpc-reply message-id="57380"> <rpc-error/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(xmlns, netconf_namespace, xml) # error in reply with namespace xml = """ <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="57380"> <rpc-error/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(xmlns, netconf_namespace, xml)
def run(**kwargs): """main entry point for all operations""" xmls = kwargs.get('xmls', []) if not xmls: ctx.logger.info("No xmls for translate") return xmlns = kwargs.get('xmlns', {}) netconf_namespace, xmlns = utils.update_xmlns( xmlns ) for xml_struct in xmls: raw_xml = xml_struct.get("raw") if not raw_xml: ctx.logger.info("Empty raw xml?") continue ctx.logger.info("Parsing %s..." % (raw_xml[:60])) xml_node = etree.XML(raw_xml) xml_dict = {} utils.generate_dict_node( xml_dict, xml_node, xmlns ) # save results to runtime properties save_to = xml_struct.get('save_to') if save_to: ctx.instance.runtime_properties[save_to] = xml_dict ctx.instance.runtime_properties[save_to + "_ns"] = xmlns
def test_rpc_gen(self): """check rpc_gen""" xmlns = { 'r': utils.NETCONF_NAMESPACE, 'n': "someaction" } netconf_namespace, xmlns = utils.update_xmlns( xmlns ) self.assertEqual(netconf_namespace, "r") data = { "b": "b" } # dont have namespace in action parent = utils.rpc_gen( "some_id", 'run', netconf_namespace, data, xmlns ) rpc_string = etree.tostring(parent) example_string = ( """<r:rpc xmlns:r="urn:ietf:params:xml:ns:netconf:base:""" + """1.0" xmlns:n="someaction" message-id="some_id"><r:ru""" + """n><r:b>b</r:b></r:run></r:rpc>""" ) self.assertEqual(rpc_string, example_string) # have namespace in action parent = utils.rpc_gen( "some_id", 'n@run', netconf_namespace, data, xmlns ) rpc_string = etree.tostring(parent) example_string = ( """<r:rpc xmlns:r="urn:ietf:params:xml:ns:netconf:base:""" + """1.0" xmlns:n="someaction" message-id="some_id"><n:ru""" + """n><n:b>b</n:b></n:run></r:rpc>""" ) self.assertEqual(rpc_string, example_string)
def test_rpc_gen(self): """check rpc_gen""" xmlns = { 'r': utils.NETCONF_NAMESPACE, 'n': "someaction" } netconf_namespace, xmlns = utils.update_xmlns( xmlns ) self.assertEqual(netconf_namespace, "r") data = { "b": "b" } # dont have namespace in action parent = utils.rpc_gen( "some_id", 'run', netconf_namespace, data, xmlns ) rpc_string = etree.tostring(parent) example_string = ( """<r:rpc xmlns:n="someaction" xmlns:r="urn:ietf:""" + """params:xml:ns:netconf:base:1.0" r:message-id""" + """="some_id"><r:run><r:b>b</r:b></r:run></r:rpc>""" ) self.assertEqual(rpc_string, example_string) # have namespace in action parent = utils.rpc_gen( "some_id", 'n@run', netconf_namespace, data, xmlns ) rpc_string = etree.tostring(parent) example_string = ( """<r:rpc xmlns:n="someaction" xmlns:r="urn:ietf:""" + """params:xml:ns:netconf:base:1.0" r:message-id""" + """="some_id"><n:run><n:b>b</n:b></n:run></r:rpc>""" ) self.assertEqual(rpc_string, example_string)
def test_server_support_1_1(self): """check support 1.1 response from server""" netconf_namespace, xmlns = utils.update_xmlns({}) self.assertFalse( rpc._server_support_1_1(xmlns, netconf_namespace, self.CORRECT_HELLO_REPLY)) self.assertTrue( rpc._server_support_1_1(xmlns, netconf_namespace, self.CORRECT_HELLO_1_1_REPLY))
def test_update_xmlns(self): """check add default namespace""" namespace, xmlns = utils.update_xmlns({}) self.assertEqual( namespace, utils.DEFAULT_NCNS ) self.assertEqual( xmlns, { utils.DEFAULT_NCNS: utils.NETCONF_NAMESPACE } )
def test_generate_goodbye(self): """check goodbye message""" goodbye_message = ( """<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<rfc6020""" + """:rpc xmlns:rfc6020="urn:ietf:params:xml:ns:netconf:b""" + """ase:1.0" message-id="last_message">\n <rfc6020:clos""" + """e-session/>\n</rfc6020:rpc>\n""") netconf_namespace, xmlns = utils.update_xmlns({}) goodbye_string = rpc._generate_goodbye(xmlns, netconf_namespace, "last_message") self.assertEqual(goodbye_string, goodbye_message)
def test_server_support_1_1(self): """check support 1.1 response from server""" netconf_namespace, xmlns = utils.update_xmlns({}) self.assertFalse( rpc._server_support_1_1( xmlns, netconf_namespace, self.CORRECT_HELLO_REPLY ) ) self.assertTrue( rpc._server_support_1_1( xmlns, netconf_namespace, self.CORRECT_HELLO_1_1_REPLY ) )
def test_generate_hello(self): """check hello message""" hello_message = ( """<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<rfc6020""" + """:hello xmlns:rfc6020="urn:ietf:params:xml:ns:netconf""" + """:base:1.0">\n <rfc6020:capabilities>\n <rfc6020:""" + """capability>urn:ietf:params:netconf:base:1.0</rfc6020""" + """:capability>\n <rfc6020:capability>urn:ietf:param""" + """s:netconf:base:1.1</rfc6020:capability>\n </rfc6020""" + """:capabilities>\n</rfc6020:hello>\n""") netconf_namespace, xmlns = utils.update_xmlns({}) hello_string = rpc._generate_hello(xmlns, netconf_namespace, {}) self.assertEqual(hello_string, hello_message)
def test_generate_goodbye(self): """check goodbye message""" goodbye_message = ( """<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<rfc6020""" + """:rpc xmlns:rfc6020="urn:ietf:params:xml:ns:netconf:b""" + """ase:1.0" message-id="last_message">\n <rfc6020:clos""" + """e-session/>\n</rfc6020:rpc>\n""" ) netconf_namespace, xmlns = utils.update_xmlns({}) goodbye_string = rpc._generate_goodbye( xmlns, netconf_namespace, "last_message" ) self.assertEqual(goodbye_string, goodbye_message)
def test_generate_hello(self): """check hello message""" hello_message = ( """<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<rfc6020""" + """:hello xmlns:rfc6020="urn:ietf:params:xml:ns:netconf""" + """:base:1.0">\n <rfc6020:capabilities>\n <rfc6020:""" + """capability>urn:ietf:params:netconf:base:1.0</rfc6020""" + """:capability>\n <rfc6020:capability>urn:ietf:param""" + """s:netconf:base:1.1</rfc6020:capability>\n </rfc6020""" + """:capabilities>\n</rfc6020:hello>\n""" ) netconf_namespace, xmlns = utils.update_xmlns({}) hello_string = rpc._generate_hello(xmlns, netconf_namespace, {}) self.assertEqual(hello_string, hello_message)
source: running: {} filter: _@@type: subtree turing@turing-machine: turing@transition-function: {} ------------------- """ if __name__ == "__main__": if len(sys.argv) != 2: print(help_message) else: yaml_rpc = open(sys.argv[1], 'rb') with yaml_rpc: yaml_text = yaml_rpc.read() yaml_dict = yaml.load(yaml_text) data = yaml_dict.get('payload', {}) xmlns = yaml_dict.get('ns', {}) operation = yaml_dict.get('action', 'get') netconf_namespace, xmlns = utils.update_xmlns( xmlns ) parent = utils.rpc_gen( "some_id", operation, netconf_namespace, data, xmlns ) rpc_string = etree.tostring( parent, pretty_print=True, xml_declaration=True, encoding='UTF-8' ) print(rpc_string)
action: get payload: source: running: {} filter: _@@type: subtree turing@turing-machine: turing@transition-function: {} ------------------- """ if __name__ == "__main__": if len(sys.argv) != 2: print(help_message) else: yaml_rpc = open(sys.argv[1], 'rb') with yaml_rpc: yaml_text = yaml_rpc.read() yaml_dict = yaml.load(yaml_text) data = yaml_dict.get('payload', {}) xmlns = yaml_dict.get('ns', {}) operation = yaml_dict.get('action', 'get') netconf_namespace, xmlns = utils.update_xmlns(xmlns) parent = utils.rpc_gen("some_id", operation, netconf_namespace, data, xmlns) rpc_string = etree.tostring(parent, pretty_print=True, xml_declaration=True, encoding='UTF-8') print(rpc_string)
def test_parse_response(self): """check parse response code""" xml = self.CORRECT_REPLY fake_ctx = cfy_mocks.MockCloudifyContext() current_ctx.set(fake_ctx) netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) self.assertEqual(response, { '_@rfc6020@message-id': '57380', 'rfc6020@ok': None }) self.assertEqual(xmlns, {netconf_namespace: utils.NETCONF_NAMESPACE}) # no namespace in reply xml = """ <rpc-reply message-id="57380"> <ok/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) self.assertEqual(response, {'_@@message-id': '57380', 'ok': None}) self.assertEqual(xmlns, {netconf_namespace: utils.NETCONF_NAMESPACE}) # dont have reply xml = """ <rpc message-id="57380"> <ok/> </rpc> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) # error in reply xml = """ <rpc-reply message-id="57380"> <rpc-error/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.RecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) # warning in reply xml = """ <rpc-reply message-id="57380"> <rpc-error> <error-severity>warning</error-severity> </rpc-error> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) # error in reply xml = """ <rpc-reply message-id="57380"> <rpc-error> <error-severity>error</error-severity> </rpc-error> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.RecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) # error in reply in uncommon place xml = """ <rpc-reply message-id="57380"> <uncommon> <rpc-error> <error-severity>error</error-severity> </rpc-error> </uncommon> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.RecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True, True) # error in reply with namespace xml = """ <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="57380"> <rpc-error/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.RecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) # warning in reply with namespace xml = """ <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="57380"> <rpc-error> <error-severity>warning</error-severity> </rpc-error> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True) # error in reply in uncommon place with namespace xml = """ <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="57380"> <uncommon> <rpc-error> <error-severity>error</error-severity> </rpc-error> </uncommon> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.RecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True, True) # check issues with xml xml = """ <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:junos="http://xml.juniper.net/junos/12.1X46/junos" xmlns:junos="http://xml.juniper.net/junos/12.1X46/junos" xmlns:rfc6020="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:xnm="http://xml.juniper.net/xnm/1.1/xnm" rfc6020:message-id="229"> <ok/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, False) self.assertEqual(response, { 'rfc6020@ok': None, '_@rfc6020@message-id': '229' }) # raise execption for uncorrect xml netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(fake_ctx, xmlns, netconf_namespace, xml, True)
def run(ctx, **kwargs): """main entry point for all calls""" calls = kwargs.get('calls', []) templates_locs = kwargs.get('templates', []) template = kwargs.get('template') templates = [] for tmpl_loc in templates_locs: templates.append(_get_template(ctx, tmpl_loc)) if template: templates.extend(_get_template(ctx, template).split("]]>]]>")) if not calls and not templates: ctx.logger.info("Please provide calls or template") return # credentials properties = ctx.node.properties netconf_auth = properties.get('netconf_auth', {}) netconf_auth.update(kwargs.get('netconf_auth', {})) user = netconf_auth.get('user') password = netconf_auth.get('password') key_content = netconf_auth.get('key_content') port = int(netconf_auth.get('port', 830)) ip_list = netconf_auth.get('ip') if isinstance(ip_list, basestring): ip_list = [ip_list] # save logs to debug file log_file_name = None if netconf_auth.get('store_logs'): log_file_name = ( "/tmp/netconf-{execution_id}_{instance_id}_{workflow_id}.log". format(execution_id=str(ctx.execution_id), instance_id=str(ctx.instance.id), workflow_id=str(ctx.workflow_id))) ctx.logger.info( "Communication logs will be saved to {log_file_name}".format( log_file_name=log_file_name)) strict_check = kwargs.get('strict_check', True) # if node contained in some other node, try to overwrite ip if not ip_list: ip_list = [ctx.instance.host_ip] ctx.logger.info("Used host from container: {ip_list}".format( ip_list=filters.shorted_text(ip_list))) # check minimal amout of credentials if not port or not ip_list or not user or (not password and not key_content): raise cfy_exc.NonRecoverableError("please check your credentials") # some random initial message id, for have different between calls message_id = int((time.time() * 100) % 100 * 1000) # xml namespaces and capabilities xmlns = properties.get('metadata', {}).get('xmlns', {}) # override by system namespaces xmlns = _merge_ns(xmlns, properties.get('base_xmlns', {})) netconf_namespace, xmlns = utils.update_xmlns(xmlns) capabilities = properties.get('metadata', {}).get('capabilities') # connect ctx.logger.info("use {user}@{ip_list}:{port} for login".format( user=user, ip_list=ip_list, port=port)) hello_string = _generate_hello(xmlns, netconf_namespace, capabilities) ctx.logger.debug( "Sent: {message}".format(message=filters.shorted_text(hello_string))) netconf = netconf_connection.NetConfConnection(logger=ctx.logger, log_file_name=log_file_name) for ip in ip_list: try: capabilities = netconf.connect(ip, user, hello_string, password, key_content, port) ctx.logger.info("Will be used: {ip}".format(ip=ip)) break except Exception as ex: ctx.logger.info("Can't connect to {ip} with {ex}".format( ip=repr(ip), ex=str(ex))) else: raise cfy_exc.NonRecoverableError("please check your ip list") ctx.logger.debug("Recieved: {capabilities}".format( capabilities=filters.shorted_text(capabilities))) if _server_support_1_1(xmlns, netconf_namespace, capabilities): ctx.logger.info("use version 1.1 of netconf protocol") netconf.current_level = netconf_connection.NETCONF_1_1_CAPABILITY else: ctx.logger.info("use version 1.0 of netconf protocol") try: message_id = _run_in_locked(ctx=ctx, netconf=netconf, message_id=message_id, netconf_namespace=netconf_namespace, xmlns=xmlns, calls=calls, templates=templates, kwargs=kwargs, strict_check=strict_check) finally: # goodbye ctx.logger.info("Connection close") message_id += 1 goodbye_string = _generate_goodbye(xmlns, netconf_namespace, message_id) ctx.logger.debug("Sent: {message}".format( message=filters.shorted_text(goodbye_string))) response = netconf.close(goodbye_string) ctx.logger.debug("Recieved: {message} ".format( message=filters.shorted_text(response)))
def run(**kwargs): """main entry point for all calls""" calls = kwargs.get('calls', []) template = kwargs.get('template') templates = [] if template: templates = ctx.get_resource(template).split("]]>]]>") if not calls and not templates: ctx.logger.info("Please provide calls or template") return # credentials properties = ctx.node.properties netconf_auth = properties.get('netconf_auth', {}) netconf_auth.update(kwargs.get('netconf_auth', {})) user = netconf_auth.get('user') password = netconf_auth.get('password') key_content = netconf_auth.get('key_content') port = int(netconf_auth.get('port', 830)) ip_list = netconf_auth.get('ip') if isinstance(ip_list, basestring): ip_list = [ip_list] # save logs to debug file log_file_name = None if netconf_auth.get('store_logs'): log_file_name = "/tmp/netconf-%s_%s_%s.log" % (str( ctx.execution_id), str(ctx.instance.id), str(ctx.workflow_id)) ctx.logger.info("Communication logs will be saved to %s" % log_file_name) # if node contained in some other node, try to overwrite ip if not ip_list: ip_list = [ctx.instance.host_ip] ctx.logger.info("Used host from container: %s" % str(ip_list)) # check minimal amout of credentials if not port or not ip_list or not user or (not password and not key_content): raise cfy_exc.NonRecoverableError("please check your credentials") # some random initial message id, for have different between calls message_id = int((time.time() * 100) % 100 * 1000) # xml namespaces and capabilities xmlns = properties.get('metadata', {}).get('xmlns', {}) # override by system namespaces xmlns = _merge_ns(xmlns, properties.get('base_xmlns', {})) netconf_namespace, xmlns = utils.update_xmlns(xmlns) capabilities = properties.get('metadata', {}).get('capabilities') # connect ctx.logger.info("use %s@%s:%s for login" % (user, ip_list, port)) hello_string = _generate_hello(xmlns, netconf_namespace, capabilities) ctx.logger.info("i sent: " + hello_string) _write_to_log(log_file_name, hello_string) netconf = netconf_connection.connection() for ip in ip_list: try: capabilities = netconf.connect(ip, user, hello_string, password, key_content, port) ctx.logger.info("Will be used: " + ip) break except Exception as ex: ctx.logger.info("Can't connect to %s with %s" % (repr(ip), str(ex))) else: raise cfy_exc.NonRecoverableError("please check your ip list") ctx.logger.info("i recieved: " + capabilities) _write_to_log(log_file_name, capabilities) if _server_support_1_1(xmlns, netconf_namespace, capabilities): ctx.logger.info("i will use version 1.1 of netconf protocol") netconf.current_level = netconf_connection.NETCONF_1_1_CAPABILITY else: ctx.logger.info("i will use version 1.0 of netconf protocol") strict_check = kwargs.get('strict_check', True) if 'lock' in kwargs: message_id = message_id + 1 for name in kwargs['lock']: _lock(name, True, netconf, message_id, netconf_namespace, xmlns, strict_check, log_file_name) if 'back_database' in kwargs and 'front_database' in kwargs: message_id = message_id + 1 _copy(kwargs['front_database'], kwargs['back_database'], netconf, message_id, netconf_namespace, xmlns, strict_check, log_file_name=log_file_name) if calls: dsdl = properties.get('metadata', {}).get('dsdl') _run_calls(netconf, message_id, netconf_namespace, xmlns, calls, kwargs.get('back_database'), dsdl, strict_check, log_file_name=log_file_name) elif templates: template_params = kwargs.get('params') deep_error_check = kwargs.get('deep_error_check') ctx.logger.info("Params for template %s" % str(template_params)) _run_templates(netconf, templates, template_params, netconf_namespace, xmlns, strict_check, deep_error_check, log_file_name=log_file_name) if 'back_database' in kwargs and 'front_database' in kwargs: message_id = message_id + 1 _copy(kwargs['back_database'], kwargs['front_database'], netconf, message_id, netconf_namespace, xmlns, strict_check, log_file_name=log_file_name) if 'lock' in kwargs: message_id = message_id + 1 for name in kwargs['lock']: _lock(name, False, netconf, message_id, netconf_namespace, xmlns, strict_check, log_file_name=log_file_name) # goodbye ctx.logger.info("connection close") message_id = message_id + 1 goodbye_string = _generate_goodbye(xmlns, netconf_namespace, message_id) ctx.logger.info("i sent: " + goodbye_string) response = netconf.close(goodbye_string) ctx.logger.info("i recieved: " + response)
def test_parse_response(self): """check parse response code""" xml = self.CORRECT_REPLY netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(xmlns, netconf_namespace, xml) self.assertEqual( response, { '_@rfc6020@message-id': '57380', 'rfc6020@ok': None } ) self.assertEqual( xmlns, { netconf_namespace: utils.NETCONF_NAMESPACE } ) # no namespace in reply xml = """ <rpc-reply message-id="57380"> <ok/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) response = rpc._parse_response(xmlns, netconf_namespace, xml) self.assertEqual( response, { '_@@message-id': '57380', 'ok': None } ) self.assertEqual( xmlns, { netconf_namespace: utils.NETCONF_NAMESPACE } ) # dont have reply xml = """ <rpc message-id="57380"> <ok/> </rpc> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(xmlns, netconf_namespace, xml) # error in reply xml = """ <rpc-reply message-id="57380"> <rpc-error/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(xmlns, netconf_namespace, xml) # error in reply with namespace xml = """ <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="57380"> <rpc-error/> </rpc-reply> """ netconf_namespace, xmlns = utils.update_xmlns({}) with self.assertRaises(cfy_exc.NonRecoverableError): rpc._parse_response(xmlns, netconf_namespace, xml)