[sonic-grpc]: Add gNOI Python gRPC client framework wheel#28341
Conversation
Add a new standalone `sonic-grpc` Python distribution providing a native,
in-process gNOI gRPC client for SONiC, and wire it into docker-sonic-mgmt.
sonic_grpc.gnoi provides:
- GnoiClient: a context-managed gRPC channel wrapper exposing gNOI service
stubs as properties (client.system.*, client.file.*), over insecure TCP,
unix:// UDS, or mTLS. Service-agnostic; new services plug in as properties.
- Vendored flat gNOI proto stubs (System, File, types, common) regenerated
against protobuf 4.x/5.x. No stub generation or gnoi.proto files needed at
install time.
- FakeGnoiServer test doubles for offline unit testing of gNOI-driven code.
Packaged as its own minimal-dependency wheel (grpcio + protobuf only) so
lightweight consumers - notably the docker-sonic-mgmt test image - can install
it without pulling in a heavier package or its dependency tree. It installs
anywhere, including:
pip install "git+https://github.com/sonic-net/sonic-buildimage#subdirectory=src/sonic-grpc"
Wired into docker-sonic-mgmt via rules/sonic-grpc.mk (SONIC_GRPC_PY3) and a
uv-based install block in the Dockerfile (the image's /opt/venv has no pip, so
the generic pip3 install_python_wheels macro is not used). grpcio is added to
the image's package list as an explicit dependency.
The gNOI client code originates from the framework in sonic-net#27760; this PR delivers
it as a reusable standalone wheel that sonic-net#27760 and the sonic-mgmt gNOI test
suite can both consume.
Signed-off-by: Dawei Huang <daweihuang@microsoft.com>
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
The wheel-install block uses the copy_files macro, which must be imported at the top of the Dockerfile.j2 (as every other wheel-consuming Dockerfile does). Without the import the j2 template fails to render. Add the import. Signed-off-by: Dawei Huang <daweihuang@microsoft.com>
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Every buildable component with GIT_CONTENT_SHA caching has a matching .dep file (included via Makefile.cache) that declares its DEP_FILES and cache mode. Add rules/sonic-grpc.dep for the new SONIC_GRPC_PY3 wheel, mirroring rules/sonic-py-common.dep, so the wheel participates in content-SHA build caching and rebuilds when its sources change. Signed-off-by: Dawei Huang <daweihuang@microsoft.com>
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
This PR adds a new standalone Python wheel (sonic-grpc) under src/sonic-grpc/ that provides a minimal gNOI gRPC client framework (client wrapper + vendored gNOI proto stubs + test fakes) and wires that wheel into the docker-sonic-mgmt image build so consumers (notably sonic-mgmt’s native gNOI tests) can install it without pulling in a larger dependency tree.
Changes:
- Introduces
sonic_grpc.gnoi.GnoiClientplus aFakeGnoiServertest double, and vendors generated gNOI stubs (System/File/types/common) in the newsonic-grpcwheel. - Adds build rules for producing the new wheel and includes it in
SONIC_PYTHON_WHEELS. - Updates
docker-sonic-mgmtto include and install locally-built wheels viauv, and addsgrpcioto the image’s Python dependencies.
Reviewed changes
Copilot reviewed 13 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/sonic-grpc/sonic_grpc/gnoi/types_pb2.py | Vendored generated protobuf stubs for gNOI types. |
| src/sonic-grpc/sonic_grpc/gnoi/types_pb2_grpc.py | Placeholder generated gRPC module for types (no services). |
| src/sonic-grpc/sonic_grpc/gnoi/common_pb2.py | Vendored generated protobuf stubs for gNOI common messages. |
| src/sonic-grpc/sonic_grpc/gnoi/common_pb2_grpc.py | Placeholder generated gRPC module for common (no services). |
| src/sonic-grpc/sonic_grpc/gnoi/system_pb2.py | Vendored generated protobuf stubs for gNOI System service messages. |
| src/sonic-grpc/sonic_grpc/gnoi/system_pb2_grpc.py | Vendored generated gRPC stubs for gNOI System service. |
| src/sonic-grpc/sonic_grpc/gnoi/file_pb2.py | Vendored generated protobuf stubs for gNOI File service messages. |
| src/sonic-grpc/sonic_grpc/gnoi/file_pb2_grpc.py | Vendored generated gRPC stubs for gNOI File service. |
| src/sonic-grpc/sonic_grpc/gnoi/client.py | Implements GnoiClient channel wrapper with per-service stub properties. |
| src/sonic-grpc/sonic_grpc/gnoi/testing.py | Implements FakeGnoiServer and FakeSystemServicer test doubles. |
| src/sonic-grpc/sonic_grpc/gnoi/init.py | Package entry point exporting GnoiClient. |
| src/sonic-grpc/sonic_grpc/init.py | Top-level sonic_grpc package docstring and future roadmap. |
| src/sonic-grpc/setup.py | Wheel metadata and runtime dependency declarations. |
| rules/sonic-grpc.mk | Adds wheel definition and registers it in SONIC_PYTHON_WHEELS. |
| rules/sonic-grpc.dep | Adds dependency tracking for caching/rebuild correctness. |
| rules/docker-sonic-mgmt.mk | Adds the sonic-grpc wheel to the docker-sonic-mgmt wheel set. |
| dockers/docker-sonic-mgmt/Dockerfile.j2 | Installs locally-built wheels via uv and adds grpcio to dependencies. |
Files not reviewed (4)
- src/sonic-grpc/sonic_grpc/gnoi/common_pb2.py: Generated file
- src/sonic-grpc/sonic_grpc/gnoi/file_pb2.py: Generated file
- src/sonic-grpc/sonic_grpc/gnoi/system_pb2.py: Generated file
- src/sonic-grpc/sonic_grpc/gnoi/types_pb2.py: Generated file
| # sonic-grpc Python wheel (gNOI / gRPC client framework for SONiC) | ||
|
|
||
| SONIC_GRPC_PY3 = sonic_grpc-1.0-py3-none-any.whl | ||
| $(SONIC_GRPC_PY3)_SRC_PATH = $(SRC_PATH)/sonic-grpc | ||
| $(SONIC_GRPC_PY3)_PYTHON_VERSION = 3 | ||
| SONIC_PYTHON_WHEELS += $(SONIC_GRPC_PY3) |
| 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, | ||
| 5, | ||
| 29, | ||
| 0, | ||
| '', | ||
| 'github.com/openconfig/gnoi/file/file.proto' | ||
| ) |
| GRPC_GENERATED_VERSION = '1.70.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},' | ||
| + f' but the generated code in github.com/openconfig/gnoi/file/file_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}.' | ||
| ) |
| install_requires=[ | ||
| 'grpcio', | ||
| 'protobuf>=4.21', | ||
| ], |
The SONiC bookworm/trixie wheel build rule runs `pytest` on each wheel's source unless <pkg>_TEST=n. sonic-grpc had no tests, so pytest collected 0 items and the wheel target failed. Add the gNOI client and FakeGnoiServer unit tests (31 tests, lifted from the framework in sonic-net#27760) under src/sonic-grpc/tests/, and declare a `testing` extra so the build's `pip install ".[testing]"` step is satisfied. Verified: `target/python-wheels/bookworm/sonic_grpc-1.0-py3-none-any.whl` now builds with `31 passed` in the test step. Signed-off-by: Dawei Huang <daweihuang@microsoft.com>
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 20 changed files in this pull request and generated 2 comments.
Files not reviewed (4)
- src/sonic-grpc/sonic_grpc/gnoi/common_pb2.py: Generated file
- src/sonic-grpc/sonic_grpc/gnoi/file_pb2.py: Generated file
- src/sonic-grpc/sonic_grpc/gnoi/system_pb2.py: Generated file
- src/sonic-grpc/sonic_grpc/gnoi/types_pb2.py: Generated file
| install_requires=[ | ||
| 'grpcio', | ||
| 'protobuf>=4.21', | ||
| ], |
| self.assertEqual(len(self.server.system.reboot_status_calls), 1) | ||
| self.assertEqual(len(self.server.system.cancel_reboot_calls), 1) | ||
|
|
||
| def test_credentials_none_uses_insecure_channel(self): |
|
@ronan-nexthop @ndas7 cannot tag you for review so FYI. Following a similar approach as #28339 @sneelam20 for review |
|
Interesting, is the a real use case for this on the box? If so the approach you take is good, althought I would have thought most of these gNOI operations would be handled off box? I appreciate gNOI RPC's don't change that frequently, in any case the regeneration story here isn't the best, appears file_pb2.py was generated by protobuf 5.29 tooling and grpcio-tools 1.70. system_pb2.py, types_pb2.py, common_pb2.py were generated by protobuf 4.25 tooling, and system_pb2_grpc.py by an older grpcio-tools. setup.py declares protobuf>=4.21 and unpinned grpcio. It would probably be good to have some form of regeneration script |
|
@ronan-nexthop Yes. There are Nvidia platform with multiple cards and a midplane network. Also Microsoft also have some orchestration that uses gnoi as a way to standardize on device operation. This change is mostly for porting to sonic-mgmt instead of on the box container. (Although as a byproduct we also want it to be available on the box). Agree with the generation. Probably can be built into the build system. |
Related: sonic-net/sonic-mgmt#26039
Why I did it
The new PTF-free native gNOI test suite in sonic-mgmt (and other in-box
components) need an in-process Python gNOI gRPC client. This adds it as a
reusable, minimal-dependency standalone wheel so lightweight consumers — notably
the
docker-sonic-mgmttest image — can install it without pulling in a heavierpackage or its dependency tree.
The gNOI client code originates from the framework in #27760; this PR delivers
it as a standalone
sonic-grpcwheel that #27760 and the sonic-mgmt gNOI testsuite can both consume.
How I did it
Added a new
src/sonic-grpc/package (sonic_grpc):sonic_grpc.gnoi.GnoiClient— context-managed gRPC channel wrapper exposinggNOI service stubs as properties (
client.system.*,client.file.*) overinsecure TCP,
unix://UDS, or mTLS. Service-agnostic.against protobuf 4.x/5.x. No stub generation or
.protofiles at install time.FakeGnoiServertest doubles for offline unit tests.setup.py: depends only ongrpcio+protobuf>=4.21.Wired into
docker-sonic-mgmt:rules/sonic-grpc.mk(SONIC_GRPC_PY3, added toSONIC_PYTHON_WHEELS).rules/docker-sonic-mgmt.mk:+= $(SONIC_GRPC_PY3)to_PYTHON_WHEELS.Dockerfile.j2: a uv-based install of the wheel (the image/opt/venvhas nopip, so the generic
pip3install_python_wheelsmacro is not used), plusgrpcioadded to the package list as an explicit dependency.How to verify it
cd src/sonic-grpc && python3 -m pytest(client + FakeGnoiServer).tests/gnoi/suite ([gnoi] PTF-free native gNOI test suite (sonic_grpc.GnoiClient) sonic-mgmt#26039)drives
System.TimeandFile.Statover this client against a DUT.build installs the wheel).
Which release branch to backport (provide reason below if selected)
None.
Description for the changelog
Add
sonic-grpcwheel: native Python gNOI gRPC client framework for SONiC.Dependencies
consume this wheel.