diff --git a/doc/python/sphinx/grpc_admin.rst b/doc/python/sphinx/grpc_admin.rst new file mode 100644 index 00000000000..40ae9f0ba0e --- /dev/null +++ b/doc/python/sphinx/grpc_admin.rst @@ -0,0 +1,14 @@ +gRPC Admin +========== + +What is gRPC Admin? +--------------------------------------------- + +It's a convenient API to improve the usability of creating a gRPC server with admin services to expose states in the gRPC library. + +Design Document `gRPC Admin Interface `_ + +Module Contents +--------------- + +.. automodule:: grpc_admin diff --git a/doc/python/sphinx/grpc_csds.rst b/doc/python/sphinx/grpc_csds.rst new file mode 100644 index 00000000000..e1550c7ff01 --- /dev/null +++ b/doc/python/sphinx/grpc_csds.rst @@ -0,0 +1,14 @@ +gRPC CSDS +========= + +What is gRPC CSDS? +--------------------------------------------- + +In short, it's a xDS configuration dump protocol. + +Design Document `gRPC CSDS `_ + +Module Contents +--------------- + +.. automodule:: grpc_csds diff --git a/doc/python/sphinx/index.rst b/doc/python/sphinx/index.rst index c033542cf27..2bdd60b826d 100644 --- a/doc/python/sphinx/index.rst +++ b/doc/python/sphinx/index.rst @@ -11,7 +11,9 @@ API Reference grpc grpc_asyncio + grpc_admin grpc_channelz + grpc_csds grpc_health_checking grpc_reflection grpc_status diff --git a/src/python/grpcio_admin/.gitignore b/src/python/grpcio_admin/.gitignore new file mode 100644 index 00000000000..6f95c5f61ae --- /dev/null +++ b/src/python/grpcio_admin/.gitignore @@ -0,0 +1,6 @@ +*.proto +*_pb2.py +*_pb2_grpc.py +build/ +grpcio_admin.egg-info/ +dist/ diff --git a/src/python/grpcio_admin/MANIFEST.in b/src/python/grpcio_admin/MANIFEST.in new file mode 100644 index 00000000000..eb7e029a4ba --- /dev/null +++ b/src/python/grpcio_admin/MANIFEST.in @@ -0,0 +1,4 @@ +include grpc_version.py +recursive-include grpc_admin *.py +global-exclude *.pyc +include LICENSE diff --git a/src/python/grpcio_admin/README.rst b/src/python/grpcio_admin/README.rst new file mode 100644 index 00000000000..d9959840eb5 --- /dev/null +++ b/src/python/grpcio_admin/README.rst @@ -0,0 +1,23 @@ +gRPC Python Admin Interface Package +=================================== + +Debugging gRPC library can be a complex task. There are many configurations and +internal states, which will affect the behavior of the library. This Python +package will be the collection of admin services that are exposing debug +information. Currently, it includes: + +* Channel tracing metrics (grpcio-channelz) +* Client Status Discovery Service (grpcio-csds) + +Here is a snippet to create an admin server on "localhost:50051": + + server = grpc.server(ThreadPoolExecutor()) + port = server.add_insecure_port('localhost:50051') + grpc_admin.add_admin_servicers(self._server) + server.start() + +Welcome to explore the admin services with CLI tool "grpcdebug": +https://github.com/grpc-ecosystem/grpcdebug. + +For any issues or suggestions, please send to +https://github.com/grpc/grpc/issues. diff --git a/src/python/grpcio_admin/grpc_admin/BUILD.bazel b/src/python/grpcio_admin/grpc_admin/BUILD.bazel new file mode 100644 index 00000000000..bd81a665497 --- /dev/null +++ b/src/python/grpcio_admin/grpc_admin/BUILD.bazel @@ -0,0 +1,25 @@ +# Copyright 2021 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "grpc_admin", + srcs = glob(["*.py"]), + imports = ["../"], + deps = [ + "//src/python/grpcio_channelz/grpc_channelz/v1:grpc_channelz", + "//src/python/grpcio_csds/grpc_csds", + ], +) diff --git a/src/python/grpcio_admin/grpc_admin/__init__.py b/src/python/grpcio_admin/grpc_admin/__init__.py new file mode 100644 index 00000000000..96dfca1e316 --- /dev/null +++ b/src/python/grpcio_admin/grpc_admin/__init__.py @@ -0,0 +1,42 @@ +# Copyright 2021 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""gRPC Python's Admin interface.""" + +import grpc_csds +from grpc_channelz.v1 import channelz + + +def add_admin_servicers(server): + """Register admin servicers to a server. + + gRPC provides some predefined admin services to make debugging easier by + exposing gRPC's internal states. Each existing admin service is packaged as + a separate library, and the documentation of the predefined admin services + is usually scattered. It can be time consuming to get the dependency + management, module initialization, and library import right for each one of + them. + + This API provides a convenient way to create a gRPC server to expose admin + services. With this, any new admin services that you may add in the future + are automatically available via the admin interface just by upgrading your + gRPC version. + + Args: + server: A gRPC server to which all admin services will be added. + """ + channelz.add_channelz_servicer(server) + grpc_csds.add_csds_servicer(server) + + +__all__ = ['add_admin_servicers'] diff --git a/src/python/grpcio_admin/grpc_version.py b/src/python/grpcio_admin/grpc_version.py new file mode 100644 index 00000000000..fffb42c0c3b --- /dev/null +++ b/src/python/grpcio_admin/grpc_version.py @@ -0,0 +1,17 @@ +# Copyright 2021 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_admin/grpc_version.py.template`!!! + +VERSION = '1.38.0.dev0' diff --git a/src/python/grpcio_admin/setup.py b/src/python/grpcio_admin/setup.py new file mode 100644 index 00000000000..6c2438ef5ee --- /dev/null +++ b/src/python/grpcio_admin/setup.py @@ -0,0 +1,60 @@ +# Copyright 2021 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Setup module for admin interface in gRPC Python.""" + +import os +import sys + +import setuptools + +_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__)) +_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst') + +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our local modules. +import grpc_version + +CLASSIFIERS = [ + 'Development Status :: 5 - Production/Stable', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', + 'License :: OSI Approved :: Apache Software License', +] + +PACKAGE_DIRECTORIES = { + '': '.', +} + +INSTALL_REQUIRES = ( + 'grpcio-channelz>={version}'.format(version=grpc_version.VERSION), + 'grpcio-csds>={version}'.format(version=grpc_version.VERSION), +) +SETUP_REQUIRES = INSTALL_REQUIRES + +setuptools.setup(name='grpcio-admin', + version=grpc_version.VERSION, + license='Apache License 2.0', + description='a collection of admin services', + long_description=open(_README_PATH, 'r').read(), + author='The gRPC Authors', + author_email='grpc-io@googlegroups.com', + classifiers=CLASSIFIERS, + url='https://grpc.io', + package_dir=PACKAGE_DIRECTORIES, + packages=setuptools.find_packages('.'), + install_requires=INSTALL_REQUIRES, + setup_requires=SETUP_REQUIRES) diff --git a/src/python/grpcio_csds/.gitignore b/src/python/grpcio_csds/.gitignore index 0c5da6b5af1..24f7dd994aa 100644 --- a/src/python/grpcio_csds/.gitignore +++ b/src/python/grpcio_csds/.gitignore @@ -2,5 +2,5 @@ *_pb2.py *_pb2_grpc.py build/ -grpcio_channelz.egg-info/ +grpcio_csds.egg-info/ dist/ diff --git a/src/python/grpcio_csds/grpc_csds/__init__.py b/src/python/grpcio_csds/grpc_csds/__init__.py index ed463b7262c..e30925585ca 100644 --- a/src/python/grpcio_csds/grpc_csds/__init__.py +++ b/src/python/grpcio_csds/grpc_csds/__init__.py @@ -42,6 +42,16 @@ class ClientStatusDiscoveryServiceServicer( def add_csds_servicer(server): + """Register CSDS servicer to a server. + + CSDS is part of xDS protocol used to expose in-effective traffic + configuration (or xDS resources). It focuses on simplify the debugging of + unexpected routing behaviors, which could be due to a misconfiguration, + unhealthy backends or issues in the control or data plane. + + Args: + server: A gRPC server to which the CSDS service will be added. + """ csds_pb2_grpc.add_ClientStatusDiscoveryServiceServicer_to_server( ClientStatusDiscoveryServiceServicer(), server) diff --git a/src/python/grpcio_tests/tests/admin/BUILD.bazel b/src/python/grpcio_tests/tests/admin/BUILD.bazel new file mode 100644 index 00000000000..d94be8c7cbf --- /dev/null +++ b/src/python/grpcio_tests/tests/admin/BUILD.bazel @@ -0,0 +1,26 @@ +# Copyright 2021 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//bazel:python_rules.bzl", "py2and3_test") + +py2and3_test( + name = "test_admin", + size = "small", + srcs = ["test_admin.py"], + main = "test_admin.py", + deps = [ + "//src/python/grpcio/grpc:grpcio", + "//src/python/grpcio_admin/grpc_admin", + ], +) diff --git a/src/python/grpcio_tests/tests/admin/test_admin.py b/src/python/grpcio_tests/tests/admin/test_admin.py new file mode 100644 index 00000000000..484cfa04db2 --- /dev/null +++ b/src/python/grpcio_tests/tests/admin/test_admin.py @@ -0,0 +1,55 @@ +# Copyright 2021 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""A test to ensure that admin services are registered correctly.""" + +import logging +import unittest +from concurrent.futures import ThreadPoolExecutor + +import grpc +import grpc_admin +from grpc_csds import csds_pb2, csds_pb2_grpc +from grpc_channelz.v1 import channelz_pb2, channelz_pb2_grpc + + +class TestAdmin(unittest.TestCase): + + def setUp(self): + self._server = grpc.server(ThreadPoolExecutor()) + port = self._server.add_insecure_port('localhost:0') + grpc_admin.add_admin_servicers(self._server) + self._server.start() + + self._channel = grpc.insecure_channel('localhost:%s' % port) + + def tearDown(self): + self._channel.close() + self._server.stop(0) + + def test_has_csds(self): + stub = csds_pb2_grpc.ClientStatusDiscoveryServiceStub(self._channel) + resp = stub.FetchClientStatus(csds_pb2.ClientStatusRequest()) + # No exception raised and the response is valid + self.assertGreater(len(resp.config), 0) + + def test_has_channelz(self): + stub = channelz_pb2_grpc.ChannelzStub(self._channel) + resp = stub.GetTopChannels(channelz_pb2.GetTopChannelsRequest()) + # No exception raised and the response is valid + self.assertGreater(len(resp.channel), 0) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + unittest.main(verbosity=2) diff --git a/templates/src/python/grpcio_admin/grpc_version.py.template b/templates/src/python/grpcio_admin/grpc_version.py.template new file mode 100644 index 00000000000..45a4fbc631c --- /dev/null +++ b/templates/src/python/grpcio_admin/grpc_version.py.template @@ -0,0 +1,19 @@ +%YAML 1.2 +--- | + # Copyright 2021 The gRPC Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_admin/grpc_version.py.template`!!! + + VERSION = '${settings.python_version.pep440()}' diff --git a/tools/distrib/python/grpc_prefixed/generate.py b/tools/distrib/python/grpc_prefixed/generate.py index e383df994c9..c685a1e1131 100644 --- a/tools/distrib/python/grpc_prefixed/generate.py +++ b/tools/distrib/python/grpc_prefixed/generate.py @@ -139,6 +139,11 @@ def main(): name_long='gRPC Client Status Discovery Service', destination_package='grpcio-csds')) + generate_package( + PackageMeta(name='grpc-admin', + name_long='gRPC Admin Interface', + destination_package='grpcio-admin')) + if __name__ == "__main__": logging.basicConfig(level=logging.INFO) diff --git a/tools/run_tests/artifacts/build_artifact_python.sh b/tools/run_tests/artifacts/build_artifact_python.sh index 0cc6e00b1f5..64f68cf3dbc 100755 --- a/tools/run_tests/artifacts/build_artifact_python.sh +++ b/tools/run_tests/artifacts/build_artifact_python.sh @@ -188,6 +188,15 @@ then ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_csds/setup.py \ sdist bdist_wheel cp -r src/python/grpcio_csds/dist/* "$ARTIFACT_DIR" + + # Build grpcio_admin source distribution and it needs the cutting-edge version + # of Channelz and CSDS to be installed. + "${PIP}" install --upgrade xds-protos==0.0.8 + "${PIP}" install grpcio-channelz --no-index --find-links "file://$ARTIFACT_DIR/" + "${PIP}" install grpcio-csds --no-index --find-links "file://$ARTIFACT_DIR/" + ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_admin/setup.py \ + sdist bdist_wheel + cp -r src/python/grpcio_admin/dist/* "$ARTIFACT_DIR" fi if [ "$GRPC_SKIP_TWINE_CHECK" == "" ] diff --git a/tools/run_tests/helper_scripts/build_python.sh b/tools/run_tests/helper_scripts/build_python.sh index 7f95d79477d..7b928d14c9c 100755 --- a/tools/run_tests/helper_scripts/build_python.sh +++ b/tools/run_tests/helper_scripts/build_python.sh @@ -223,6 +223,9 @@ pip_install_dir "$ROOT/src/python/grpcio_status" # Build/install csds pip_install_dir "$ROOT/src/python/grpcio_csds" +# Build/install admin +pip_install_dir "$ROOT/src/python/grpcio_admin" + # Install testing pip_install_dir "$ROOT/src/python/grpcio_testing"