From 2585cd8db05bdfad4dd32617d9c5c249b588cdf8 Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Mon, 30 Mar 2026 16:55:13 +0000 Subject: [PATCH 1/4] [sonic-py-common] Add gRPC framework with gNOI client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sonic_py_common.grpc — a reusable gRPC framework for SONiC Python components, starting with gNOI support. Structure: sonic_py_common/ grpc/ # gRPC framework root __init__.py # future: gnmi, gribi sub-packages gnoi/ # gNOI services __init__.py client.py # GnoiClient - channel manager + service stubs system_pb2.py # vendored proto stubs (openconfig/gnoi) system_pb2_grpc.py types_pb2.py types_pb2_grpc.py common_pb2.py common_pb2_grpc.py GnoiClient is service-agnostic: stubs accessed via properties (client.system, with scaffolding for healthz/cert/file/os). The grpc/ namespace leaves room for gnmi/ and gribi/ sub-packages. Dependencies: grpcio, protobuf (already used by other SONiC components). Signed-off-by: sigabrtv1-ui Signed-off-by: Dawei Huang --- src/sonic-py-common/setup.py | 7 +- .../sonic_py_common/grpc/__init__.py | 10 + .../sonic_py_common/grpc/gnoi/__init__.py | 19 + .../sonic_py_common/grpc/gnoi/client.py | 89 ++++ .../sonic_py_common/grpc/gnoi/common_pb2.py | 40 ++ .../grpc/gnoi/common_pb2_grpc.py | 24 + .../sonic_py_common/grpc/gnoi/system_pb2.py | 95 ++++ .../grpc/gnoi/system_pb2_grpc.py | 480 ++++++++++++++++++ .../sonic_py_common/grpc/gnoi/types_pb2.py | 52 ++ .../grpc/gnoi/types_pb2_grpc.py | 24 + src/sonic-py-common/tests/test_gnoi_client.py | 93 ++++ 11 files changed, 932 insertions(+), 1 deletion(-) create mode 100644 src/sonic-py-common/sonic_py_common/grpc/__init__.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/__init__.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/client.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py create mode 100644 src/sonic-py-common/tests/test_gnoi_client.py diff --git a/src/sonic-py-common/setup.py b/src/sonic-py-common/setup.py index d4ae6853641..6597d409436 100644 --- a/src/sonic-py-common/setup.py +++ b/src/sonic-py-common/setup.py @@ -36,9 +36,14 @@ url='https://github.com/Azure/SONiC', maintainer='Joe LeVeque', maintainer_email='jolevequ@microsoft.com', - install_requires=dependencies, + install_requires=dependencies + [ + 'grpcio', + 'protobuf', + ], packages=[ 'sonic_py_common', + 'sonic_py_common.grpc', + 'sonic_py_common.grpc.gnoi', ], setup_requires= [ 'pytest-runner', diff --git a/src/sonic-py-common/sonic_py_common/grpc/__init__.py b/src/sonic-py-common/sonic_py_common/grpc/__init__.py new file mode 100644 index 00000000000..9b39e0b0318 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/__init__.py @@ -0,0 +1,10 @@ +""" +sonic_py_common.grpc - gRPC framework for SONiC. + +Sub-packages: + - gnoi: gNOI service stubs and client (System, Healthz, Cert, etc.) + +Future: + - gnmi: gNMI service stubs and client + - gribi: gRIBI service stubs and client +""" diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/__init__.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/__init__.py new file mode 100644 index 00000000000..f1c599ef44b --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/__init__.py @@ -0,0 +1,19 @@ +""" +sonic_py_common.grpc.gnoi - gNOI Python framework for SONiC. + +Provides: + - Vendored gNOI proto stubs (System, types, common) + - GnoiClient: gRPC channel manager with service stub access + +Usage: + from sonic_py_common.grpc.gnoi import GnoiClient + from sonic_py_common.grpc.gnoi import system_pb2 + + with GnoiClient("10.0.0.1:8080") as client: + request = system_pb2.RebootRequest(method=system_pb2.HALT) + client.system.Reboot(request, timeout=60) +""" + +from sonic_py_common.grpc.gnoi.client import GnoiClient + +__all__ = ['GnoiClient'] diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/client.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/client.py new file mode 100644 index 00000000000..0cb7de934e6 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/client.py @@ -0,0 +1,89 @@ +""" +gNOI gRPC client for SONiC. + +Manages a gRPC channel and provides access to gNOI service stubs. +All RPCs are accessed through service properties: + + with GnoiClient("10.0.0.1:8080") as client: + client.system.Reboot(request, timeout=60) + client.system.RebootStatus(request, timeout=10) + +The client is service-agnostic — new gNOI services (Healthz, Cert, +File, OS, etc.) can be added as properties without modifying existing +code. +""" + +import grpc +from sonic_py_common.grpc.gnoi import system_pb2_grpc + + +class GnoiClient: + """gNOI gRPC client for SONiC components. + + Manages a single insecure gRPC channel and exposes gNOI service stubs + as properties. Use as a context manager for automatic cleanup. + + Args: + target: gRPC target address, e.g. "10.0.0.1:8080". + options: Optional list of gRPC channel options. + + Example: + with GnoiClient("10.0.0.1:8080") as client: + req = system_pb2.RebootRequest(method=system_pb2.HALT) + client.system.Reboot(req, timeout=60) + """ + + def __init__(self, target, options=None): + self._target = target + self._options = options + self._channel = None + + def __enter__(self): + self._channel = grpc.insecure_channel(self._target, options=self._options) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + return False + + def close(self): + """Close the gRPC channel.""" + if self._channel: + self._channel.close() + self._channel = None + + @property + def channel(self): + """The underlying gRPC channel for constructing custom stubs.""" + return self._channel + + # ---- gNOI service stubs ---- + + @property + def system(self): + """gNOI System service stub (gnoi.system.System). + + Provides: Reboot, RebootStatus, CancelReboot, Time, Ping, + Traceroute, SwitchControlProcessor, etc. + """ + return system_pb2_grpc.SystemStub(self._channel) + + # Future services can be added here: + # + # @property + # def healthz(self): + # """gNOI Healthz service stub.""" + # from sonic_py_common.grpc.gnoi import healthz_pb2_grpc + # return healthz_pb2_grpc.HealthzStub(self._channel) + # + # @property + # def cert(self): + # """gNOI CertificateManagement service stub.""" + # from sonic_py_common.grpc.gnoi import cert_pb2_grpc + # return cert_pb2_grpc.CertificateManagementStub(self._channel) + # + # @property + # def file(self): + # """gNOI File service stub.""" + # from sonic_py_common.grpc.gnoi import file_pb2_grpc + # return file_pb2_grpc.FileStub(self._channel) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py new file mode 100644 index 00000000000..4397ea9640c --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: github.com/openconfig/gnoi/common/common.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'github.com/openconfig/gnoi/common/common.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from sonic_py_common.grpc.gnoi import types_pb2 as github_dot_com_dot_openconfig_dot_gnoi_dot_types_dot_types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n.github.com/openconfig/gnoi/common/common.proto\x12\x0bgnoi.common\x1a,github.com/openconfig/gnoi/types/types.proto\"\xf1\x01\n\x0eRemoteDownload\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x36\n\x08protocol\x18\x02 \x01(\x0e\x32$.gnoi.common.RemoteDownload.Protocol\x12,\n\x0b\x63redentials\x18\x03 \x01(\x0b\x32\x17.gnoi.types.Credentials\x12\x16\n\x0esource_address\x18\x04 \x01(\t\x12\x12\n\nsource_vrf\x18\x05 \x01(\t\"?\n\x08Protocol\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04SFTP\x10\x01\x12\x08\n\x04HTTP\x10\x02\x12\t\n\x05HTTPS\x10\x03\x12\x07\n\x03SCP\x10\x04\x42#Z!github.com/openconfig/gnoi/commonb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.openconfig.gnoi.common.common_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z!github.com/openconfig/gnoi/common' + _globals['_REMOTEDOWNLOAD']._serialized_start=110 + _globals['_REMOTEDOWNLOAD']._serialized_end=351 + _globals['_REMOTEDOWNLOAD_PROTOCOL']._serialized_start=288 + _globals['_REMOTEDOWNLOAD_PROTOCOL']._serialized_end=351 +# @@protoc_insertion_point(module_scope) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py new file mode 100644 index 00000000000..b2660b454d8 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.80.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in github.com/openconfig/gnoi/common/common_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py new file mode 100644 index 00000000000..3fcb2cc9af6 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: github.com/openconfig/gnoi/system/system.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'github.com/openconfig/gnoi/system/system.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from sonic_py_common.grpc.gnoi import common_pb2 as github_dot_com_dot_openconfig_dot_gnoi_dot_common_dot_common__pb2 +from sonic_py_common.grpc.gnoi import types_pb2 as github_dot_com_dot_openconfig_dot_gnoi_dot_types_dot_types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n.github.com/openconfig/gnoi/system/system.proto\x12\x0bgnoi.system\x1a.github.com/openconfig/gnoi/common/common.proto\x1a,github.com/openconfig/gnoi/types/types.proto\"L\n\x1dSwitchControlProcessorRequest\x12+\n\x11\x63ontrol_processor\x18\x01 \x01(\x0b\x32\x10.gnoi.types.Path\"n\n\x1eSwitchControlProcessorResponse\x12+\n\x11\x63ontrol_processor\x18\x01 \x01(\x0b\x32\x10.gnoi.types.Path\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0e\n\x06uptime\x18\x03 \x01(\x03\"\x92\x01\n\rRebootRequest\x12)\n\x06method\x18\x01 \x01(\x0e\x32\x19.gnoi.system.RebootMethod\x12\r\n\x05\x64\x65lay\x18\x02 \x01(\x04\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\'\n\rsubcomponents\x18\x04 \x03(\x0b\x32\x10.gnoi.types.Path\x12\r\n\x05\x66orce\x18\x05 \x01(\x08\"\x10\n\x0eRebootResponse\"O\n\x13\x43\x61ncelRebootRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\'\n\rsubcomponents\x18\x02 \x03(\x0b\x32\x10.gnoi.types.Path\"\x16\n\x14\x43\x61ncelRebootResponse\">\n\x13RebootStatusRequest\x12\'\n\rsubcomponents\x18\x01 \x03(\x0b\x32\x10.gnoi.types.Path\"\xb7\x01\n\x14RebootStatusResponse\x12\x0e\n\x06\x61\x63tive\x18\x01 \x01(\x08\x12\x0c\n\x04wait\x18\x02 \x01(\x04\x12\x0c\n\x04when\x18\x03 \x01(\x04\x12\x0e\n\x06reason\x18\x04 \x01(\t\x12\r\n\x05\x63ount\x18\x05 \x01(\r\x12)\n\x06method\x18\x06 \x01(\x0e\x32\x19.gnoi.system.RebootMethod\x12)\n\x06status\x18\x07 \x01(\x0b\x32\x19.gnoi.system.RebootStatus\"\xb5\x01\n\x0cRebootStatus\x12\x30\n\x06status\x18\x01 \x01(\x0e\x32 .gnoi.system.RebootStatus.Status\x12\x0f\n\x07message\x18\x02 \x01(\t\"b\n\x06Status\x12\x12\n\x0eSTATUS_UNKNOWN\x10\x00\x12\x12\n\x0eSTATUS_SUCCESS\x10\x01\x12\x1c\n\x18STATUS_RETRIABLE_FAILURE\x10\x02\x12\x12\n\x0eSTATUS_FAILURE\x10\x03\"\r\n\x0bTimeRequest\"\x1c\n\x0cTimeResponse\x12\x0c\n\x04time\x18\x01 \x01(\x04\"\xe6\x01\n\x0bPingRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x05\x12\x10\n\x08interval\x18\x04 \x01(\x03\x12\x0c\n\x04wait\x18\x05 \x01(\x03\x12\x0c\n\x04size\x18\x06 \x01(\x05\x12\x17\n\x0f\x64o_not_fragment\x18\x07 \x01(\x08\x12\x16\n\x0e\x64o_not_resolve\x18\x08 \x01(\x08\x12*\n\nl3protocol\x18\t \x01(\x0e\x32\x16.gnoi.types.L3Protocol\x12\x18\n\x10network_instance\x18\n \x01(\t\"\xc1\x01\n\x0cPingResponse\x12\x0e\n\x06source\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x03\x12\x0c\n\x04sent\x18\x03 \x01(\x05\x12\x10\n\x08received\x18\x04 \x01(\x05\x12\x10\n\x08min_time\x18\x05 \x01(\x03\x12\x10\n\x08\x61vg_time\x18\x06 \x01(\x03\x12\x10\n\x08max_time\x18\x07 \x01(\x03\x12\x0f\n\x07std_dev\x18\x08 \x01(\x03\x12\r\n\x05\x62ytes\x18\x0b \x01(\x05\x12\x10\n\x08sequence\x18\x0c \x01(\x05\x12\x0b\n\x03ttl\x18\r \x01(\x05\"\xe7\x02\n\x11TracerouteRequest\x12\x0e\n\x06source\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\x12\x13\n\x0binitial_ttl\x18\x03 \x01(\r\x12\x0f\n\x07max_ttl\x18\x04 \x01(\x05\x12\x0c\n\x04wait\x18\x05 \x01(\x03\x12\x17\n\x0f\x64o_not_fragment\x18\x06 \x01(\x08\x12\x16\n\x0e\x64o_not_resolve\x18\x07 \x01(\x08\x12*\n\nl3protocol\x18\x08 \x01(\x0e\x32\x16.gnoi.types.L3Protocol\x12=\n\nl4protocol\x18\t \x01(\x0e\x32).gnoi.system.TracerouteRequest.L4Protocol\x12\x19\n\x11\x64o_not_lookup_asn\x18\n \x01(\x08\x12\x18\n\x10network_instance\x18\x0b \x01(\t\"(\n\nL4Protocol\x12\x08\n\x04ICMP\x10\x00\x12\x07\n\x03TCP\x10\x01\x12\x07\n\x03UDP\x10\x02\"\xda\x05\n\x12TracerouteResponse\x12\x18\n\x10\x64\x65stination_name\x18\x01 \x01(\t\x12\x1b\n\x13\x64\x65stination_address\x18\x02 \x01(\t\x12\x0c\n\x04hops\x18\x03 \x01(\x05\x12\x13\n\x0bpacket_size\x18\x04 \x01(\x05\x12\x0b\n\x03hop\x18\x05 \x01(\x05\x12\x0f\n\x07\x61\x64\x64ress\x18\x06 \x01(\t\x12\x0c\n\x04name\x18\x07 \x01(\t\x12\x0b\n\x03rtt\x18\x08 \x01(\x03\x12\x34\n\x05state\x18\t \x01(\x0e\x32%.gnoi.system.TracerouteResponse.State\x12\x11\n\ticmp_code\x18\n \x01(\x05\x12\x37\n\x04mpls\x18\x0b \x03(\x0b\x32).gnoi.system.TracerouteResponse.MplsEntry\x12\x0f\n\x07\x61s_path\x18\x0c \x03(\x05\x12\x42\n\ricmp_ext_data\x18\r \x03(\x0b\x32+.gnoi.system.TracerouteResponse.IcmpExtData\x1a\x38\n\x0bIcmpExtData\x12\r\n\x05\x63lass\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x03 \x03(\r\x1a+\n\tMplsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf2\x01\n\x05State\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x08\n\x04NONE\x10\x01\x12\x0b\n\x07UNKNOWN\x10\x02\x12\x08\n\x04ICMP\x10\x03\x12\x14\n\x10HOST_UNREACHABLE\x10\x04\x12\x17\n\x13NETWORK_UNREACHABLE\x10\x05\x12\x18\n\x14PROTOCOL_UNREACHABLE\x10\x06\x12\x17\n\x13SOURCE_ROUTE_FAILED\x10\x07\x12\x18\n\x14\x46RAGMENTATION_NEEDED\x10\x08\x12\x0e\n\nPROHIBITED\x10\t\x12\x18\n\x14PRECEDENCE_VIOLATION\x10\n\x12\x15\n\x11PRECEDENCE_CUTOFF\x10\x0b\"t\n\x07Package\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x10\n\x08\x61\x63tivate\x18\x05 \x01(\x08\x12\x34\n\x0fremote_download\x18\x06 \x01(\x0b\x32\x1b.gnoi.common.RemoteDownload\"\x81\x01\n\x11SetPackageRequest\x12\'\n\x07package\x18\x01 \x01(\x0b\x32\x14.gnoi.system.PackageH\x00\x12\x12\n\x08\x63ontents\x18\x02 \x01(\x0cH\x00\x12$\n\x04hash\x18\x03 \x01(\x0b\x32\x14.gnoi.types.HashTypeH\x00\x42\t\n\x07request\"\x14\n\x12SetPackageResponse\"\xdd\x01\n\x12KillProcessRequest\x12\x0b\n\x03pid\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x36\n\x06signal\x18\x03 \x01(\x0e\x32&.gnoi.system.KillProcessRequest.Signal\x12\x0f\n\x07restart\x18\x04 \x01(\x08\"c\n\x06Signal\x12\x16\n\x12SIGNAL_UNSPECIFIED\x10\x00\x12\x0f\n\x0bSIGNAL_TERM\x10\x01\x12\x0f\n\x0bSIGNAL_KILL\x10\x02\x12\x0e\n\nSIGNAL_HUP\x10\x03\x12\x0f\n\x0bSIGNAL_ABRT\x10\x04\"\x15\n\x13KillProcessResponse*d\n\x0cRebootMethod\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04\x43OLD\x10\x01\x12\r\n\tPOWERDOWN\x10\x02\x12\x08\n\x04HALT\x10\x03\x12\x08\n\x04WARM\x10\x04\x12\x07\n\x03NSF\x10\x05\x12\x0b\n\x07POWERUP\x10\x07\"\x04\x08\x06\x10\x06\x32\xea\x05\n\x06System\x12?\n\x04Ping\x12\x18.gnoi.system.PingRequest\x1a\x19.gnoi.system.PingResponse\"\x00\x30\x01\x12Q\n\nTraceroute\x12\x1e.gnoi.system.TracerouteRequest\x1a\x1f.gnoi.system.TracerouteResponse\"\x00\x30\x01\x12=\n\x04Time\x12\x18.gnoi.system.TimeRequest\x1a\x19.gnoi.system.TimeResponse\"\x00\x12Q\n\nSetPackage\x12\x1e.gnoi.system.SetPackageRequest\x1a\x1f.gnoi.system.SetPackageResponse\"\x00(\x01\x12s\n\x16SwitchControlProcessor\x12*.gnoi.system.SwitchControlProcessorRequest\x1a+.gnoi.system.SwitchControlProcessorResponse\"\x00\x12\x43\n\x06Reboot\x12\x1a.gnoi.system.RebootRequest\x1a\x1b.gnoi.system.RebootResponse\"\x00\x12U\n\x0cRebootStatus\x12 .gnoi.system.RebootStatusRequest\x1a!.gnoi.system.RebootStatusResponse\"\x00\x12U\n\x0c\x43\x61ncelReboot\x12 .gnoi.system.CancelRebootRequest\x1a!.gnoi.system.CancelRebootResponse\"\x00\x12R\n\x0bKillProcess\x12\x1f.gnoi.system.KillProcessRequest\x1a .gnoi.system.KillProcessResponse\"\x00\x42+Z!github.com/openconfig/gnoi/system\xd2>\x05\x31.4.0b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.openconfig.gnoi.system.system_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z!github.com/openconfig/gnoi/system\322>\0051.4.0' + _globals['_TRACEROUTERESPONSE_MPLSENTRY']._loaded_options = None + _globals['_TRACEROUTERESPONSE_MPLSENTRY']._serialized_options = b'8\001' + _globals['_REBOOTMETHOD']._serialized_start=3141 + _globals['_REBOOTMETHOD']._serialized_end=3241 + _globals['_SWITCHCONTROLPROCESSORREQUEST']._serialized_start=157 + _globals['_SWITCHCONTROLPROCESSORREQUEST']._serialized_end=233 + _globals['_SWITCHCONTROLPROCESSORRESPONSE']._serialized_start=235 + _globals['_SWITCHCONTROLPROCESSORRESPONSE']._serialized_end=345 + _globals['_REBOOTREQUEST']._serialized_start=348 + _globals['_REBOOTREQUEST']._serialized_end=494 + _globals['_REBOOTRESPONSE']._serialized_start=496 + _globals['_REBOOTRESPONSE']._serialized_end=512 + _globals['_CANCELREBOOTREQUEST']._serialized_start=514 + _globals['_CANCELREBOOTREQUEST']._serialized_end=593 + _globals['_CANCELREBOOTRESPONSE']._serialized_start=595 + _globals['_CANCELREBOOTRESPONSE']._serialized_end=617 + _globals['_REBOOTSTATUSREQUEST']._serialized_start=619 + _globals['_REBOOTSTATUSREQUEST']._serialized_end=681 + _globals['_REBOOTSTATUSRESPONSE']._serialized_start=684 + _globals['_REBOOTSTATUSRESPONSE']._serialized_end=867 + _globals['_REBOOTSTATUS']._serialized_start=870 + _globals['_REBOOTSTATUS']._serialized_end=1051 + _globals['_REBOOTSTATUS_STATUS']._serialized_start=953 + _globals['_REBOOTSTATUS_STATUS']._serialized_end=1051 + _globals['_TIMEREQUEST']._serialized_start=1053 + _globals['_TIMEREQUEST']._serialized_end=1066 + _globals['_TIMERESPONSE']._serialized_start=1068 + _globals['_TIMERESPONSE']._serialized_end=1096 + _globals['_PINGREQUEST']._serialized_start=1099 + _globals['_PINGREQUEST']._serialized_end=1329 + _globals['_PINGRESPONSE']._serialized_start=1332 + _globals['_PINGRESPONSE']._serialized_end=1525 + _globals['_TRACEROUTEREQUEST']._serialized_start=1528 + _globals['_TRACEROUTEREQUEST']._serialized_end=1887 + _globals['_TRACEROUTEREQUEST_L4PROTOCOL']._serialized_start=1847 + _globals['_TRACEROUTEREQUEST_L4PROTOCOL']._serialized_end=1887 + _globals['_TRACEROUTERESPONSE']._serialized_start=1890 + _globals['_TRACEROUTERESPONSE']._serialized_end=2620 + _globals['_TRACEROUTERESPONSE_ICMPEXTDATA']._serialized_start=2274 + _globals['_TRACEROUTERESPONSE_ICMPEXTDATA']._serialized_end=2330 + _globals['_TRACEROUTERESPONSE_MPLSENTRY']._serialized_start=2332 + _globals['_TRACEROUTERESPONSE_MPLSENTRY']._serialized_end=2375 + _globals['_TRACEROUTERESPONSE_STATE']._serialized_start=2378 + _globals['_TRACEROUTERESPONSE_STATE']._serialized_end=2620 + _globals['_PACKAGE']._serialized_start=2622 + _globals['_PACKAGE']._serialized_end=2738 + _globals['_SETPACKAGEREQUEST']._serialized_start=2741 + _globals['_SETPACKAGEREQUEST']._serialized_end=2870 + _globals['_SETPACKAGERESPONSE']._serialized_start=2872 + _globals['_SETPACKAGERESPONSE']._serialized_end=2892 + _globals['_KILLPROCESSREQUEST']._serialized_start=2895 + _globals['_KILLPROCESSREQUEST']._serialized_end=3116 + _globals['_KILLPROCESSREQUEST_SIGNAL']._serialized_start=3017 + _globals['_KILLPROCESSREQUEST_SIGNAL']._serialized_end=3116 + _globals['_KILLPROCESSRESPONSE']._serialized_start=3118 + _globals['_KILLPROCESSRESPONSE']._serialized_end=3139 + _globals['_SYSTEM']._serialized_start=3244 + _globals['_SYSTEM']._serialized_end=3990 +# @@protoc_insertion_point(module_scope) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py new file mode 100644 index 00000000000..f2a0c0162f5 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py @@ -0,0 +1,480 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from sonic_py_common.grpc.gnoi import system_pb2 as github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2 + +GRPC_GENERATED_VERSION = '1.80.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in github.com/openconfig/gnoi/system/system_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class SystemStub(object): + """The gNOI service is a collection of operational RPC's that allow for the + management of a target outside of the configuration and telemetry pipeline. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Ping = channel.unary_stream( + '/gnoi.system.System/Ping', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingResponse.FromString, + _registered_method=True) + self.Traceroute = channel.unary_stream( + '/gnoi.system.System/Traceroute', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteResponse.FromString, + _registered_method=True) + self.Time = channel.unary_unary( + '/gnoi.system.System/Time', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeResponse.FromString, + _registered_method=True) + self.SetPackage = channel.stream_unary( + '/gnoi.system.System/SetPackage', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageResponse.FromString, + _registered_method=True) + self.SwitchControlProcessor = channel.unary_unary( + '/gnoi.system.System/SwitchControlProcessor', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorResponse.FromString, + _registered_method=True) + self.Reboot = channel.unary_unary( + '/gnoi.system.System/Reboot', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootResponse.FromString, + _registered_method=True) + self.RebootStatus = channel.unary_unary( + '/gnoi.system.System/RebootStatus', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusResponse.FromString, + _registered_method=True) + self.CancelReboot = channel.unary_unary( + '/gnoi.system.System/CancelReboot', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootResponse.FromString, + _registered_method=True) + self.KillProcess = channel.unary_unary( + '/gnoi.system.System/KillProcess', + request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessRequest.SerializeToString, + response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessResponse.FromString, + _registered_method=True) + + +class SystemServicer(object): + """The gNOI service is a collection of operational RPC's that allow for the + management of a target outside of the configuration and telemetry pipeline. + """ + + def Ping(self, request, context): + """Ping executes the ping command on the target and streams back + the results. Some targets may not stream any results until all + results are in. The stream should provide single ping packet responses + and must provide summary statistics. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Traceroute(self, request, context): + """Traceroute executes the traceroute command on the target and streams back + the results. Some targets may not stream any results until all + results are in. If a hop count is not explicitly provided, + 30 is used. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Time(self, request, context): + """Time returns the current time on the target. Time is typically used to + test if a target is actually responding. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SetPackage(self, request_iterator, context): + """SetPackage places a software package (possibly including bootable images) + on the target. The file is sent in sequential messages, each message + up to 64KB of data. A final message must be sent that includes the hash + of the data sent. An error is returned if the location does not exist or + there is an error writing the data. If no checksum is received, the target + must assume the operation is incomplete and remove the partially + transmitted file. The target should initially write the file to a temporary + location so a failure does not destroy the original file. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SwitchControlProcessor(self, request, context): + """SwitchControlProcessor will switch from the current route processor to the + provided route processor. If the current route processor is the same as the + one provided it is a NOOP. If the target does not exist an error is + returned. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Reboot(self, request, context): + """Reboot causes the target to reboot, possibly at some point in the future. + If the method of reboot is not supported then the Reboot RPC will fail. + If the reboot is immediate the command will block until the subcomponents + have restarted. + If a reboot on the active control processor is pending the service must + reject all other reboot requests. + If a reboot request for active control processor is initiated with other + pending reboot requests it must be rejected. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def RebootStatus(self, request, context): + """RebootStatus returns the status of reboot for the target. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CancelReboot(self, request, context): + """CancelReboot cancels any pending reboot request. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def KillProcess(self, request, context): + """KillProcess kills an OS process and optionally restarts it. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_SystemServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Ping': grpc.unary_stream_rpc_method_handler( + servicer.Ping, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingResponse.SerializeToString, + ), + 'Traceroute': grpc.unary_stream_rpc_method_handler( + servicer.Traceroute, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteResponse.SerializeToString, + ), + 'Time': grpc.unary_unary_rpc_method_handler( + servicer.Time, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeResponse.SerializeToString, + ), + 'SetPackage': grpc.stream_unary_rpc_method_handler( + servicer.SetPackage, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageResponse.SerializeToString, + ), + 'SwitchControlProcessor': grpc.unary_unary_rpc_method_handler( + servicer.SwitchControlProcessor, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorResponse.SerializeToString, + ), + 'Reboot': grpc.unary_unary_rpc_method_handler( + servicer.Reboot, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootResponse.SerializeToString, + ), + 'RebootStatus': grpc.unary_unary_rpc_method_handler( + servicer.RebootStatus, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusResponse.SerializeToString, + ), + 'CancelReboot': grpc.unary_unary_rpc_method_handler( + servicer.CancelReboot, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootResponse.SerializeToString, + ), + 'KillProcess': grpc.unary_unary_rpc_method_handler( + servicer.KillProcess, + request_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessRequest.FromString, + response_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'gnoi.system.System', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('gnoi.system.System', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class System(object): + """The gNOI service is a collection of operational RPC's that allow for the + management of a target outside of the configuration and telemetry pipeline. + """ + + @staticmethod + def Ping(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/gnoi.system.System/Ping', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Traceroute(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/gnoi.system.System/Traceroute', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Time(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/gnoi.system.System/Time', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def SetPackage(request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.stream_unary( + request_iterator, + target, + '/gnoi.system.System/SetPackage', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def SwitchControlProcessor(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/gnoi.system.System/SwitchControlProcessor', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Reboot(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/gnoi.system.System/Reboot', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def RebootStatus(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/gnoi.system.System/RebootStatus', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CancelReboot(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/gnoi.system.System/CancelReboot', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def KillProcess(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/gnoi.system.System/KillProcess', + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessRequest.SerializeToString, + github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py new file mode 100644 index 00000000000..549fd9bc8bb --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: github.com/openconfig/gnoi/types/types.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'github.com/openconfig/gnoi/types/types.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,github.com/openconfig/gnoi/types/types.proto\x12\ngnoi.types\x1a google/protobuf/descriptor.proto\"\x89\x01\n\x08HashType\x12/\n\x06method\x18\x01 \x01(\x0e\x32\x1f.gnoi.types.HashType.HashMethod\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\">\n\nHashMethod\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\n\n\x06SHA256\x10\x01\x12\n\n\x06SHA512\x10\x02\x12\x07\n\x03MD5\x10\x03\":\n\x04Path\x12\x0e\n\x06origin\x18\x02 \x01(\t\x12\"\n\x04\x65lem\x18\x03 \x03(\x0b\x32\x14.gnoi.types.PathElem\"p\n\x08PathElem\x12\x0c\n\x04name\x18\x01 \x01(\t\x12*\n\x03key\x18\x02 \x03(\x0b\x32\x1d.gnoi.types.PathElem.KeyEntry\x1a*\n\x08KeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"h\n\x0b\x43redentials\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x13\n\tcleartext\x18\x02 \x01(\tH\x00\x12&\n\x06hashed\x18\x03 \x01(\x0b\x32\x14.gnoi.types.HashTypeH\x00\x42\n\n\x08password*1\n\nL3Protocol\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02:3\n\x0cgnoi_version\x12\x1c.google.protobuf.FileOptions\x18\xea\x07 \x01(\tB\"Z github.com/openconfig/gnoi/typesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.openconfig.gnoi.types.types_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z github.com/openconfig/gnoi/types' + _globals['_PATHELEM_KEYENTRY']._loaded_options = None + _globals['_PATHELEM_KEYENTRY']._serialized_options = b'8\001' + _globals['_L3PROTOCOL']._serialized_start=514 + _globals['_L3PROTOCOL']._serialized_end=563 + _globals['_HASHTYPE']._serialized_start=95 + _globals['_HASHTYPE']._serialized_end=232 + _globals['_HASHTYPE_HASHMETHOD']._serialized_start=170 + _globals['_HASHTYPE_HASHMETHOD']._serialized_end=232 + _globals['_PATH']._serialized_start=234 + _globals['_PATH']._serialized_end=292 + _globals['_PATHELEM']._serialized_start=294 + _globals['_PATHELEM']._serialized_end=406 + _globals['_PATHELEM_KEYENTRY']._serialized_start=364 + _globals['_PATHELEM_KEYENTRY']._serialized_end=406 + _globals['_CREDENTIALS']._serialized_start=408 + _globals['_CREDENTIALS']._serialized_end=512 +# @@protoc_insertion_point(module_scope) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py new file mode 100644 index 00000000000..5dce0282d74 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.80.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in github.com/openconfig/gnoi/types/types_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/src/sonic-py-common/tests/test_gnoi_client.py b/src/sonic-py-common/tests/test_gnoi_client.py new file mode 100644 index 00000000000..9e9c50f57a1 --- /dev/null +++ b/src/sonic-py-common/tests/test_gnoi_client.py @@ -0,0 +1,93 @@ +"""Tests for sonic_py_common.grpc.gnoi.client.GnoiClient.""" + +import sys +import unittest +from unittest.mock import MagicMock, patch, PropertyMock + +# Mock grpc +mock_grpc = MagicMock() +mock_grpc.RpcError = type('RpcError', (Exception,), {}) +sys.modules['grpc'] = mock_grpc + +# Mock proto stubs +sys.modules['sonic_py_common.grpc.gnoi.system_pb2'] = MagicMock() +sys.modules['sonic_py_common.grpc.gnoi.system_pb2_grpc'] = MagicMock() +sys.modules['sonic_py_common.grpc.gnoi.types_pb2'] = MagicMock() +sys.modules['sonic_py_common.grpc.gnoi.common_pb2'] = MagicMock() + +from sonic_py_common.grpc.gnoi.client import GnoiClient +from sonic_py_common.grpc.gnoi import system_pb2_grpc + + +class TestGnoiClient(unittest.TestCase): + + def test_context_manager_creates_channel(self): + """Test that entering context creates an insecure channel.""" + with GnoiClient("10.0.0.1:8080") as client: + mock_grpc.insecure_channel.assert_called_with("10.0.0.1:8080", options=None) + self.assertIsNotNone(client.channel) + + def test_context_manager_closes_channel(self): + """Test that exiting context closes the channel.""" + with GnoiClient("10.0.0.1:8080") as client: + channel = client.channel + channel.close.assert_called() + + def test_close_idempotent(self): + """Test that close() can be called multiple times safely.""" + client = GnoiClient("10.0.0.1:8080") + client.__enter__() + client.close() + client.close() # Should not raise + + def test_system_property_returns_stub(self): + """Test that .system returns a SystemStub.""" + with GnoiClient("10.0.0.1:8080") as client: + stub = client.system + system_pb2_grpc.SystemStub.assert_called_with(client.channel) + + def test_channel_options_passed(self): + """Test that gRPC channel options are forwarded.""" + opts = [('grpc.keepalive_time_ms', 10000)] + with GnoiClient("10.0.0.1:8080", options=opts) as client: + mock_grpc.insecure_channel.assert_called_with("10.0.0.1:8080", options=opts) + + def test_system_reboot_rpc(self): + """Test calling Reboot through the system stub.""" + mock_system_stub = MagicMock() + system_pb2_grpc.SystemStub.return_value = mock_system_stub + + with GnoiClient("10.0.0.1:8080") as client: + request = MagicMock() + client.system.Reboot(request, timeout=60) + mock_system_stub.Reboot.assert_called_once_with(request, timeout=60) + + def test_system_reboot_status_rpc(self): + """Test calling RebootStatus through the system stub.""" + mock_system_stub = MagicMock() + system_pb2_grpc.SystemStub.return_value = mock_system_stub + + with GnoiClient("10.0.0.1:8080") as client: + request = MagicMock() + client.system.RebootStatus(request, timeout=10) + mock_system_stub.RebootStatus.assert_called_once_with(request, timeout=10) + + def test_grpc_error_propagation(self): + """Test that gRPC errors propagate to callers.""" + mock_system_stub = MagicMock() + rpc_error = mock_grpc.RpcError() + mock_system_stub.Reboot.side_effect = rpc_error + system_pb2_grpc.SystemStub.return_value = mock_system_stub + + with GnoiClient("10.0.0.1:8080") as client: + with self.assertRaises(mock_grpc.RpcError): + client.system.Reboot(MagicMock(), timeout=60) + + def test_channel_not_available_before_enter(self): + """Test that channel is None before entering context.""" + client = GnoiClient("10.0.0.1:8080") + self.assertIsNone(client.channel) + + +if __name__ == '__main__': + unittest.main() From 77d21bfb5d574cfde6cc3dc92fe18f4cbcc6a332 Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Mon, 30 Mar 2026 17:00:49 +0000 Subject: [PATCH 2/4] Add FakeGnoiClient test double for realistic testing Add sonic_py_common.grpc.gnoi.testing with: - FakeSystemStub: controllable System service stub with injectable responses, side_effects (including sequences for polling scenarios), and call history for assertions - FakeGnoiClient: drop-in replacement for GnoiClient with no real gRPC connections, same context manager protocol Consumers can write tests like: fake = FakeGnoiClient() fake.system.set_reboot_status(active=False, status=STATUS_SUCCESS) with patch('my_module.GnoiClient', return_value=fake): assert my_reboot_function() == True assert len(fake.system.reboot_calls) == 1 12 new tests for the fake itself. Signed-off-by: sigabrtv1-ui Signed-off-by: Dawei Huang --- .../sonic_py_common/grpc/gnoi/testing.py | 154 ++++++++++++++++++ .../tests/test_gnoi_testing.py | 143 ++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py create mode 100644 src/sonic-py-common/tests/test_gnoi_testing.py diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py new file mode 100644 index 00000000000..ae9901e2057 --- /dev/null +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py @@ -0,0 +1,154 @@ +""" +Fake gNOI client for testing. + +Provides a test double that behaves like GnoiClient but with +controllable responses and no real gRPC connections. + +Usage: + from sonic_py_common.grpc.gnoi.testing import FakeGnoiClient + from sonic_py_common.grpc.gnoi import system_pb2 + + fake = FakeGnoiClient() + fake.system.set_reboot_status(active=False, status=system_pb2.RebootStatus.Status.STATUS_SUCCESS) + + with fake as client: + resp = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) + assert not resp.active +""" + +from unittest.mock import MagicMock +from sonic_py_common.grpc.gnoi import system_pb2 + + +class FakeSystemStub: + """Fake gNOI System service stub with controllable RPC responses. + + Each RPC can be configured with: + - A return value (response proto) + - A side_effect (exception or callable) + - A call history for assertions + """ + + def __init__(self): + self._reboot_response = system_pb2.RebootResponse() + self._reboot_status_response = self._default_reboot_status() + self._cancel_reboot_response = system_pb2.CancelRebootResponse() + + self._reboot_side_effect = None + self._reboot_status_side_effect = None + self._cancel_reboot_side_effect = None + + self.reboot_calls = [] + self.reboot_status_calls = [] + self.cancel_reboot_calls = [] + + @staticmethod + def _default_reboot_status(): + resp = system_pb2.RebootStatusResponse() + resp.active = False + resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS + return resp + + # ---- Configuration methods ---- + + def set_reboot_response(self, response=None, side_effect=None): + """Configure Reboot RPC response or side_effect (exception/callable).""" + if response is not None: + self._reboot_response = response + self._reboot_side_effect = side_effect + + def set_reboot_status(self, active=None, status=None, message="", + response=None, side_effect=None): + """Configure RebootStatus response fields or provide full response.""" + if response is not None: + self._reboot_status_response = response + else: + if active is not None: + self._reboot_status_response.active = active + if status is not None: + self._reboot_status_response.status.status = status + if message: + self._reboot_status_response.status.message = message + self._reboot_status_side_effect = side_effect + + def set_cancel_reboot_response(self, response=None, side_effect=None): + """Configure CancelReboot RPC response or side_effect.""" + if response is not None: + self._cancel_reboot_response = response + self._cancel_reboot_side_effect = side_effect + + # ---- RPC methods (match real SystemStub interface) ---- + + def Reboot(self, request, timeout=None, metadata=None, credentials=None): + self.reboot_calls.append({ + 'request': request, + 'timeout': timeout, + }) + if self._reboot_side_effect: + if callable(self._reboot_side_effect) and not isinstance(self._reboot_side_effect, type): + return self._reboot_side_effect(request, timeout=timeout) + raise self._reboot_side_effect + return self._reboot_response + + def RebootStatus(self, request, timeout=None, metadata=None, credentials=None): + self.reboot_status_calls.append({ + 'request': request, + 'timeout': timeout, + }) + if self._reboot_status_side_effect: + if isinstance(self._reboot_status_side_effect, list): + effect = self._reboot_status_side_effect.pop(0) + if isinstance(effect, Exception): + raise effect + return effect + if callable(self._reboot_status_side_effect) and not isinstance(self._reboot_status_side_effect, type): + return self._reboot_status_side_effect(request, timeout=timeout) + raise self._reboot_status_side_effect + return self._reboot_status_response + + def CancelReboot(self, request, timeout=None, metadata=None, credentials=None): + self.cancel_reboot_calls.append({ + 'request': request, + 'timeout': timeout, + }) + if self._cancel_reboot_side_effect: + if callable(self._cancel_reboot_side_effect) and not isinstance(self._cancel_reboot_side_effect, type): + return self._cancel_reboot_side_effect(request, timeout=timeout) + raise self._cancel_reboot_side_effect + return self._cancel_reboot_response + + +class FakeGnoiClient: + """Fake GnoiClient for testing — drop-in replacement with no real gRPC. + + Supports context manager protocol and exposes configurable service stubs. + + Example: + fake = FakeGnoiClient() + fake.system.set_reboot_status(active=False) + + # Patch GnoiClient to return fake + with patch('my_module.GnoiClient', return_value=fake): + result = my_function_that_uses_gnoi() + """ + + def __init__(self, target="fake:0"): + self._target = target + self._system = FakeSystemStub() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + + def close(self): + pass + + @property + def channel(self): + return MagicMock() + + @property + def system(self): + return self._system diff --git a/src/sonic-py-common/tests/test_gnoi_testing.py b/src/sonic-py-common/tests/test_gnoi_testing.py new file mode 100644 index 00000000000..b1a6cf79d8e --- /dev/null +++ b/src/sonic-py-common/tests/test_gnoi_testing.py @@ -0,0 +1,143 @@ +"""Tests for sonic_py_common.grpc.gnoi.testing (FakeGnoiClient).""" + +import sys +import unittest +from unittest.mock import MagicMock + +# Mock grpc module and submodules before any imports +mock_grpc = MagicMock() +mock_grpc.__version__ = '1.80.0' +mock_grpc.RpcError = type('RpcError', (Exception,), { + 'code': lambda self: 'UNAVAILABLE', + 'details': lambda self: 'fake error', +}) +mock_utilities = MagicMock() +mock_utilities.first_version_is_lower = lambda a, b: False +sys.modules['grpc'] = mock_grpc +sys.modules['grpc._utilities'] = mock_utilities + +from sonic_py_common.grpc.gnoi.testing import FakeGnoiClient, FakeSystemStub +from sonic_py_common.grpc.gnoi import system_pb2 + + +class TestFakeSystemStub(unittest.TestCase): + + def test_reboot_default_success(self): + stub = FakeSystemStub() + req = system_pb2.RebootRequest(method=system_pb2.HALT) + resp = stub.Reboot(req, timeout=60) + self.assertIsNotNone(resp) + self.assertEqual(len(stub.reboot_calls), 1) + self.assertEqual(stub.reboot_calls[0]['timeout'], 60) + + def test_reboot_side_effect_exception(self): + stub = FakeSystemStub() + rpc_error = mock_grpc.RpcError() + stub.set_reboot_response(side_effect=rpc_error) + with self.assertRaises(mock_grpc.RpcError): + stub.Reboot(system_pb2.RebootRequest(), timeout=10) + + def test_reboot_status_default_complete(self): + stub = FakeSystemStub() + resp = stub.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) + self.assertFalse(resp.active) + self.assertEqual(resp.status.status, system_pb2.RebootStatus.Status.STATUS_SUCCESS) + + def test_reboot_status_active(self): + stub = FakeSystemStub() + stub.set_reboot_status(active=True) + resp = stub.RebootStatus(system_pb2.RebootStatusRequest()) + self.assertTrue(resp.active) + + def test_reboot_status_sequence(self): + """Test list side_effect for polling scenarios.""" + stub = FakeSystemStub() + active_resp = system_pb2.RebootStatusResponse() + active_resp.active = True + done_resp = system_pb2.RebootStatusResponse() + done_resp.active = False + done_resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS + + stub.set_reboot_status(side_effect=[active_resp, active_resp, done_resp]) + + r1 = stub.RebootStatus(system_pb2.RebootStatusRequest()) + self.assertTrue(r1.active) + r2 = stub.RebootStatus(system_pb2.RebootStatusRequest()) + self.assertTrue(r2.active) + r3 = stub.RebootStatus(system_pb2.RebootStatusRequest()) + self.assertFalse(r3.active) + self.assertEqual(len(stub.reboot_status_calls), 3) + + def test_reboot_status_sequence_with_error(self): + """Test mixed responses and errors in sequence.""" + stub = FakeSystemStub() + rpc_error = mock_grpc.RpcError() + done_resp = system_pb2.RebootStatusResponse() + done_resp.active = False + + stub.set_reboot_status(side_effect=[rpc_error, done_resp]) + + with self.assertRaises(mock_grpc.RpcError): + stub.RebootStatus(system_pb2.RebootStatusRequest()) + r2 = stub.RebootStatus(system_pb2.RebootStatusRequest()) + self.assertFalse(r2.active) + + def test_cancel_reboot(self): + stub = FakeSystemStub() + resp = stub.CancelReboot(system_pb2.CancelRebootRequest(), timeout=30) + self.assertIsNotNone(resp) + self.assertEqual(len(stub.cancel_reboot_calls), 1) + + def test_call_history_tracks_requests(self): + stub = FakeSystemStub() + req = system_pb2.RebootRequest(method=system_pb2.HALT, message="test") + stub.Reboot(req, timeout=60) + self.assertEqual(stub.reboot_calls[0]['request'], req) + + +class TestFakeGnoiClient(unittest.TestCase): + + def test_context_manager(self): + with FakeGnoiClient() as client: + self.assertIsNotNone(client.system) + + def test_system_stub_persists(self): + """Same stub instance across property accesses.""" + fake = FakeGnoiClient() + self.assertIs(fake.system, fake.system) + + def test_end_to_end_reboot_flow(self): + """Simulate a reboot + poll flow like gnoi_shutdown_daemon.""" + fake = FakeGnoiClient() + + # Configure: reboot succeeds, status shows active then done + active_resp = system_pb2.RebootStatusResponse() + active_resp.active = True + done_resp = system_pb2.RebootStatusResponse() + done_resp.active = False + done_resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS + fake.system.set_reboot_status(side_effect=[active_resp, done_resp]) + + with fake as client: + # Send reboot + client.system.Reboot( + system_pb2.RebootRequest(method=system_pb2.HALT), + timeout=60, + ) + + # Poll status + r1 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) + self.assertTrue(r1.active) + r2 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) + self.assertFalse(r2.active) + + self.assertEqual(len(fake.system.reboot_calls), 1) + self.assertEqual(len(fake.system.reboot_status_calls), 2) + + def test_close_is_noop(self): + fake = FakeGnoiClient() + fake.close() # Should not raise + + +if __name__ == '__main__': + unittest.main() From 39d31496480adc9f078403a50e371eeb81a6d275 Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Mon, 30 Mar 2026 17:03:06 +0000 Subject: [PATCH 3/4] Add FakeGnoiServer for real gRPC testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace mock-based testing with a real gRPC fake server: - FakeGnoiServer: starts a real gRPC server on localhost with controllable service implementations. Tests use the real GnoiClient connecting over a real channel — no mocking of gRPC internals. - FakeSystemServicer: configurable System service with: - set_reboot_response() / set_reboot_status() for single responses - set_reboot_status_sequence() for polling scenarios (active→done) - Error injection via grpc.StatusCode - Call history for assertions (reboot_calls, etc.) - reset() to clear state between tests - Rewrote all client tests to use FakeGnoiServer (no sys.modules mocking) 19 tests, all using real gRPC. Zero mocks. Signed-off-by: sigabrtv1-ui Signed-off-by: Dawei Huang --- .../sonic_py_common/grpc/gnoi/testing.py | 235 +++++++++++------- src/sonic-py-common/tests/test_gnoi_client.py | 133 +++++----- .../tests/test_gnoi_testing.py | 231 +++++++++-------- 3 files changed, 341 insertions(+), 258 deletions(-) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py index ae9901e2057..a9041423b9d 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/testing.py @@ -1,65 +1,98 @@ """ -Fake gNOI client for testing. +Fake gNOI gRPC server for testing. -Provides a test double that behaves like GnoiClient but with -controllable responses and no real gRPC connections. +Spins up a real gRPC server on localhost with controllable service +implementations. Tests use the real GnoiClient against this server — +no mocking of gRPC internals. Usage: - from sonic_py_common.grpc.gnoi.testing import FakeGnoiClient - from sonic_py_common.grpc.gnoi import system_pb2 + from sonic_py_common.grpc.gnoi.testing import FakeGnoiServer + from sonic_py_common.grpc.gnoi import GnoiClient, system_pb2 - fake = FakeGnoiClient() - fake.system.set_reboot_status(active=False, status=system_pb2.RebootStatus.Status.STATUS_SUCCESS) + server = FakeGnoiServer() + server.start() - with fake as client: - resp = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) + # Configure responses + server.system.set_reboot_response() # default success + server.system.set_reboot_status(active=False, status=STATUS_SUCCESS) + + # Test with real client + with GnoiClient(server.target) as client: + client.system.Reboot(system_pb2.RebootRequest(method=system_pb2.HALT), timeout=5) + resp = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) assert not resp.active + + server.stop() """ -from unittest.mock import MagicMock +from concurrent import futures +import grpc + from sonic_py_common.grpc.gnoi import system_pb2 +from sonic_py_common.grpc.gnoi import system_pb2_grpc -class FakeSystemStub: - """Fake gNOI System service stub with controllable RPC responses. +class FakeSystemServicer(system_pb2_grpc.SystemServicer): + """Fake gNOI System servicer with configurable responses. - Each RPC can be configured with: - - A return value (response proto) - - A side_effect (exception or callable) - - A call history for assertions + Each RPC returns a configured response or raises a configured error. + Supports response sequences for polling tests. + Call history is recorded for assertions. """ def __init__(self): + self.reset() + + def reset(self): + """Reset all configured responses and call history.""" self._reboot_response = system_pb2.RebootResponse() - self._reboot_status_response = self._default_reboot_status() - self._cancel_reboot_response = system_pb2.CancelRebootResponse() + self._reboot_error = None + + self._reboot_status_responses = None # None = use single response + self._reboot_status_response = self._default_status_response() + self._reboot_status_error = None - self._reboot_side_effect = None - self._reboot_status_side_effect = None - self._cancel_reboot_side_effect = None + self._cancel_reboot_response = system_pb2.CancelRebootResponse() + self._cancel_reboot_error = None self.reboot_calls = [] self.reboot_status_calls = [] self.cancel_reboot_calls = [] @staticmethod - def _default_reboot_status(): + def _default_status_response(): resp = system_pb2.RebootStatusResponse() resp.active = False resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS return resp - # ---- Configuration methods ---- + # ---- Configuration ---- + + def set_reboot_response(self, response=None, error_code=None, error_message=""): + """Set Reboot RPC response or error. - def set_reboot_response(self, response=None, side_effect=None): - """Configure Reboot RPC response or side_effect (exception/callable).""" + Args: + response: RebootResponse proto (default: empty success). + error_code: grpc.StatusCode to return as error (e.g. grpc.StatusCode.UNAVAILABLE). + error_message: Error detail string. + """ if response is not None: self._reboot_response = response - self._reboot_side_effect = side_effect + self._reboot_error = (error_code, error_message) if error_code else None def set_reboot_status(self, active=None, status=None, message="", - response=None, side_effect=None): - """Configure RebootStatus response fields or provide full response.""" + response=None, error_code=None, error_message=""): + """Set RebootStatus single response. + + Args: + active: Whether reboot is in progress. + status: RebootStatus.Status enum value. + message: Status message. + response: Full RebootStatusResponse (overrides field args). + error_code: grpc.StatusCode for error response. + error_message: Error detail string. + """ + self._reboot_status_responses = None if response is not None: self._reboot_status_response = response else: @@ -69,86 +102,108 @@ def set_reboot_status(self, active=None, status=None, message="", self._reboot_status_response.status.status = status if message: self._reboot_status_response.status.message = message - self._reboot_status_side_effect = side_effect + self._reboot_status_error = (error_code, error_message) if error_code else None + + def set_reboot_status_sequence(self, responses): + """Set a sequence of RebootStatus responses for polling tests. + + Each call pops the next item. Items can be: + - RebootStatusResponse proto → returned as success + - (grpc.StatusCode, message) tuple → returned as error + + After exhaustion, falls back to the single response. + + Args: + responses: List of RebootStatusResponse or (StatusCode, msg) tuples. + """ + self._reboot_status_responses = list(responses) - def set_cancel_reboot_response(self, response=None, side_effect=None): - """Configure CancelReboot RPC response or side_effect.""" + def set_cancel_reboot_response(self, response=None, error_code=None, error_message=""): + """Set CancelReboot RPC response or error.""" if response is not None: self._cancel_reboot_response = response - self._cancel_reboot_side_effect = side_effect - - # ---- RPC methods (match real SystemStub interface) ---- - - def Reboot(self, request, timeout=None, metadata=None, credentials=None): - self.reboot_calls.append({ - 'request': request, - 'timeout': timeout, - }) - if self._reboot_side_effect: - if callable(self._reboot_side_effect) and not isinstance(self._reboot_side_effect, type): - return self._reboot_side_effect(request, timeout=timeout) - raise self._reboot_side_effect + self._cancel_reboot_error = (error_code, error_message) if error_code else None + + # ---- RPC implementations ---- + + def Reboot(self, request, context): + self.reboot_calls.append(request) + if self._reboot_error: + context.abort(self._reboot_error[0], self._reboot_error[1]) return self._reboot_response - def RebootStatus(self, request, timeout=None, metadata=None, credentials=None): - self.reboot_status_calls.append({ - 'request': request, - 'timeout': timeout, - }) - if self._reboot_status_side_effect: - if isinstance(self._reboot_status_side_effect, list): - effect = self._reboot_status_side_effect.pop(0) - if isinstance(effect, Exception): - raise effect - return effect - if callable(self._reboot_status_side_effect) and not isinstance(self._reboot_status_side_effect, type): - return self._reboot_status_side_effect(request, timeout=timeout) - raise self._reboot_status_side_effect + def RebootStatus(self, request, context): + self.reboot_status_calls.append(request) + if self._reboot_status_responses: + item = self._reboot_status_responses.pop(0) + if isinstance(item, tuple): + context.abort(item[0], item[1]) + return item + if self._reboot_status_error: + context.abort(self._reboot_status_error[0], self._reboot_status_error[1]) return self._reboot_status_response - def CancelReboot(self, request, timeout=None, metadata=None, credentials=None): - self.cancel_reboot_calls.append({ - 'request': request, - 'timeout': timeout, - }) - if self._cancel_reboot_side_effect: - if callable(self._cancel_reboot_side_effect) and not isinstance(self._cancel_reboot_side_effect, type): - return self._cancel_reboot_side_effect(request, timeout=timeout) - raise self._cancel_reboot_side_effect + def CancelReboot(self, request, context): + self.cancel_reboot_calls.append(request) + if self._cancel_reboot_error: + context.abort(self._cancel_reboot_error[0], self._cancel_reboot_error[1]) return self._cancel_reboot_response -class FakeGnoiClient: - """Fake GnoiClient for testing — drop-in replacement with no real gRPC. +class FakeGnoiServer: + """Fake gNOI gRPC server for integration-style tests. - Supports context manager protocol and exposes configurable service stubs. + Starts a real gRPC server on a random localhost port. + Tests use GnoiClient(server.target) for real channel communication. Example: - fake = FakeGnoiClient() - fake.system.set_reboot_status(active=False) - - # Patch GnoiClient to return fake - with patch('my_module.GnoiClient', return_value=fake): - result = my_function_that_uses_gnoi() + server = FakeGnoiServer() + server.start() + try: + with GnoiClient(server.target) as client: + client.system.Reboot(req, timeout=5) + assert len(server.system.reboot_calls) == 1 + finally: + server.stop() + + Or as context manager: + with FakeGnoiServer() as server: + with GnoiClient(server.target) as client: + ... """ - def __init__(self, target="fake:0"): - self._target = target - self._system = FakeSystemStub() + def __init__(self, max_workers=2): + self._max_workers = max_workers + self._server = None + self._port = None + self.system = FakeSystemServicer() - def __enter__(self): + @property + def target(self): + """gRPC target string, e.g. 'localhost:50051'.""" + return f"localhost:{self._port}" + + def start(self): + """Start the fake gRPC server on a random port.""" + self._server = grpc.server(futures.ThreadPoolExecutor(max_workers=self._max_workers)) + system_pb2_grpc.add_SystemServicer_to_server(self.system, self._server) + self._port = self._server.add_insecure_port("localhost:0") + self._server.start() return self - def __exit__(self, exc_type, exc_val, exc_tb): - return False + def stop(self, grace=0): + """Stop the server.""" + if self._server: + self._server.stop(grace) + self._server = None - def close(self): - pass + def reset(self): + """Reset all service state (responses + call history).""" + self.system.reset() - @property - def channel(self): - return MagicMock() + def __enter__(self): + return self.start() - @property - def system(self): - return self._system + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + return False diff --git a/src/sonic-py-common/tests/test_gnoi_client.py b/src/sonic-py-common/tests/test_gnoi_client.py index 9e9c50f57a1..59e99ffda9e 100644 --- a/src/sonic-py-common/tests/test_gnoi_client.py +++ b/src/sonic-py-common/tests/test_gnoi_client.py @@ -1,92 +1,89 @@ -"""Tests for sonic_py_common.grpc.gnoi.client.GnoiClient.""" +"""Tests for sonic_py_common.grpc.gnoi.client.GnoiClient. -import sys -import unittest -from unittest.mock import MagicMock, patch, PropertyMock - -# Mock grpc -mock_grpc = MagicMock() -mock_grpc.RpcError = type('RpcError', (Exception,), {}) -sys.modules['grpc'] = mock_grpc +Uses FakeGnoiServer for real gRPC — no mocking. +""" -# Mock proto stubs -sys.modules['sonic_py_common.grpc.gnoi.system_pb2'] = MagicMock() -sys.modules['sonic_py_common.grpc.gnoi.system_pb2_grpc'] = MagicMock() -sys.modules['sonic_py_common.grpc.gnoi.types_pb2'] = MagicMock() -sys.modules['sonic_py_common.grpc.gnoi.common_pb2'] = MagicMock() +import unittest +import grpc +from sonic_py_common.grpc.gnoi.testing import FakeGnoiServer from sonic_py_common.grpc.gnoi.client import GnoiClient -from sonic_py_common.grpc.gnoi import system_pb2_grpc +from sonic_py_common.grpc.gnoi import system_pb2 class TestGnoiClient(unittest.TestCase): - def test_context_manager_creates_channel(self): - """Test that entering context creates an insecure channel.""" - with GnoiClient("10.0.0.1:8080") as client: - mock_grpc.insecure_channel.assert_called_with("10.0.0.1:8080", options=None) - self.assertIsNotNone(client.channel) + @classmethod + def setUpClass(cls): + cls.server = FakeGnoiServer() + cls.server.start() + + @classmethod + def tearDownClass(cls): + cls.server.stop() - def test_context_manager_closes_channel(self): - """Test that exiting context closes the channel.""" - with GnoiClient("10.0.0.1:8080") as client: - channel = client.channel - channel.close.assert_called() + def setUp(self): + self.server.reset() + + def test_context_manager_creates_and_closes_channel(self): + with GnoiClient(self.server.target) as client: + self.assertIsNotNone(client.channel) + # After exit, channel is None + self.assertIsNone(client.channel) def test_close_idempotent(self): - """Test that close() can be called multiple times safely.""" - client = GnoiClient("10.0.0.1:8080") + client = GnoiClient(self.server.target) client.__enter__() client.close() client.close() # Should not raise - def test_system_property_returns_stub(self): - """Test that .system returns a SystemStub.""" - with GnoiClient("10.0.0.1:8080") as client: - stub = client.system - system_pb2_grpc.SystemStub.assert_called_with(client.channel) - - def test_channel_options_passed(self): - """Test that gRPC channel options are forwarded.""" - opts = [('grpc.keepalive_time_ms', 10000)] - with GnoiClient("10.0.0.1:8080", options=opts) as client: - mock_grpc.insecure_channel.assert_called_with("10.0.0.1:8080", options=opts) + def test_channel_not_available_before_enter(self): + client = GnoiClient(self.server.target) + self.assertIsNone(client.channel) def test_system_reboot_rpc(self): - """Test calling Reboot through the system stub.""" - mock_system_stub = MagicMock() - system_pb2_grpc.SystemStub.return_value = mock_system_stub - - with GnoiClient("10.0.0.1:8080") as client: - request = MagicMock() - client.system.Reboot(request, timeout=60) - mock_system_stub.Reboot.assert_called_once_with(request, timeout=60) + with GnoiClient(self.server.target) as client: + resp = client.system.Reboot( + system_pb2.RebootRequest(method=system_pb2.HALT), + timeout=5, + ) + self.assertIsNotNone(resp) + self.assertEqual(len(self.server.system.reboot_calls), 1) def test_system_reboot_status_rpc(self): - """Test calling RebootStatus through the system stub.""" - mock_system_stub = MagicMock() - system_pb2_grpc.SystemStub.return_value = mock_system_stub - - with GnoiClient("10.0.0.1:8080") as client: - request = MagicMock() - client.system.RebootStatus(request, timeout=10) - mock_system_stub.RebootStatus.assert_called_once_with(request, timeout=10) + with GnoiClient(self.server.target) as client: + resp = client.system.RebootStatus( + system_pb2.RebootStatusRequest(), timeout=5 + ) + self.assertFalse(resp.active) def test_grpc_error_propagation(self): - """Test that gRPC errors propagate to callers.""" - mock_system_stub = MagicMock() - rpc_error = mock_grpc.RpcError() - mock_system_stub.Reboot.side_effect = rpc_error - system_pb2_grpc.SystemStub.return_value = mock_system_stub - - with GnoiClient("10.0.0.1:8080") as client: - with self.assertRaises(mock_grpc.RpcError): - client.system.Reboot(MagicMock(), timeout=60) - - def test_channel_not_available_before_enter(self): - """Test that channel is None before entering context.""" - client = GnoiClient("10.0.0.1:8080") - self.assertIsNone(client.channel) + self.server.system.set_reboot_response( + error_code=grpc.StatusCode.UNAVAILABLE, + error_message="connection refused", + ) + with GnoiClient(self.server.target) as client: + with self.assertRaises(grpc.RpcError) as ctx: + client.system.Reboot(system_pb2.RebootRequest(), timeout=5) + self.assertEqual(ctx.exception.code(), grpc.StatusCode.UNAVAILABLE) + + def test_channel_options_accepted(self): + """GnoiClient accepts gRPC channel options.""" + opts = [('grpc.keepalive_time_ms', 10000)] + with GnoiClient(self.server.target, options=opts) as client: + resp = client.system.RebootStatus( + system_pb2.RebootStatusRequest(), timeout=5 + ) + self.assertIsNotNone(resp) + + def test_multiple_rpcs_on_same_channel(self): + with GnoiClient(self.server.target) as client: + client.system.Reboot(system_pb2.RebootRequest(), timeout=5) + client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + client.system.CancelReboot(system_pb2.CancelRebootRequest(), timeout=5) + self.assertEqual(len(self.server.system.reboot_calls), 1) + self.assertEqual(len(self.server.system.reboot_status_calls), 1) + self.assertEqual(len(self.server.system.cancel_reboot_calls), 1) if __name__ == '__main__': diff --git a/src/sonic-py-common/tests/test_gnoi_testing.py b/src/sonic-py-common/tests/test_gnoi_testing.py index b1a6cf79d8e..79182f8a2f9 100644 --- a/src/sonic-py-common/tests/test_gnoi_testing.py +++ b/src/sonic-py-common/tests/test_gnoi_testing.py @@ -1,142 +1,173 @@ -"""Tests for sonic_py_common.grpc.gnoi.testing (FakeGnoiClient).""" +"""Tests for sonic_py_common.grpc.gnoi.testing (FakeGnoiServer). + +These tests use a real gRPC server and real GnoiClient — no mocking. +""" -import sys import unittest -from unittest.mock import MagicMock - -# Mock grpc module and submodules before any imports -mock_grpc = MagicMock() -mock_grpc.__version__ = '1.80.0' -mock_grpc.RpcError = type('RpcError', (Exception,), { - 'code': lambda self: 'UNAVAILABLE', - 'details': lambda self: 'fake error', -}) -mock_utilities = MagicMock() -mock_utilities.first_version_is_lower = lambda a, b: False -sys.modules['grpc'] = mock_grpc -sys.modules['grpc._utilities'] = mock_utilities - -from sonic_py_common.grpc.gnoi.testing import FakeGnoiClient, FakeSystemStub +import grpc + +from sonic_py_common.grpc.gnoi.testing import FakeGnoiServer, FakeSystemServicer +from sonic_py_common.grpc.gnoi.client import GnoiClient from sonic_py_common.grpc.gnoi import system_pb2 -class TestFakeSystemStub(unittest.TestCase): +class TestFakeSystemServicer(unittest.TestCase): + """Unit tests for FakeSystemServicer behavior.""" + + @classmethod + def setUpClass(cls): + cls.server = FakeGnoiServer() + cls.server.start() + + @classmethod + def tearDownClass(cls): + cls.server.stop() + + def setUp(self): + self.server.reset() def test_reboot_default_success(self): - stub = FakeSystemStub() - req = system_pb2.RebootRequest(method=system_pb2.HALT) - resp = stub.Reboot(req, timeout=60) + with GnoiClient(self.server.target) as client: + resp = client.system.Reboot( + system_pb2.RebootRequest(method=system_pb2.HALT, message="test"), + timeout=5, + ) self.assertIsNotNone(resp) - self.assertEqual(len(stub.reboot_calls), 1) - self.assertEqual(stub.reboot_calls[0]['timeout'], 60) - - def test_reboot_side_effect_exception(self): - stub = FakeSystemStub() - rpc_error = mock_grpc.RpcError() - stub.set_reboot_response(side_effect=rpc_error) - with self.assertRaises(mock_grpc.RpcError): - stub.Reboot(system_pb2.RebootRequest(), timeout=10) + self.assertEqual(len(self.server.system.reboot_calls), 1) + self.assertEqual(self.server.system.reboot_calls[0].method, system_pb2.HALT) + self.assertEqual(self.server.system.reboot_calls[0].message, "test") + + def test_reboot_error(self): + self.server.system.set_reboot_response( + error_code=grpc.StatusCode.UNAVAILABLE, + error_message="DPU unreachable", + ) + with GnoiClient(self.server.target) as client: + with self.assertRaises(grpc.RpcError) as ctx: + client.system.Reboot(system_pb2.RebootRequest(), timeout=5) + self.assertEqual(ctx.exception.code(), grpc.StatusCode.UNAVAILABLE) + self.assertIn("DPU unreachable", ctx.exception.details()) def test_reboot_status_default_complete(self): - stub = FakeSystemStub() - resp = stub.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) + with GnoiClient(self.server.target) as client: + resp = client.system.RebootStatus( + system_pb2.RebootStatusRequest(), timeout=5 + ) self.assertFalse(resp.active) - self.assertEqual(resp.status.status, system_pb2.RebootStatus.Status.STATUS_SUCCESS) + self.assertEqual( + resp.status.status, + system_pb2.RebootStatus.Status.STATUS_SUCCESS, + ) def test_reboot_status_active(self): - stub = FakeSystemStub() - stub.set_reboot_status(active=True) - resp = stub.RebootStatus(system_pb2.RebootStatusRequest()) + self.server.system.set_reboot_status(active=True) + with GnoiClient(self.server.target) as client: + resp = client.system.RebootStatus( + system_pb2.RebootStatusRequest(), timeout=5 + ) self.assertTrue(resp.active) def test_reboot_status_sequence(self): - """Test list side_effect for polling scenarios.""" - stub = FakeSystemStub() + """Polling scenario: active → active → done.""" active_resp = system_pb2.RebootStatusResponse() active_resp.active = True + done_resp = system_pb2.RebootStatusResponse() done_resp.active = False done_resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS - stub.set_reboot_status(side_effect=[active_resp, active_resp, done_resp]) + self.server.system.set_reboot_status_sequence([active_resp, active_resp, done_resp]) + + with GnoiClient(self.server.target) as client: + r1 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertTrue(r1.active) + r2 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertTrue(r2.active) + r3 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertFalse(r3.active) - r1 = stub.RebootStatus(system_pb2.RebootStatusRequest()) - self.assertTrue(r1.active) - r2 = stub.RebootStatus(system_pb2.RebootStatusRequest()) - self.assertTrue(r2.active) - r3 = stub.RebootStatus(system_pb2.RebootStatusRequest()) - self.assertFalse(r3.active) - self.assertEqual(len(stub.reboot_status_calls), 3) + self.assertEqual(len(self.server.system.reboot_status_calls), 3) def test_reboot_status_sequence_with_error(self): - """Test mixed responses and errors in sequence.""" - stub = FakeSystemStub() - rpc_error = mock_grpc.RpcError() + """Polling with transient error then success.""" done_resp = system_pb2.RebootStatusResponse() done_resp.active = False + done_resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS + + self.server.system.set_reboot_status_sequence([ + (grpc.StatusCode.UNAVAILABLE, "transient"), + done_resp, + ]) - stub.set_reboot_status(side_effect=[rpc_error, done_resp]) + with GnoiClient(self.server.target) as client: + with self.assertRaises(grpc.RpcError): + client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + r2 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertFalse(r2.active) - with self.assertRaises(mock_grpc.RpcError): - stub.RebootStatus(system_pb2.RebootStatusRequest()) - r2 = stub.RebootStatus(system_pb2.RebootStatusRequest()) - self.assertFalse(r2.active) + def test_reboot_status_error(self): + self.server.system.set_reboot_status( + error_code=grpc.StatusCode.INTERNAL, + error_message="internal failure", + ) + with GnoiClient(self.server.target) as client: + with self.assertRaises(grpc.RpcError) as ctx: + client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertEqual(ctx.exception.code(), grpc.StatusCode.INTERNAL) def test_cancel_reboot(self): - stub = FakeSystemStub() - resp = stub.CancelReboot(system_pb2.CancelRebootRequest(), timeout=30) + with GnoiClient(self.server.target) as client: + resp = client.system.CancelReboot( + system_pb2.CancelRebootRequest(message="abort"), timeout=5 + ) self.assertIsNotNone(resp) - self.assertEqual(len(stub.cancel_reboot_calls), 1) + self.assertEqual(len(self.server.system.cancel_reboot_calls), 1) - def test_call_history_tracks_requests(self): - stub = FakeSystemStub() - req = system_pb2.RebootRequest(method=system_pb2.HALT, message="test") - stub.Reboot(req, timeout=60) - self.assertEqual(stub.reboot_calls[0]['request'], req) + def test_reset_clears_history(self): + with GnoiClient(self.server.target) as client: + client.system.Reboot(system_pb2.RebootRequest(), timeout=5) + self.assertEqual(len(self.server.system.reboot_calls), 1) + self.server.reset() + self.assertEqual(len(self.server.system.reboot_calls), 0) -class TestFakeGnoiClient(unittest.TestCase): +class TestFakeGnoiServer(unittest.TestCase): + """Test server lifecycle.""" def test_context_manager(self): - with FakeGnoiClient() as client: - self.assertIsNotNone(client.system) - - def test_system_stub_persists(self): - """Same stub instance across property accesses.""" - fake = FakeGnoiClient() - self.assertIs(fake.system, fake.system) + with FakeGnoiServer() as server: + self.assertIn("localhost:", server.target) + with GnoiClient(server.target) as client: + resp = client.system.RebootStatus( + system_pb2.RebootStatusRequest(), timeout=5 + ) + self.assertFalse(resp.active) def test_end_to_end_reboot_flow(self): - """Simulate a reboot + poll flow like gnoi_shutdown_daemon.""" - fake = FakeGnoiClient() - - # Configure: reboot succeeds, status shows active then done - active_resp = system_pb2.RebootStatusResponse() - active_resp.active = True - done_resp = system_pb2.RebootStatusResponse() - done_resp.active = False - done_resp.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS - fake.system.set_reboot_status(side_effect=[active_resp, done_resp]) - - with fake as client: - # Send reboot - client.system.Reboot( - system_pb2.RebootRequest(method=system_pb2.HALT), - timeout=60, - ) - - # Poll status - r1 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) - self.assertTrue(r1.active) - r2 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=10) - self.assertFalse(r2.active) - - self.assertEqual(len(fake.system.reboot_calls), 1) - self.assertEqual(len(fake.system.reboot_status_calls), 2) - - def test_close_is_noop(self): - fake = FakeGnoiClient() - fake.close() # Should not raise + """Full reboot + poll flow like gnoi_shutdown_daemon.""" + with FakeGnoiServer() as server: + active = system_pb2.RebootStatusResponse() + active.active = True + done = system_pb2.RebootStatusResponse() + done.active = False + done.status.status = system_pb2.RebootStatus.Status.STATUS_SUCCESS + server.system.set_reboot_status_sequence([active, done]) + + with GnoiClient(server.target) as client: + # Send reboot + client.system.Reboot( + system_pb2.RebootRequest(method=system_pb2.HALT, message="shutdown"), + timeout=5, + ) + # Poll + r1 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertTrue(r1.active) + r2 = client.system.RebootStatus(system_pb2.RebootStatusRequest(), timeout=5) + self.assertFalse(r2.active) + + self.assertEqual(len(server.system.reboot_calls), 1) + self.assertEqual(server.system.reboot_calls[0].method, system_pb2.HALT) + self.assertEqual(len(server.system.reboot_status_calls), 2) if __name__ == '__main__': From 0ce4db8fede615645b02590eb5bc3228996bfdbe Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Thu, 2 Apr 2026 15:47:31 +0000 Subject: [PATCH 4/4] Fix protobuf version mismatch: regenerate pb2 stubs with protobuf 4.x The generated protobuf stubs were created with protobuf 6.31.1 which uses 'runtime_version' import not available in the build image's protobuf 4.25.9. Regenerated using grpcio-tools 1.60.1 + protobuf 4.25.9 for compatibility with the SONiC build environment. Signed-off-by: Dawei Huang --- .../sonic_py_common/grpc/gnoi/common_pb2.py | 16 +- .../grpc/gnoi/common_pb2_grpc.py | 20 -- .../sonic_py_common/grpc/gnoi/system_pb2.py | 18 +- .../grpc/gnoi/system_pb2_grpc.py | 183 ++++-------------- .../sonic_py_common/grpc/gnoi/types_pb2.py | 18 +- .../grpc/gnoi/types_pb2_grpc.py | 20 -- 6 files changed, 47 insertions(+), 228 deletions(-) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py index 4397ea9640c..f03813c2dba 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2.py @@ -1,22 +1,12 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE # source: github.com/openconfig/gnoi/common/common.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'github.com/openconfig/gnoi/common/common.proto' -) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -30,8 +20,8 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.openconfig.gnoi.common.common_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'Z!github.com/openconfig/gnoi/common' _globals['_REMOTEDOWNLOAD']._serialized_start=110 _globals['_REMOTEDOWNLOAD']._serialized_end=351 diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py index b2660b454d8..2daafffebfc 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/common_pb2_grpc.py @@ -1,24 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc -import warnings - -GRPC_GENERATED_VERSION = '1.80.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in github.com/openconfig/gnoi/common/common_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py index 3fcb2cc9af6..6dcad656b2c 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2.py @@ -1,22 +1,12 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE # source: github.com/openconfig/gnoi/system/system.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'github.com/openconfig/gnoi/system/system.proto' -) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -31,10 +21,10 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.openconfig.gnoi.system.system_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'Z!github.com/openconfig/gnoi/system\322>\0051.4.0' - _globals['_TRACEROUTERESPONSE_MPLSENTRY']._loaded_options = None + _globals['_TRACEROUTERESPONSE_MPLSENTRY']._options = None _globals['_TRACEROUTERESPONSE_MPLSENTRY']._serialized_options = b'8\001' _globals['_REBOOTMETHOD']._serialized_start=3141 _globals['_REBOOTMETHOD']._serialized_end=3241 diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py index f2a0c0162f5..84514f9956f 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/system_pb2_grpc.py @@ -1,29 +1,9 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc -import warnings from sonic_py_common.grpc.gnoi import system_pb2 as github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2 -GRPC_GENERATED_VERSION = '1.80.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in github.com/openconfig/gnoi/system/system_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) - class SystemStub(object): """The gNOI service is a collection of operational RPC's that allow for the @@ -40,47 +20,47 @@ def __init__(self, channel): '/gnoi.system.System/Ping', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingResponse.FromString, - _registered_method=True) + ) self.Traceroute = channel.unary_stream( '/gnoi.system.System/Traceroute', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteResponse.FromString, - _registered_method=True) + ) self.Time = channel.unary_unary( '/gnoi.system.System/Time', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeResponse.FromString, - _registered_method=True) + ) self.SetPackage = channel.stream_unary( '/gnoi.system.System/SetPackage', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageResponse.FromString, - _registered_method=True) + ) self.SwitchControlProcessor = channel.unary_unary( '/gnoi.system.System/SwitchControlProcessor', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorResponse.FromString, - _registered_method=True) + ) self.Reboot = channel.unary_unary( '/gnoi.system.System/Reboot', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootResponse.FromString, - _registered_method=True) + ) self.RebootStatus = channel.unary_unary( '/gnoi.system.System/RebootStatus', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusResponse.FromString, - _registered_method=True) + ) self.CancelReboot = channel.unary_unary( '/gnoi.system.System/CancelReboot', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootResponse.FromString, - _registered_method=True) + ) self.KillProcess = channel.unary_unary( '/gnoi.system.System/KillProcess', request_serializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessRequest.SerializeToString, response_deserializer=github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessResponse.FromString, - _registered_method=True) + ) class SystemServicer(object): @@ -227,7 +207,6 @@ def add_SystemServicer_to_server(servicer, server): generic_handler = grpc.method_handlers_generic_handler( 'gnoi.system.System', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers('gnoi.system.System', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -247,21 +226,11 @@ def Ping(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_stream( - request, - target, - '/gnoi.system.System/Ping', + return grpc.experimental.unary_stream(request, target, '/gnoi.system.System/Ping', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.PingResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def Traceroute(request, @@ -274,21 +243,11 @@ def Traceroute(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_stream( - request, - target, - '/gnoi.system.System/Traceroute', + return grpc.experimental.unary_stream(request, target, '/gnoi.system.System/Traceroute', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TracerouteResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def Time(request, @@ -301,21 +260,11 @@ def Time(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/gnoi.system.System/Time', + return grpc.experimental.unary_unary(request, target, '/gnoi.system.System/Time', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.TimeResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def SetPackage(request_iterator, @@ -328,21 +277,11 @@ def SetPackage(request_iterator, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_unary( - request_iterator, - target, - '/gnoi.system.System/SetPackage', + return grpc.experimental.stream_unary(request_iterator, target, '/gnoi.system.System/SetPackage', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SetPackageResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def SwitchControlProcessor(request, @@ -355,21 +294,11 @@ def SwitchControlProcessor(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/gnoi.system.System/SwitchControlProcessor', + return grpc.experimental.unary_unary(request, target, '/gnoi.system.System/SwitchControlProcessor', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.SwitchControlProcessorResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def Reboot(request, @@ -382,21 +311,11 @@ def Reboot(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/gnoi.system.System/Reboot', + return grpc.experimental.unary_unary(request, target, '/gnoi.system.System/Reboot', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def RebootStatus(request, @@ -409,21 +328,11 @@ def RebootStatus(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/gnoi.system.System/RebootStatus', + return grpc.experimental.unary_unary(request, target, '/gnoi.system.System/RebootStatus', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.RebootStatusResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def CancelReboot(request, @@ -436,21 +345,11 @@ def CancelReboot(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/gnoi.system.System/CancelReboot', + return grpc.experimental.unary_unary(request, target, '/gnoi.system.System/CancelReboot', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.CancelRebootResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def KillProcess(request, @@ -463,18 +362,8 @@ def KillProcess(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/gnoi.system.System/KillProcess', + return grpc.experimental.unary_unary(request, target, '/gnoi.system.System/KillProcess', github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessRequest.SerializeToString, github_dot_com_dot_openconfig_dot_gnoi_dot_system_dot_system__pb2.KillProcessResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py index 549fd9bc8bb..5f22d2aca45 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2.py @@ -1,22 +1,12 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE # source: github.com/openconfig/gnoi/types/types.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'github.com/openconfig/gnoi/types/types.proto' -) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -30,10 +20,10 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.openconfig.gnoi.types.types_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'Z github.com/openconfig/gnoi/types' - _globals['_PATHELEM_KEYENTRY']._loaded_options = None + _globals['_PATHELEM_KEYENTRY']._options = None _globals['_PATHELEM_KEYENTRY']._serialized_options = b'8\001' _globals['_L3PROTOCOL']._serialized_start=514 _globals['_L3PROTOCOL']._serialized_end=563 diff --git a/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py index 5dce0282d74..2daafffebfc 100644 --- a/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py +++ b/src/sonic-py-common/sonic_py_common/grpc/gnoi/types_pb2_grpc.py @@ -1,24 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc -import warnings - -GRPC_GENERATED_VERSION = '1.80.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in github.com/openconfig/gnoi/types/types_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - )