diff --git a/src/cisco_gnmi/__init__.py b/src/cisco_gnmi/__init__.py index 60f494f..73dc717 100644 --- a/src/cisco_gnmi/__init__.py +++ b/src/cisco_gnmi/__init__.py @@ -30,4 +30,4 @@ from .xe import XEClient from .builder import ClientBuilder -__version__ = "1.0.7" +__version__ = "1.0.8" diff --git a/src/cisco_gnmi/builder.py b/src/cisco_gnmi/builder.py index a74a219..8fba394 100644 --- a/src/cisco_gnmi/builder.py +++ b/src/cisco_gnmi/builder.py @@ -31,6 +31,10 @@ from .util import gen_target_netloc, get_cert_from_target, get_cn_from_cert +LOGGER = logging.getLogger(__name__) +logger = LOGGER + + class ClientBuilder(object): """Builder for the creation of a gNMI client. Supports construction of base Client and XRClient. @@ -134,8 +138,8 @@ def set_os(self, name=None): if name not in self.os_class_map.keys(): raise Exception("OS not supported!") else: + LOGGER.debug("Using %s wrapper.", name or "Client") self.__client_class = self.os_class_map[name] - logging.debug("Using %s wrapper.", name or "Client") return self def set_secure( @@ -257,7 +261,7 @@ def set_channel_option(self, name, value): found_index = index break if found_index is not None: - logging.warning("Found existing channel option %s, overwriting!", name) + LOGGER.warning("Found existing channel option %s, overwriting!", name) self.__channel_options[found_index] = new_option else: self.__channel_options.append(new_option) @@ -279,18 +283,18 @@ def construct(self): self.__root_certificates, self.__private_key, self.__certificate_chain ) if self.__username and self.__password: + LOGGER.debug("Using username/password call authentication.") channel_metadata_creds = grpc.metadata_call_credentials( CiscoAuthPlugin(self.__username, self.__password) ) - logging.debug("Using username/password call authentication.") if channel_ssl_creds and channel_metadata_creds: + LOGGER.debug("Using SSL/metadata authentication composite credentials.") channel_creds = grpc.composite_channel_credentials( channel_ssl_creds, channel_metadata_creds ) - logging.debug("Using SSL/metadata authentication composite credentials.") else: + LOGGER.debug("Using SSL credentials, no metadata authentication.") channel_creds = channel_ssl_creds - logging.debug("Using SSL credentials, no metadata authentication.") if self.__ssl_target_name_override is not False: if self.__ssl_target_name_override is None: if not self.__root_certificates: @@ -298,7 +302,7 @@ def construct(self): self.__ssl_target_name_override = get_cn_from_cert( self.__root_certificates ) - logging.warning( + LOGGER.warning( "Overriding SSL option from certificate could increase MITM susceptibility!" ) self.set_channel_option( diff --git a/src/cisco_gnmi/client.py b/src/cisco_gnmi/client.py index d3caf08..f4ec9d8 100755 --- a/src/cisco_gnmi/client.py +++ b/src/cisco_gnmi/client.py @@ -31,6 +31,10 @@ from . import util +LOGGER = logging.getLogger(__name__) +logger = LOGGER + + class Client(object): """gNMI gRPC wrapper client to ease usage of gNMI. @@ -109,6 +113,7 @@ def capabilities(self): proto.gnmi_pb2.CapabilityResponse """ message = proto.gnmi_pb2.CapabilityRequest() + LOGGER.debug(str(message)) response = self.service.Capabilities(message) return response @@ -163,6 +168,9 @@ def get( request.use_models = use_models if extension: request.extension = extension + + LOGGER.debug(str(request)) + get_response = self.service.Get(request) return get_response @@ -190,7 +198,7 @@ def set( """ request = proto.gnmi_pb2.SetRequest() if prefix: - request.prefix = prefix + request.prefix.CopyFrom(prefix) test_list = [updates, replaces, deletes] if not any(test_list): raise Exception("At least update, replace, or delete must be specified!") @@ -207,6 +215,9 @@ def set( request.delete.extend(deletes) if extensions: request.extension.extend(extensions) + + LOGGER.debug(str(request)) + response = self.service.Set(request) return response @@ -244,6 +255,9 @@ def validate_request(request): ) if extensions: subscribe_request.extensions.extend(extensions) + + LOGGER.debug(str(subscribe_request)) + return subscribe_request response_stream = self.service.Subscribe( diff --git a/src/cisco_gnmi/nx.py b/src/cisco_gnmi/nx.py index 4aab05b..ae6f729 100644 --- a/src/cisco_gnmi/nx.py +++ b/src/cisco_gnmi/nx.py @@ -30,6 +30,10 @@ from .client import Client, proto, util +LOGGER = logging.getLogger(__name__) +logger = LOGGER + + class NXClient(Client): """NX-OS-specific wrapper for gNMI functionality. diff --git a/src/cisco_gnmi/util.py b/src/cisco_gnmi/util.py index 7d205a6..572a7d3 100644 --- a/src/cisco_gnmi/util.py +++ b/src/cisco_gnmi/util.py @@ -37,6 +37,10 @@ from urlparse import urlparse +LOGGER = logging.getLogger(__name__) +logger = LOGGER + + def gen_target_netloc(target, netloc_prefix="//", default_port=9339): """Parses and validates a supplied target URL for gRPC calls. Uses urllib to parse the netloc property from the URL. @@ -51,11 +55,11 @@ def gen_target_netloc(target, netloc_prefix="//", default_port=9339): if not parsed_target.netloc: raise ValueError("Unable to parse netloc from target URL %s!" % target) if parsed_target.scheme: - logging.debug("Scheme identified in target, ignoring and using netloc.") + LOGGER.debug("Scheme identified in target, ignoring and using netloc.") target_netloc = parsed_target if parsed_target.port is None: ported_target = "%s:%i" % (parsed_target.hostname, default_port) - logging.debug("No target port detected, reassembled to %s.", ported_target) + LOGGER.debug("No target port detected, reassembled to %s.", ported_target) target_netloc = gen_target_netloc(ported_target) return target_netloc @@ -120,11 +124,11 @@ def get_cn_from_cert(cert_pem): cert_cns = cert_parsed.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME) if len(cert_cns) > 0: if len(cert_cns) > 1: - logging.warning( + LOGGER.warning( "Multiple CNs found for certificate, defaulting to the first one." ) cert_cn = cert_cns[0].value - logging.debug("Using %s as certificate CN.", cert_cn) + LOGGER.debug("Using %s as certificate CN.", cert_cn) else: - logging.warning("No CN found for certificate.") + LOGGER.warning("No CN found for certificate.") return cert_cn diff --git a/src/cisco_gnmi/xe.py b/src/cisco_gnmi/xe.py index 6c9e525..ebd469e 100644 --- a/src/cisco_gnmi/xe.py +++ b/src/cisco_gnmi/xe.py @@ -30,6 +30,10 @@ from .client import Client, proto, util +LOGGER = logging.getLogger(__name__) +logger = LOGGER + + class XEClient(Client): """IOS XE-specific wrapper for gNMI functionality. Assumes IOS XE 16.12+ @@ -108,7 +112,13 @@ def delete_xpaths(self, xpaths, prefix=None): paths.append(self.parse_xpath_to_gnmi_path(xpath)) return self.set(deletes=paths) - def set_json(self, update_json_configs=None, replace_json_configs=None, ietf=True): + def set_json( + self, + update_json_configs=None, + replace_json_configs=None, + ietf=True, + prefix=None, + ): """A convenience wrapper for set() which assumes JSON payloads and constructs desired messages. All parameters are optional, but at least one must be present. @@ -132,15 +142,15 @@ def set_json(self, update_json_configs=None, replace_json_configs=None, ietf=Tru raise Exception("Must supply at least one set of configurations to method!") def check_configs(name, configs): - if isinstance(name, string_types): - logging.debug("Handling %s as JSON string.", name) + if isinstance(configs, string_types): + LOGGER.debug("Handling %s as JSON string.", name) try: configs = json.loads(configs) except: raise Exception("{name} is invalid JSON!".format(name=name)) configs = [configs] - elif isinstance(name, dict): - logging.debug("Handling %s as already serialized JSON object.", name) + elif isinstance(configs, dict): + LOGGER.debug("Handling %s as already serialized JSON object.", name) configs = [configs] elif not isinstance(configs, (list, set)): raise Exception( @@ -171,7 +181,7 @@ def create_updates(name, configs): updates = create_updates("update_json_configs", update_json_configs) replaces = create_updates("replace_json_configs", replace_json_configs) - return self.set(updates=updates, replaces=replaces) + return self.set(prefix=prefix, updates=updates, replaces=replaces) def get_xpaths(self, xpaths, data_type="ALL", encoding="JSON_IETF"): """A convenience wrapper for get() which forms proto.gnmi_pb2.Path from supplied xpaths. diff --git a/src/cisco_gnmi/xr.py b/src/cisco_gnmi/xr.py index f1bc809..e7cfb4d 100644 --- a/src/cisco_gnmi/xr.py +++ b/src/cisco_gnmi/xr.py @@ -30,6 +30,10 @@ from .client import Client, proto, util +LOGGER = logging.getLogger(__name__) +logger = LOGGER + + class XRClient(Client): """IOS XR-specific wrapper for gNMI functionality. @@ -130,14 +134,14 @@ def set_json(self, update_json_configs=None, replace_json_configs=None, ietf=Tru def check_configs(name, configs): if isinstance(name, string_types): - logging.debug("Handling %s as JSON string.", name) + LOGGER.debug("Handling %s as JSON string.", name) try: configs = json.loads(configs) except: raise Exception("{name} is invalid JSON!".format(name=name)) configs = [configs] elif isinstance(name, dict): - logging.debug("Handling %s as already serialized JSON object.", name) + LOGGER.debug("Handling %s as already serialized JSON object.", name) configs = [configs] elif not isinstance(configs, (list, set)): raise Exception(