diff --git a/cni.Dockerfile b/cni.Dockerfile index f3f4bfb6f..6738e6e84 100644 --- a/cni.Dockerfile +++ b/cni.Dockerfile @@ -5,7 +5,7 @@ ARG UPPER_CONSTRAINTS_FILE="https://git.openstack.org/cgit/openstack/requirement ARG OSLO_LOCK_PATH=/var/kuryr-lock RUN yum install -y epel-release https://rdoproject.org/repos/rdo-release.rpm \ - && yum install -y --setopt=tsflags=nodocs python-pip iproute bridge-utils openvswitch sudo \ + && yum install -y --setopt=tsflags=nodocs python-pip iproute bridge-utils openvswitch sudo libstdc++ \ && yum install -y --setopt=tsflags=nodocs gcc python-devel git COPY . /opt/kuryr-kubernetes diff --git a/cni_py3.Dockerfile b/cni_py3.Dockerfile index 994de11fa..7103fe9dd 100644 --- a/cni_py3.Dockerfile +++ b/cni_py3.Dockerfile @@ -5,7 +5,7 @@ ARG UPPER_CONSTRAINTS_FILE="https://git.openstack.org/cgit/openstack/requirement ARG OSLO_LOCK_PATH=/var/kuryr-lock RUN dnf update -y \ - && dnf install -y --setopt=tsflags=nodocs python36 iproute bridge-utils openvswitch sudo \ + && dnf install -y --setopt=tsflags=nodocs python36 iproute bridge-utils openvswitch sudo libstdc++ \ && dnf install -y --setopt=tsflags=nodocs gcc git COPY . /opt/kuryr-kubernetes diff --git a/controller.Dockerfile b/controller.Dockerfile index 04a5803e2..f115d0a0a 100644 --- a/controller.Dockerfile +++ b/controller.Dockerfile @@ -4,7 +4,7 @@ LABEL authors="Antoni Segura Puimedon, Michał Dulko"), diff --git a/kuryr_kubernetes/pod_resources/__init__.py b/kuryr_kubernetes/pod_resources/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/kuryr_kubernetes/pod_resources/api.proto b/kuryr_kubernetes/pod_resources/api.proto new file mode 100644 index 000000000..a54311b31 --- /dev/null +++ b/kuryr_kubernetes/pod_resources/api.proto @@ -0,0 +1,41 @@ +// Generated from kubernetes/pkg/kubelet/apis/podresources/v1alpha1/api.proto +// by removing the 'gogoproto' dependencies. +// To regenerate api_pb2.py and api_pb2_grpc.py use: +// python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. kuryr_kubernetes/pod_resources/api.proto + +syntax = 'proto3'; + +package v1alpha1; + +// PodResourcesLister is a service provided by the kubelet that provides information about the +// node resources consumed by pods and containers on the node +service PodResourcesLister { + rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {} +} + +// ListPodResourcesRequest is the request made to the PodResourcesLister service +message ListPodResourcesRequest {} + +// ListPodResourcesResponse is the response returned by List function +message ListPodResourcesResponse { + repeated PodResources pod_resources = 1; +} + +// PodResources contains information about the node resources assigned to a pod +message PodResources { + string name = 1; + string namespace = 2; + repeated ContainerResources containers = 3; +} + +// ContainerResources contains information about the resources assigned to a container +message ContainerResources { + string name = 1; + repeated ContainerDevices devices = 2; +} + +// ContainerDevices contains information about the devices assigned to a container +message ContainerDevices { + string resource_name = 1; + repeated string device_ids = 2; +} diff --git a/kuryr_kubernetes/pod_resources/api_pb2.py b/kuryr_kubernetes/pod_resources/api_pb2.py new file mode 100644 index 000000000..a9adb8d87 --- /dev/null +++ b/kuryr_kubernetes/pod_resources/api_pb2.py @@ -0,0 +1,273 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: kuryr_kubernetes/pod_resources/api.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='kuryr_kubernetes/pod_resources/api.proto', + package='v1alpha1', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n(kuryr_kubernetes/pod_resources/api.proto\x12\x08v1alpha1\"\x19\n\x17ListPodResourcesRequest\"I\n\x18ListPodResourcesResponse\x12-\n\rpod_resources\x18\x01 \x03(\x0b\x32\x16.v1alpha1.PodResources\"a\n\x0cPodResources\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x30\n\ncontainers\x18\x03 \x03(\x0b\x32\x1c.v1alpha1.ContainerResources\"O\n\x12\x43ontainerResources\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x07\x64\x65vices\x18\x02 \x03(\x0b\x32\x1a.v1alpha1.ContainerDevices\"=\n\x10\x43ontainerDevices\x12\x15\n\rresource_name\x18\x01 \x01(\t\x12\x12\n\ndevice_ids\x18\x02 \x03(\t2e\n\x12PodResourcesLister\x12O\n\x04List\x12!.v1alpha1.ListPodResourcesRequest\x1a\".v1alpha1.ListPodResourcesResponse\"\x00\x62\x06proto3') +) + + + + +_LISTPODRESOURCESREQUEST = _descriptor.Descriptor( + name='ListPodResourcesRequest', + full_name='v1alpha1.ListPodResourcesRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=54, + serialized_end=79, +) + + +_LISTPODRESOURCESRESPONSE = _descriptor.Descriptor( + name='ListPodResourcesResponse', + full_name='v1alpha1.ListPodResourcesResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='pod_resources', full_name='v1alpha1.ListPodResourcesResponse.pod_resources', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=81, + serialized_end=154, +) + + +_PODRESOURCES = _descriptor.Descriptor( + name='PodResources', + full_name='v1alpha1.PodResources', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='v1alpha1.PodResources.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='namespace', full_name='v1alpha1.PodResources.namespace', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='containers', full_name='v1alpha1.PodResources.containers', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=156, + serialized_end=253, +) + + +_CONTAINERRESOURCES = _descriptor.Descriptor( + name='ContainerResources', + full_name='v1alpha1.ContainerResources', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='v1alpha1.ContainerResources.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='devices', full_name='v1alpha1.ContainerResources.devices', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=255, + serialized_end=334, +) + + +_CONTAINERDEVICES = _descriptor.Descriptor( + name='ContainerDevices', + full_name='v1alpha1.ContainerDevices', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='resource_name', full_name='v1alpha1.ContainerDevices.resource_name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='device_ids', full_name='v1alpha1.ContainerDevices.device_ids', index=1, + number=2, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=336, + serialized_end=397, +) + +_LISTPODRESOURCESRESPONSE.fields_by_name['pod_resources'].message_type = _PODRESOURCES +_PODRESOURCES.fields_by_name['containers'].message_type = _CONTAINERRESOURCES +_CONTAINERRESOURCES.fields_by_name['devices'].message_type = _CONTAINERDEVICES +DESCRIPTOR.message_types_by_name['ListPodResourcesRequest'] = _LISTPODRESOURCESREQUEST +DESCRIPTOR.message_types_by_name['ListPodResourcesResponse'] = _LISTPODRESOURCESRESPONSE +DESCRIPTOR.message_types_by_name['PodResources'] = _PODRESOURCES +DESCRIPTOR.message_types_by_name['ContainerResources'] = _CONTAINERRESOURCES +DESCRIPTOR.message_types_by_name['ContainerDevices'] = _CONTAINERDEVICES +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ListPodResourcesRequest = _reflection.GeneratedProtocolMessageType('ListPodResourcesRequest', (_message.Message,), dict( + DESCRIPTOR = _LISTPODRESOURCESREQUEST, + __module__ = 'kuryr_kubernetes.pod_resources.api_pb2' + # @@protoc_insertion_point(class_scope:v1alpha1.ListPodResourcesRequest) + )) +_sym_db.RegisterMessage(ListPodResourcesRequest) + +ListPodResourcesResponse = _reflection.GeneratedProtocolMessageType('ListPodResourcesResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTPODRESOURCESRESPONSE, + __module__ = 'kuryr_kubernetes.pod_resources.api_pb2' + # @@protoc_insertion_point(class_scope:v1alpha1.ListPodResourcesResponse) + )) +_sym_db.RegisterMessage(ListPodResourcesResponse) + +PodResources = _reflection.GeneratedProtocolMessageType('PodResources', (_message.Message,), dict( + DESCRIPTOR = _PODRESOURCES, + __module__ = 'kuryr_kubernetes.pod_resources.api_pb2' + # @@protoc_insertion_point(class_scope:v1alpha1.PodResources) + )) +_sym_db.RegisterMessage(PodResources) + +ContainerResources = _reflection.GeneratedProtocolMessageType('ContainerResources', (_message.Message,), dict( + DESCRIPTOR = _CONTAINERRESOURCES, + __module__ = 'kuryr_kubernetes.pod_resources.api_pb2' + # @@protoc_insertion_point(class_scope:v1alpha1.ContainerResources) + )) +_sym_db.RegisterMessage(ContainerResources) + +ContainerDevices = _reflection.GeneratedProtocolMessageType('ContainerDevices', (_message.Message,), dict( + DESCRIPTOR = _CONTAINERDEVICES, + __module__ = 'kuryr_kubernetes.pod_resources.api_pb2' + # @@protoc_insertion_point(class_scope:v1alpha1.ContainerDevices) + )) +_sym_db.RegisterMessage(ContainerDevices) + + + +_PODRESOURCESLISTER = _descriptor.ServiceDescriptor( + name='PodResourcesLister', + full_name='v1alpha1.PodResourcesLister', + file=DESCRIPTOR, + index=0, + serialized_options=None, + serialized_start=399, + serialized_end=500, + methods=[ + _descriptor.MethodDescriptor( + name='List', + full_name='v1alpha1.PodResourcesLister.List', + index=0, + containing_service=None, + input_type=_LISTPODRESOURCESREQUEST, + output_type=_LISTPODRESOURCESRESPONSE, + serialized_options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_PODRESOURCESLISTER) + +DESCRIPTOR.services_by_name['PodResourcesLister'] = _PODRESOURCESLISTER + +# @@protoc_insertion_point(module_scope) diff --git a/kuryr_kubernetes/pod_resources/api_pb2_grpc.py b/kuryr_kubernetes/pod_resources/api_pb2_grpc.py new file mode 100644 index 000000000..845e1d76b --- /dev/null +++ b/kuryr_kubernetes/pod_resources/api_pb2_grpc.py @@ -0,0 +1,48 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +from kuryr_kubernetes.pod_resources import api_pb2 as kuryr__kubernetes_dot_pod__resources_dot_api__pb2 + + +class PodResourcesListerStub(object): + """PodResourcesLister is a service provided by the kubelet that provides information about the + node resources consumed by pods and containers on the node + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.List = channel.unary_unary( + '/v1alpha1.PodResourcesLister/List', + request_serializer=kuryr__kubernetes_dot_pod__resources_dot_api__pb2.ListPodResourcesRequest.SerializeToString, + response_deserializer=kuryr__kubernetes_dot_pod__resources_dot_api__pb2.ListPodResourcesResponse.FromString, + ) + + +class PodResourcesListerServicer(object): + """PodResourcesLister is a service provided by the kubelet that provides information about the + node resources consumed by pods and containers on the node + """ + + def List(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_PodResourcesListerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'List': grpc.unary_unary_rpc_method_handler( + servicer.List, + request_deserializer=kuryr__kubernetes_dot_pod__resources_dot_api__pb2.ListPodResourcesRequest.FromString, + response_serializer=kuryr__kubernetes_dot_pod__resources_dot_api__pb2.ListPodResourcesResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'v1alpha1.PodResourcesLister', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/kuryr_kubernetes/pod_resources/client.py b/kuryr_kubernetes/pod_resources/client.py new file mode 100644 index 000000000..03f00f113 --- /dev/null +++ b/kuryr_kubernetes/pod_resources/client.py @@ -0,0 +1,43 @@ +# Copyright (c) 2019 Samsung Electronics Co.,Ltd +# All Rights Reserved. +# +# 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. + +from oslo_log import log + +import grpc + +from kuryr_kubernetes.pod_resources import api_pb2 +from kuryr_kubernetes.pod_resources import api_pb2_grpc + +LOG = log.getLogger(__name__) + +POD_RESOURCES_SOCKET = '/pod-resources/kubelet.sock' + + +class PodResourcesClient(object): + + def __init__(self, kubelet_root_dir): + socket = 'unix:' + kubelet_root_dir + POD_RESOURCES_SOCKET + LOG.debug("Creating PodResourcesClient on socket: %s", socket) + self._channel = grpc.insecure_channel(socket) + self._stub = api_pb2_grpc.PodResourcesListerStub(self._channel) + + def list(self): + try: + response = self._stub.List(api_pb2.ListPodResourcesRequest()) + LOG.debug("PodResourceResponse: %s", response) + return response + except grpc.RpcError as e: + LOG.error("ListPodResourcesRequest failed: %s", e) + raise diff --git a/lower-constraints.txt b/lower-constraints.txt index 83b35ba04..e50cadcf2 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -30,6 +30,7 @@ Flask==0.12.3 future==0.16.0 futurist==1.6.0 greenlet==0.4.13 +grpcio==1.12.0 hacking==0.12.0 idna==2.6 imagesize==1.0.0 @@ -86,6 +87,7 @@ pep8==1.5.7 pika==0.10.0 pika-pool==0.1.3 prettytable==0.7.2 +protobuf==3.5.2 psutil==5.4.3 pycparser==2.18 pyflakes==0.8.1 diff --git a/requirements.txt b/requirements.txt index 8107f4544..5f7679168 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,3 +23,5 @@ pyroute2>=0.5.1;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) retrying!=1.3.0,>=1.2.3 # Apache-2.0 six>=1.10.0 # MIT stevedore>=1.20.0 # Apache-2.0 +grpcio>=1.12.0 # Apache-2.0 +protobuf>=3.5.2 # 3-Clause BSD diff --git a/tox.ini b/tox.ini index 899d6c5c7..0e7258652 100644 --- a/tox.ini +++ b/tox.ini @@ -59,7 +59,7 @@ show-source = true enable-extensions = H106,H203 # TODO(dougw) neutron/tests/unit/vmware exclusion is a temporary services split hack -exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,.ropeproject,rally-scenarios,neutron/tests/unit/vmware*,releasenotes +exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,.ropeproject,rally-scenarios,neutron/tests/unit/vmware*,releasenotes,kuryr_kubernetes/pod_resources/api_pb2* [testenv:pylint] basepython = python3