def _create_delete_paths(self) -> List[Path]: paths: List[Path] = [] if not self.ascii_mode: for path in self._features.keys(): if not path == "": paths.append(create_gnmi_path(path)) return paths
def subscribe( self, encoding: str, requests: List[str], sample_rate: int, stream_mode: str, subscribe_mode: str, ) -> Iterable[ParsedResponse]: """Subscribe to sensor path(s) and poll them at a given interval on a gNMI device :param encoding: The encoding to use when you subscribe to a gNMI device :type encoding: str :param requests: A list of sensor path(s) to subscribe to :type requests: List[str] :param sample_rate: How often to poll the subscription in seconds :type sample_rate: int :param stream_mode: The way to stream off the data either STREAM, ONCE, POLL :type stream_mode: str :param subscribe_mode: Either can be SAMPLE or ON_CHANGE to either do MDT at a sample interval or EDT :type subscribe_mode: str :returns: An iterable of ParsedResponse of the streaming data """ subs = [] sample_rate = sample_rate * 1000000000 for request in requests: subs.append( Subscription( path=create_gnmi_path(request), mode=SubscriptionMode.Value(subscribe_mode), sample_interval=sample_rate, ) ) sub_list = SubscriptionList( subscription=subs, mode=SubscriptionList.Mode.Value(stream_mode), encoding=Encoding.Value(encoding), ) sub_request = SubscribeRequest(subscribe=sub_list) try: stub = self._get_stub() for response in stub.Subscribe(self.sub_to_path(sub_request), metadata=self.metadata): if not response.sync_response: keys, start_yang_path = self.process_header(response.update) for update in response.update.update: update_keys, update_yang_path = self.process_update_header(update) total_yang_path = f"{start_yang_path}/{update_yang_path}" value = self.get_value(update.val) parsed_dict = { "@timestamp": (int(response.update.timestamp) / 1000000), "byte_size": response.ByteSize(), "ip": self.host, } keys.update(update_keys) parsed_dict["keys"] = keys leaf = "-".join(total_yang_path.split("/")[-2:]) parsed_dict[leaf] = value parsed_dict["index"] = yang_path_to_es_index(total_yang_path) parsed_dict["yang_path"] = total_yang_path yield ParsedResponse(parsed_dict, self.version, self.hostname) except Exception as e: raise GNMIException(f"Failed to complete Subscription:\n {e}")
def _split_full_config(response: GetResponse) -> List[GetResponse]: responses: List[GetResponse] = [] full_config_json: Dict[str, Any] = {} timestamp = [n.timestamp for n in response.notification][0] for notification in response.notification: for update in notification.update: full_config_json = json.loads(update.val.json_ietf_val) models: List[str] = [] for model, config in full_config_json.items(): str_config: str = json.dumps(config) type_config_val: TypedValue = TypedValue(json_ietf_val=str_config.encode()) up: Update = Update(path=create_gnmi_path(model), val=type_config_val) notification: Notification = Notification(update=[up], timestamp=timestamp) responses.append(GetResponse(notification=[notification])) models.append(model) model_str_config: str = json.dumps({"configs": models}) model_type_config_val: TypedValue = TypedValue(json_ietf_val=model_str_config.encode()) up: Update = Update(path=create_gnmi_path("router-configs"), val=model_type_config_val) notification: Notification = Notification(update=[up], timestamp=timestamp) responses.append(GetResponse(notification=[notification])) return responses
def get_config(self, encoding: str, config_models: List[str] = None, raw: bool = False) -> List[ParsedResponse]: """Get configuration of the gNMI device :param encoding: The encoding to use to for the Get Config operation :type encoding: str :param config_models: Yang model(s) of a specific configuration to get :type config_models: str :returns: A List of ParsedResponse of configuration data """ try: stub: gNMIStub = self._get_stub() responses: List[ParsedResponse] = [] if config_models: for config_model in config_models: split_full_config_response = [] get_message: GetRequest = GetRequest( path=[create_gnmi_path(config_model)], type=GetRequest.DataType.Value("CONFIG"), encoding=Encoding.Value(encoding), ) response: GetResponse = stub.Get(get_message, metadata=self.metadata) if raw: return response else: split_full_config_response.append(response) else: get_message: GetRequest = GetRequest( path=[Path()], type=GetRequest.DataType.Value("CONFIG"), encoding=Encoding.Value(encoding), ) full_config_response: GetResponse = stub.Get(get_message, metadata=self.metadata) if raw: return full_config_response else: split_full_config_response: List[Dict[str, Any]] = self._split_full_config(full_config_response) for response in split_full_config_response: parsed_dict: Dict[str, Any] = { "@timestamp": (int(response.notification[0].timestamp) / 1000000), "byte_size": response.ByteSize(), } model = response.notification[0].update[0].path.elem[0].name parsed_dict["model"] = model parsed_dict["index"] = yang_path_to_es_index(model) parsed_dict["ip"] = self.host parsed_dict["config"] = json.loads(response.notification[0].update[0].val.json_ietf_val) responses.append(ParsedResponse(parsed_dict, self.version, self.hostname)) return responses except Exception as e: raise GNMIException(f"Failed to complete the Get Config:\n {e}")
def _get_version(self) -> str: stub = self._get_stub() get_message: GetRequest = GetRequest( path=[create_gnmi_path("Cisco-IOS-XR-install-oper:install/version")], type=GetRequest.DataType.Value("STATE"), encoding=Encoding.Value("JSON_IETF") ) response: GetResponse = stub.Get(get_message, metadata=self.metadata) def _parse_version(version: GetResponse) -> str: rc = "" for notification in version.notification: for update in notification.update: rc = json.loads(update.val.json_ietf_val) return rc["package"][0]["version"] return _parse_version(response)
def _get_hostname(self) -> str: stub = self._get_stub() get_message: GetRequest = GetRequest( path=[create_gnmi_path("Cisco-IOS-XR-shellutil-cfg:host-names")], type=GetRequest.DataType.Value("CONFIG"), encoding=Encoding.Value("JSON_IETF"), ) response: GetResponse = stub.Get(get_message, metadata=self.metadata) def _parse_hostname(hostname_response: GetResponse) -> str: for notification in hostname_response.notification: for update in notification.update: rc = update.val.json_ietf_val if not rc: return "" else: return json.loads(rc)["host-name"] return _parse_hostname(response)
def _create_updates(self) -> List[Update]: updates: List[Update] = [] if self.ascii_mode: type_config_val: TypedValue = TypedValue( ascii_val=self.ascii_config) updates.append(Update(path=self.path, val=type_config_val)) return updates else: for path, config in self._features.items(): str_config: str = json.dumps(config) type_config_val: TypedValue = TypedValue( json_ietf_val=str_config.encode()) if path == "": updates.append(Update(path=Path(), val=type_config_val)) else: updates.append( Update(path=create_gnmi_path(path), val=type_config_val)) return updates
def get(self, encoding: str, oper_models: List[str], raw: bool = False) -> List[ParsedResponse]: """Get oper data of a gNMI device :param encoding: The encoding to use to for the Get operation :type encoding: str :param oper_models: The yang model of the operational data to get :type oper_models: List[str] :returns: A list of ParsedResponse of the flatten operational data """ try: stub: gNMIStub = self._get_stub() paths: List[Path] = [] for oper_model in oper_models: paths.append(create_gnmi_path(oper_model)) get_message: GetRequest = GetRequest( path=paths, type=GetRequest.DataType.Value("OPERATIONAL"), encoding=Encoding.Value(encoding), ) response: GetResponse = stub.Get(get_message, metadata=self.metadata) if raw: return response else: rc: List[ParsedResponse] = [] for notification in response.notification: start_yang_path: List[str] = [] start_yang_keys: Dict[str, str] = {} sub_yang_path: List[str] = [] sub_yang_info: List[Dict[str, Any]] = [] for update in notification.update: for elem in update.path.elem: start_yang_path.append(elem.name) if elem.key: for key, value in elem.key.items(): if isinstance(value, str): start_yang_keys[key] = value.replace('"', "").replace("'", "") else: start_yang_keys[key] = value keywords = self.yang_keywords[start_yang_path[0].split(":")[0]]["keys"] start_yang_path_str: str = "/".join(start_yang_path) response_value: Any = self.get_value(update.val) if update.val.WhichOneof("value") in ["json_val", "json_ietf_val"]: if response_value == "": rc.append(ParsedResponse({}, self.version, self.hostname)) return rc if isinstance(response_value, list): for sub_response_value in response_value: for key, value in sub_response_value.items(): self._walk_yang_data( sub_yang_path, key, value, keywords, start_yang_keys, sub_yang_info, ) else: for key, value in response_value.items(): self._walk_yang_data( sub_yang_path, key, value, keywords, start_yang_keys, sub_yang_info, ) for sub_yang in sub_yang_info: parsed_dict = { "@timestamp": (int(notification.timestamp) / 1000000), "byte_size": response.ByteSize(), "keys": sub_yang["keys"], } yang_path = sub_yang["yang_path"] parsed_dict["yang_path"] = f"{start_yang_path_str}/{yang_path}" leaf = "-".join(parsed_dict["yang_path"].split("/")[-2:]) parsed_dict[leaf] = sub_yang["value"] parsed_dict["ip"] = self.host parsed_dict["index"] = yang_path_to_es_index(parsed_dict["yang_path"]) rc.append(ParsedResponse(parsed_dict, self.version, self.hostname)) else: raise GNMIException("Unsupported Get encoding") return rc except Exception as error: raise GNMIException(f"Failed to complete the Get:\n {error}")