From de7bf4eb09b09af340fa60ffb3a9fb23acb6753a Mon Sep 17 00:00:00 2001 From: "Jochum Jonas (ETAS-DAP/XPC-Fe8)" Date: Mon, 23 Sep 2024 09:23:53 +0200 Subject: [PATCH 1/2] Work around gRPC issue. Threads are not joined properly and cause an exception on the Python side. The connection termination is usually okay and shouldn't crash scripts. --- pygnmi/client.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pygnmi/client.py b/pygnmi/client.py index f8bae4d..69ada8f 100644 --- a/pygnmi/client.py +++ b/pygnmi/client.py @@ -1109,6 +1109,12 @@ def enqueue_updates(): self._updates.put(update) except Exception as error: self.error = error + + # The connection was terminated by the server. This is generally okay and + # shouldn't raise an exception. + if isinstance(error, grpc._channel._MultiThreadedRendezvous) and error.code() == grpc.StatusCode.CANCELLED: + return + raise error self._subscribe_thread = threading.Thread(target=enqueue_updates) From 108e7a2a9dafc398ca8bf3beb0705911397bd41b Mon Sep 17 00:00:00 2001 From: "Jochum Jonas (ETAS-DAP/XPC-Fe8)" Date: Mon, 23 Sep 2024 10:53:27 +0200 Subject: [PATCH 2/2] Implement parsing a leaflist containing TypedValues. All elements must have the same type. This was also added to the get() method, since leaflists weren't supported at all. --- pygnmi/client.py | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/pygnmi/client.py b/pygnmi/client.py index 69ada8f..9da7090 100644 --- a/pygnmi/client.py +++ b/pygnmi/client.py @@ -581,6 +581,27 @@ def get( elif update_msg.val.HasField("proto_bytes"): update_container.update({"val": update_msg.val.proto_bytes}) + elif update_msg.val.HasField("leaflist_val"): + val_leaflist = update_msg.val + element_val = None + if all([isinstance(e, TypedValue) for e in val_leaflist.leaflist_val.element]): + element_val = {} + for e in val_leaflist.leaflist_val.element: + if hasattr(e, "json_val"): + element_val.update(json.loads(e.json_val)) + elif hasattr(e, "json_ietf_val"): + element_val.update(json.loads(e.json_ietf_val)) + else: + raise TypeError(f"Neither json_val nor json_ietf_val found in element {e}.") + elif all([isinstance(e, str) for e in val_leaflist.leaflist_val.element]): + element_val = "" + for e in val_leaflist.leaflist_val.element: + element_val += e + else: + raise Exception("leaflist elements have differing types. Only str and TypedValue are supported.") + + update_container.update({"val": element_val}) + notification_container["update"].append(update_container) response["notification"].append(notification_container) @@ -1384,10 +1405,24 @@ def telemetryParser(in_message=None, debug: bool = False): elif update_msg.val.HasField("leaflist_val"): val_leaflist = update_msg.val - element_str = "" - for element in val_leaflist.leaflist_val.element: - element_str += element - update_container.update({"val": element_str}) + element_val = None + if all([isinstance(e, TypedValue) for e in val_leaflist.leaflist_val.element]): + element_val = {} + for e in val_leaflist.leaflist_val.element: + if hasattr(e, "json_val"): + element_val.update(json.loads(e.json_val)) + elif hasattr(e, "json_ietf_val"): + element_val.update(json.loads(e.json_ietf_val)) + else: + raise TypeError(f"Neither json_val nor json_ietf_val found in element {e}.") + elif all([isinstance(e, str) for e in val_leaflist.leaflist_val.element]): + element_val = "" + for e in val_leaflist.leaflist_val.element: + element_val += e + else: + raise Exception("leaflist elements have differing types. Only str and TypedValue are supported.") + + update_container.update({"val": element_val}) response["update"]["update"].append(update_container) @@ -1419,8 +1454,8 @@ def telemetryParser(in_message=None, debug: bool = False): return response - except: - logger.error(f"Parsing of telemetry information is failed.") + except Exception as exc: + logger.error(f"Parsing of telemetry information is failed: {exc}") return None