From 826ea9d2a90a09111c6a4fc8000858d08f2010dd Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 20 Sep 2023 16:25:30 +0200 Subject: [PATCH] test/infamy: add missing RPC to netconf-client The netconf-client Python lib currently does not support the RPC. This commit adds a local extension to the Device class which can be upstreamed at a later time. Also some added comments and disabling of pylint warnings. Signed-off-by: Joachim Wiberg --- test/infamy/netconf.py | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/infamy/netconf.py b/test/infamy/netconf.py index 14353767..56303d96 100644 --- a/test/infamy/netconf.py +++ b/test/infamy/netconf.py @@ -6,6 +6,7 @@ import logging import socket import sys import time +import uuid # For _ncc_get_data() extension import libyang import lxml @@ -45,6 +46,13 @@ class Manager(netconf_client.ncclient.Manager): self.set_logger_level(logging.DEBUG) self.logger().addHandler(logging.StreamHandler(sys.stderr)) +class NccGetDataReply: + """Fold in to DataReply class when upstreaming""" + def __init__(self, raw, ele): + self.data_ele = ele.find("{urn:ietf:params:xml:ns:yang:ietf-netconf-nmda}data") + self.data_xml = lxml.etree.tostring(self.data_ele) + self.raw_reply = raw + @dataclass class Location: host: str @@ -120,6 +128,25 @@ class Device(object): return list(filter(lambda m: m["name"] in modnames, self.modules.values())) + def _ncc_make_rpc(self, guts, msg_id=None): + if not msg_id: + msg_id = uuid.uuid4() + + return '{guts}' \ + .format(guts=guts, id=msg_id).encode("utf-8") + + def _ncc_get_data_rpc(self, datastore="operational", filter=None, msg_id=None): + pieces = [] + pieces.append('') + pieces.append(f'' + f'ds:{datastore}' + f'') + if filter: + xmlns = " ".join([f"xmlns:{m['name']}=\"{m['namespace']}\"" for m in self._modules_in_xpath(filter)]) + pieces.append(f'{filter}') + pieces.append("") + return self._ncc_make_rpc("".join(pieces), msg_id=msg_id) + def _get(self, xpath, getter): # Figure out which modules we are referencing mods = self._modules_in_xpath(xpath) @@ -127,16 +154,32 @@ class Device(object): # Fetch the data xmlns = " ".join([f"xmlns:{m['name']}=\"{m['namespace']}\"" for m in mods]) filt = f"" + # pylint: disable=c-extension-no-member cfg = lxml.etree.tostring(getter(filter=filt).data_ele[0]) return self.ly.parse_data_mem(cfg, "xml", parse_only=True) + def _get_data(self, xpath): + """Local member wrapper for netconf-client RPC""" + # pylint: disable=protected-access + (raw, ele) = self.ncc._send_rpc(self._ncc_get_data_rpc(filter=xpath)) + data = NccGetDataReply(raw, ele) + # pylint: disable=c-extension-no-member + cfg = lxml.etree.tostring(data.data_ele[0]) + return self.ly.parse_data_mem(cfg, "xml", parse_only=True) + def get(self, xpath): + """RPC (legacy NETCONF) fetches config:false data""" return self._get(xpath, self.ncc.get) def get_dict(self, xpath): + """Return Python dictionary of RPC data""" return self.get(xpath).print_dict() + def get_data(self, xpath): + """RPC to fetch operational data""" + return self._get_data(xpath).print_dict() + def get_config(self, xpath): return self._get(xpath, self.ncc.get_config)