SEBA-495 eliminate chameleon dependency
Change-Id: Ia359d751c3ac84bf8f7038f611d1c5f1a126d1df
diff --git a/lib/xos-api/Makefile b/lib/xos-api/Makefile
deleted file mode 100644
index dcf7eac..0000000
--- a/lib/xos-api/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-all: chameleon_protos xosapi_install
-
-xosapi/chameleon:
- cd xosapi ;\
- ln -s ../../../../../component/chameleon chameleon
-
-chameleon_protos: | xosapi/chameleon
- cd xosapi/chameleon/protos; VOLTHA_BASE=anything make
-
-xosapi_install:
- python ./setup.py install
diff --git a/lib/xos-api/README.md b/lib/xos-api/README.md
index 9757833..0ac428a 100644
--- a/lib/xos-api/README.md
+++ b/lib/xos-api/README.md
@@ -1,23 +1,24 @@
-# xos_client #
+# xos_api #
-Xos\_client is a python library that is used by synchronizers and other components that need to communicate with the xos core. It includes a self-learning gRPC client (based on Chameleon) as well as a client-side ORM layer.
+Xos\_api is a python library that is used by synchronizers and other components that need to communicate with the xos core. It includes a self-learning gRPC client (based on Chameleon) as well as a client-side ORM layer.
-Xos\_client includes a python setup.py program that may be used to install the library. As part of the standard CORD build, the container image xosproject/xos-client is created with xos_client already installed, so that it may easily be used as a base image for other components.
+Xos\_api includes a python setup.py program that may be used to install the library. As part of the standard CORD build, the container image xosproject/xos-client is created with xos_api already installed, so that it may easily be used as a base image for other components.
## xossh ##
Xossh is a shell for interactively using the xos client API and client ORM layer. Generally xossh is run inside a container, and the xos\_client container is configured to run xossh as its default entrypoint. A script is provided in xos/tools to assist in invoking xossh from inside the head node environment.
-## Running Unit Tests ##
+## Rebuilding Chameleon's protobuf API ##
-Some unit tests (orm\_test.py) optionally support an environment where the xos\_client library is installed, and a core API container is available to serve the API. This allows testing against the actual grpc client, instead of the mock-up. It's suggested that the xos-client container be used together with a frontend or CiaB installation.
+The Chameleon API is expected not to change, and as such to facilitate build and deployment of xos-api, the Chameleon protobufs are distributed pre-built. If it is necessary to rebuild them, the following instructions may be helpful.
- docker run --rm -it --entrypoint bash docker-registry:5000/xosproject/xos-client:candidate
+First, install the necessary prerequisite environment. This can be done by entering the xos virtualenv (see `scripts/setup_env.sh` from the root of this repository) or by installing the following pip packages: `protobuf==3.5.2`, `grpcio==1.12.0`, and `grpcio-tools==1.12.0`.
-Once inside of the container, run the test(s). For example,
+Then compile the protobuf files:
- python /usr/local/lib/python2.7/dist-packages/xosapi/orm_test.py -R
+```bash
+cd chameleon_client/protos
+make
+```
-The test may be run using a mock-up of the grpc client by omitting the -R option:
-
- python orm-test.py
+Finally, for each file you may wish to prepend an appropriate copyright header.
\ No newline at end of file
diff --git a/lib/xos-api/setup.py b/lib/xos-api/setup.py
index d10b5c6..32c566f 100644
--- a/lib/xos-api/setup.py
+++ b/lib/xos-api/setup.py
@@ -26,24 +26,16 @@
from xosapi.version import __version__
-CHAMELEON_DIR = "xosapi/chameleon"
-
-if not os.path.exists(CHAMELEON_DIR):
- raise Exception("%s does not exist!" % CHAMELEON_DIR)
-
-if not os.path.exists(os.path.join(CHAMELEON_DIR, "protos/schema_pb2.py")):
- raise Exception("Please make the chameleon protos")
-
-# Chameleon requires these files have executable permission set.
-
class InstallFixChameleonPermissions(install):
+ # Chameleon requires these files have executable permission set,
+ # but setup.py installs them without the execute bit.
def run(self):
install.run(self)
for filepath in self.get_outputs():
if filepath.endswith(
- "chameleon/protoc_plugins/gw_gen.py"
- ) or filepath.endswith("chameleon/protoc_plugins/swagger_gen.py"):
+ "chameleon_client/protoc_plugins/gw_gen.py"
+ ):
os.chmod(filepath, 0o777)
@@ -52,20 +44,12 @@
version=__version__,
cmdclass={"install": InstallFixChameleonPermissions},
description="XOS api client",
- package_dir={"xosapi.chameleon": CHAMELEON_DIR},
packages=[
- "xosapi.chameleon.grpc_client",
- "xosapi.chameleon.protos",
- "xosapi.chameleon.utils",
- "xosapi.chameleon.protoc_plugins",
+ "xosapi.chameleon_client",
+ "xosapi.chameleon_client.protos",
+ "xosapi.chameleon_client.protoc_plugins",
"xosapi",
"xosapi.convenience",
],
- py_modules=["xosapi.chameleon.__init__"],
- include_package_data=True,
- package_data={
- "xosapi.chameleon.protos": ["*.proto"],
- "xosapi.chameleon.protoc_plugins": ["*.desc"],
- },
scripts=["xossh"],
)
diff --git a/lib/xos-api/xosapi/chameleon_client/__init__.py b/lib/xos-api/xosapi/chameleon_client/__init__.py
new file mode 100644
index 0000000..1a13cdf
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/__init__.py
@@ -0,0 +1,15 @@
+
+# Copyright 2017 the original author or 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.
+#
diff --git a/lib/xos-api/xosapi/chameleon_client/asleep.py b/lib/xos-api/xosapi/chameleon_client/asleep.py
new file mode 100644
index 0000000..295f675
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/asleep.py
@@ -0,0 +1,28 @@
+#
+# Copyright 2017 the original author or 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.
+#
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred
+
+
+def asleep(dt):
+ """
+ Async (event driven) wait for given time period (in seconds)
+ :param dt: Delay in seconds
+ :return: Deferred to be fired with value None when time expires.
+ """
+ d = Deferred()
+ reactor.callLater(dt, lambda: d.callback(None))
+ return d
\ No newline at end of file
diff --git a/lib/xos-api/xosapi/chameleon_client/grpc_client.py b/lib/xos-api/xosapi/chameleon_client/grpc_client.py
new file mode 100644
index 0000000..a8ad69d
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/grpc_client.py
@@ -0,0 +1,327 @@
+#
+# Copyright 2017 the original author or 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 client meant to connect to a gRPC server endpoint, and query the
+end-point's schema by calling SchemaService.Schema(Empty) and all of its
+semantics are derived from the recovered schema.
+"""
+
+import os
+import sys
+import time
+from random import randint
+from zlib import decompress
+
+import functools
+import grpc
+from consul import Consul
+from grpc._channel import _Rendezvous
+from structlog import get_logger
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue
+from werkzeug.exceptions import ServiceUnavailable
+
+from protos.schema_pb2_grpc import SchemaServiceStub
+from google.protobuf.empty_pb2 import Empty
+
+from asleep import asleep
+
+log = get_logger()
+
+
+class GrpcClient(object):
+ """
+ Connect to a gRPC server, fetch its schema, and process the downloaded
+ schema files to drive the customization of the north-bound interface(s)
+ of Chameleon.
+ """
+ RETRY_BACKOFF = [0.05, 0.1, 0.2, 0.5, 1, 2, 5]
+
+ def __init__(self, consul_endpoint, work_dir, endpoint='localhost:50055',
+ reconnect_callback=None, credentials=None, restart_on_disconnect=False):
+ self.consul_endpoint = consul_endpoint
+ self.endpoint = endpoint
+ self.work_dir = work_dir
+ self.reconnect_callback = reconnect_callback
+ self.credentials = credentials
+ self.restart_on_disconnect = restart_on_disconnect
+
+ self.plugin_dir = os.path.abspath(os.path.join(
+ os.path.dirname(__file__), 'protoc_plugins'))
+
+ self.channel = None
+ self.schema = None
+ self.retries = 0
+ self.shutting_down = False
+ self.connected = False
+ self.was_connected = False
+
+ def start(self):
+ log.debug('starting')
+ if not self.connected:
+ reactor.callLater(0, self.connect)
+ log.info('started')
+ return self
+
+ def stop(self):
+ log.debug('stopping')
+ if self.shutting_down:
+ return
+ self.shutting_down = True
+ log.info('stopped')
+
+ def set_reconnect_callback(self, reconnect_callback):
+ self.reconnect_callback = reconnect_callback
+ return self
+
+ def connectivity_callback(self, client, connectivity):
+ if (self.was_connected) and (connectivity in [connectivity.TRANSIENT_FAILURE, connectivity.SHUTDOWN]):
+ log.info("connectivity lost -- restarting")
+ os.execv(sys.executable, ['python'] + sys.argv)
+
+ if (connectivity == connectivity.READY):
+ self.was_connected = True
+
+ # Sometimes gRPC transitions from READY to IDLE, skipping TRANSIENT_FAILURE even though a socket is
+ # disconnected. So on idle, force a connectivity check.
+ if (connectivity == connectivity.IDLE) and (self.was_connected):
+ connectivity = client.channel._channel.check_connectivity_state(True)
+ # The result will probably show IDLE, but passing in True has the side effect of reconnecting if the
+ # connection has been lost, which will trigger the TRANSIENT_FALURE we were looking for.
+
+ @inlineCallbacks
+ def connect(self):
+ """
+ (Re-)Connect to end-point
+ """
+
+ if self.shutting_down or self.connected:
+ return
+
+ try:
+ if self.endpoint.startswith('@'):
+ _endpoint = yield self._get_endpoint_from_consul(
+ self.endpoint[1:])
+ else:
+ _endpoint = self.endpoint
+
+ if self.credentials:
+ log.info('securely connecting', endpoint=_endpoint)
+ self.channel = grpc.secure_channel(_endpoint, self.credentials)
+ else:
+ log.info('insecurely connecting', endpoint=_endpoint)
+ self.channel = grpc.insecure_channel(_endpoint)
+
+ if self.restart_on_disconnect:
+ connectivity_callback = functools.partial(self.connectivity_callback, self)
+ self.channel.subscribe(connectivity_callback)
+
+ # Delay between initiating connection and executing first gRPC. See CORD-3012.
+ time.sleep(0.5)
+
+ swagger_from = self._retrieve_schema()
+ self._compile_proto_files(swagger_from)
+ self._clear_backoff()
+
+ self.connected = True
+ if self.reconnect_callback is not None:
+ reactor.callLater(0, self.reconnect_callback)
+
+ return
+
+ except _Rendezvous, e:
+ if e.code() == grpc.StatusCode.UNAVAILABLE:
+ log.info('grpc-endpoint-not-available')
+ else:
+ log.exception('rendezvous error', e=e)
+ yield self._backoff('not-available')
+
+ except Exception, e:
+ if not self.shutting_down:
+ log.exception('cannot-connect', endpoint=_endpoint)
+ yield self._backoff('unknown-error')
+
+ reactor.callLater(0, self.connect)
+
+ def _backoff(self, msg):
+ wait_time = self.RETRY_BACKOFF[min(self.retries,
+ len(self.RETRY_BACKOFF) - 1)]
+ self.retries += 1
+ log.error(msg, retry_in=wait_time)
+ return asleep(wait_time)
+
+ def _clear_backoff(self):
+ if self.retries:
+ log.info('reconnected', after_retries=self.retries)
+ self.retries = 0
+
+ @inlineCallbacks
+ def _get_endpoint_from_consul(self, service_name):
+ """
+ Look up an appropriate grpc endpoint (host, port) from
+ consul, under the service name specified by service-name
+ """
+ host = self.consul_endpoint.split(':')[0].strip()
+ port = int(self.consul_endpoint.split(':')[1].strip())
+
+ while True:
+ log.debug('consul-lookup', host=host, port=port)
+ consul = Consul(host=host, port=port)
+ _, services = consul.catalog.service(service_name)
+ log.debug('consul-response', services=services)
+ if services:
+ break
+ log.warning('no-service', consul_host=host, consul_port=port,
+ service_name=service_name)
+ yield asleep(1.0)
+
+ # pick local addresses when resolving a service via consul
+ # see CORD-815 (https://jira.opencord.org/browse/CORD-815)
+
+ service = services[randint(0, len(services) - 1)]
+ endpoint = '{}:{}'.format(service['ServiceAddress'],
+ service['ServicePort'])
+ returnValue(endpoint)
+
+ def _retrieve_schema(self):
+ """
+ Retrieve schema from gRPC end-point, and save all *.proto files in
+ the work directory.
+ """
+ assert isinstance(self.channel, grpc.Channel)
+ stub = SchemaServiceStub(self.channel)
+ # try:
+ schemas = stub.GetSchema(Empty(), timeout=120)
+ # except _Rendezvous, e:
+ # if e.code == grpc.StatusCode.UNAVAILABLE:
+ #
+ # else:
+ # raise e
+
+ os.system('mkdir -p %s' % self.work_dir)
+ os.system('rm -fr /tmp/%s/*' %
+ self.work_dir.replace('/tmp/', '')) # safer
+
+ for proto_file in schemas.protos:
+ proto_fname = proto_file.file_name
+ proto_content = proto_file.proto
+ log.debug('saving-proto', fname=proto_fname, dir=self.work_dir,
+ length=len(proto_content))
+ with open(os.path.join(self.work_dir, proto_fname), 'w') as f:
+ f.write(proto_content)
+
+ desc_content = decompress(proto_file.descriptor)
+ desc_fname = proto_fname.replace('.proto', '.desc')
+ log.debug('saving-descriptor', fname=desc_fname, dir=self.work_dir,
+ length=len(desc_content))
+ with open(os.path.join(self.work_dir, desc_fname), 'wb') as f:
+ f.write(desc_content)
+ return schemas.swagger_from
+
+ def _compile_proto_files(self, swagger_from):
+ """
+ For each *.proto file in the work directory, compile the proto
+ file into the respective *_pb2.py file as well as generate the
+ web server gateway python file *_gw.py.
+ :return: None
+ """
+
+ chameleon_base_dir = os.path.abspath(os.path.join(
+ os.path.dirname(__file__), '.'
+ ))
+
+ for fname in [f for f in os.listdir(self.work_dir)
+ if f.endswith('.proto')]:
+
+ need_swagger = fname == swagger_from
+ log.debug('compiling', file=fname, need_swagger=need_swagger)
+ cmd = (
+ 'cd %s && '
+ 'env PATH=%s PYTHONPATH=%s '
+ 'python -m grpc.tools.protoc '
+ '-I. '
+ '--python_out=. '
+ '--grpc_python_out=. '
+ '--plugin=protoc-gen-gw=%s/gw_gen.py '
+ '--gw_out=. '
+ '%s' % (
+ self.work_dir,
+ ':'.join([os.environ['PATH'], self.plugin_dir]),
+ chameleon_base_dir,
+ self.plugin_dir,
+ fname)
+ )
+ log.debug('executing', cmd=cmd, file=fname)
+ os.system(cmd)
+ log.info('compiled', file=fname)
+
+ # test-load each _pb2 file to see all is right
+ if self.work_dir not in sys.path:
+ sys.path.insert(0, self.work_dir)
+
+ for fname in [f for f in os.listdir(self.work_dir)
+ if f.endswith('_pb2.py')]:
+ modname = fname[:-len('.py')]
+ log.debug('test-import', modname=modname)
+ _ = __import__(modname)
+
+ @inlineCallbacks
+ def invoke(self, stub, method_name, request, metadata, retry=1):
+ """
+ Invoke a gRPC call to the remote server and return the response.
+ :param stub: Reference to the *_pb2 service stub
+ :param method_name: The method name inside the service stub
+ :param request: The request protobuf message
+ :param metadata: [(str, str), (str, str), ...]
+ :return: The response protobuf message and returned trailing metadata
+ """
+
+ if not self.connected:
+ raise ServiceUnavailable()
+
+ try:
+ method = getattr(stub(self.channel), method_name)
+ response, rendezvous = method.with_call(request, metadata=metadata)
+ returnValue((response, rendezvous.trailing_metadata()))
+
+ except grpc._channel._Rendezvous, e:
+ code = e.code()
+ if code == grpc.StatusCode.UNAVAILABLE:
+ e = ServiceUnavailable()
+
+ if self.connected:
+ self.connected = False
+ yield self.connect()
+ if retry > 0:
+ response = yield self.invoke(stub, method_name,
+ request, metadata,
+ retry=retry - 1)
+ returnValue(response)
+
+ elif code in (
+ grpc.StatusCode.NOT_FOUND,
+ grpc.StatusCode.INVALID_ARGUMENT,
+ grpc.StatusCode.ALREADY_EXISTS,
+ grpc.StatusCode.UNAUTHENTICATED,
+ grpc.StatusCode.PERMISSION_DENIED):
+
+ pass # don't log error, these occur naturally
+
+ else:
+ log.exception(e)
+
+ raise e
diff --git a/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py b/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py
new file mode 100755
index 0000000..9cd2f6f
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py
@@ -0,0 +1,305 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+import sys
+
+from google.protobuf.compiler import plugin_pb2 as plugin
+from google.protobuf.descriptor_pb2 import ServiceDescriptorProto, \
+ MethodOptions
+from jinja2 import Template
+from simplejson import dumps
+
+from xosapi.chameleon_client.protos import annotations_pb2, http_pb2
+
+template = Template("""
+# Generated file; please do not edit
+
+from simplejson import dumps, load
+from structlog import get_logger
+from google.protobuf.json_format import MessageToDict, ParseDict
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+{% set package = file_name.replace('.proto', '') %}
+
+{% for pypackage, module in includes %}
+{% if pypackage %}
+from {{ pypackage }} import {{ module }}
+try:
+ from {{ pypackage }} import {{ module }}_grpc
+except ImportError:
+ pass
+{% else %}
+import {{ module }}
+try:
+ import {{ module }}_grpc
+except ImportError:
+ pass
+{% endif %}
+{% endfor %}
+
+log = get_logger()
+
+def add_routes(app, grpc_client):
+
+ pass # so that if no endpoints are defined, Python is still happy
+
+ {% for method in methods %}
+ {% set method_name = method['service'].rpartition('.')[2] + '_' + method['method'] %}
+ {% set path = method['path'].replace('{', '<string:').replace('}', '>') %}
+ @app.route('{{ path }}', methods=['{{ method['verb'].upper() }}'])
+ @inlineCallbacks
+ def {{ method_name }}(server, request, **kw):
+ log.debug('{{ method_name }}', request=request, server=server, **kw)
+ {% if method['body'] == '*' %}
+ data = load(request.content)
+ data.update(kw)
+ {% elif method['body'] == '' %}
+ data = kw
+ {% else %}
+ raise NotImplementedError('cannot handle specific body field list')
+ {% endif %}
+ try:
+ req = ParseDict(data, {{ type_map[method['input_type']] }}())
+ except Exception, e:
+ log.error('cannot-convert-to-protobuf', e=e, data=data)
+ raise
+ res, metadata = yield grpc_client.invoke(
+ {{ stub_map[method['service']] }}Stub,
+ '{{ method['method'] }}', req, request.getAllHeaders().items())
+ try:
+ out_data = MessageToDict(res, True, True)
+ except AttributeError, e:
+ filename = '/tmp/chameleon_failed_to_convert_data.pbd'
+ with file(filename, 'w') as f:
+ f.write(res.SerializeToString())
+ log.error('cannot-convert-from-protobuf', outdata_saved=filename)
+ raise
+ for key, value in metadata:
+ request.setHeader(key, value)
+ request.setHeader('Content-Type', 'application/json')
+ log.debug('{{ method_name }}', **out_data)
+ returnValue(dumps(out_data))
+
+ {% endfor %}
+
+""", trim_blocks=True, lstrip_blocks=True)
+
+
+def traverse_methods(proto_file):
+
+ package = proto_file.name
+ for service in proto_file.service:
+ assert isinstance(service, ServiceDescriptorProto)
+
+ for method in service.method:
+ options = method.options
+ assert isinstance(options, MethodOptions)
+ for fd, http in options.ListFields():
+ if fd.full_name == 'googleapi.http':
+ assert fd.name == 'http'
+ assert isinstance(http, http_pb2.HttpRule)
+
+ input_type = method.input_type
+ if input_type.startswith('.'):
+ input_type = input_type[1:]
+
+ output_type = method.output_type
+ if output_type.startswith('.'):
+ output_type = output_type[1:]
+
+ if http.delete:
+ verb = 'delete'
+ path = http.delete
+ elif http.get:
+ verb = 'get'
+ path = http.get
+ elif http.patch:
+ verb = 'patch'
+ path = http.patch
+ elif http.post:
+ verb = 'post'
+ path = http.post
+ elif http.put:
+ verb = 'put'
+ path = http.put
+ else:
+ raise AttributeError('No valid verb in method %s' %
+ method.name)
+
+ body = http.body
+
+ data = {
+ 'package': package,
+ 'filename': proto_file.name,
+ 'service': proto_file.package + '.' + service.name,
+ 'method': method.name,
+ 'input_type': input_type,
+ 'output_type': output_type,
+ 'path': path,
+ 'verb': verb,
+ 'body': body
+ }
+
+ yield data
+
+
+def generate_gw_code(file_name, methods, type_map, stub_map, includes):
+ return template.render(file_name=file_name, methods=methods,
+ type_map=type_map, stub_map=stub_map, includes=includes)
+
+
+class IncludeManager(object):
+ # need to keep track of what files define what message types and
+ # under what package name. Later, when we analyze the methods, we
+ # need to be able to derive the list of files we need to load and we
+ # also need to replce the <proto-package-name>.<artifact-name> in the
+ # templates with <python-package-name>.<artifact-name> so Python can
+ # resolve these.
+ def __init__(self):
+ self.package_to_localname = {}
+ self.fullname_to_filename = {}
+ self.prefix_table = [] # sorted table of top-level symbols in protos
+ self.type_map = {} # full name as used in .proto -> python name
+ self.stub_map = {}
+ self.includes_needed = set() # names of files needed to be included
+ self.filename_to_module = {} # filename -> (package, module)
+
+ def extend_symbol_tables(self, proto_file):
+ # keep track of what file adds what top-level symbol to what abstract
+ # package name
+ package_name = proto_file.package
+ file_name = proto_file.name
+ self._add_filename(file_name)
+ all_defs = list(proto_file.message_type)
+ all_defs.extend(list(proto_file.enum_type))
+ all_defs.extend(list(proto_file.service))
+ for typedef in all_defs:
+ name = typedef.name
+ fullname = package_name + '.' + name
+ self.fullname_to_filename[fullname] = file_name
+ self.package_to_localname.setdefault(package_name, []).append(name)
+ self._update_prefix_table()
+
+ def _add_filename(self, filename):
+ if filename not in self.filename_to_module:
+ python_path = filename.replace('.proto', '_pb2').replace('/', '.')
+ package_name, _, module_name = python_path.rpartition('.')
+ self.filename_to_module[filename] = (package_name, module_name)
+
+ def _update_prefix_table(self):
+ # make a sorted list symbol prefixes needed to resolv for potential use
+ # of nested symbols
+ self.prefix_table = sorted(self.fullname_to_filename.iterkeys(),
+ reverse=True)
+
+ def _find_matching_prefix(self, fullname):
+ for prefix in self.prefix_table:
+ if fullname.startswith(prefix):
+ return prefix
+ # This should never happen
+ raise Exception('No match for type name "{}"'.format(fullname))
+
+ def add_needed_symbol(self, fullname):
+ if fullname in self.type_map:
+ return
+ top_level_symbol = self._find_matching_prefix(fullname)
+ name = top_level_symbol.rpartition('.')[2]
+ nested_name = fullname[len(top_level_symbol):] # may be empty
+ file_name = self.fullname_to_filename[top_level_symbol]
+ self.includes_needed.add(file_name)
+ module_name = self.filename_to_module[file_name][1]
+ python_name = module_name + '.' + name + nested_name
+ self.type_map[fullname] = python_name
+ python_name = module_name + '_grpc.' + name + nested_name
+ self.stub_map[fullname] = python_name
+
+ def get_type_map(self):
+ return self.type_map
+
+ def get_stub_map(self):
+ return self.stub_map
+
+ def get_includes(self):
+ return sorted(
+ self.filename_to_module[fn] for fn in self.includes_needed)
+
+
+def generate_code(request, response):
+
+ assert isinstance(request, plugin.CodeGeneratorRequest)
+
+ include_manager = IncludeManager()
+ for proto_file in request.proto_file:
+
+ include_manager.extend_symbol_tables(proto_file)
+
+ methods = []
+
+ for data in traverse_methods(proto_file):
+ methods.append(data)
+ include_manager.add_needed_symbol(data['input_type'])
+ include_manager.add_needed_symbol(data['output_type'])
+ include_manager.add_needed_symbol(data['service'])
+
+ type_map = include_manager.get_type_map()
+ stub_map = include_manager.get_stub_map()
+ includes = include_manager.get_includes()
+
+ # as a nice side-effect, generate a json file capturing the essence
+ # of the RPC method entries
+ f = response.file.add()
+ f.name = proto_file.name + '.json'
+ f.content = dumps(dict(
+ type_rename_map=type_map, # TODO: is stub_map needed here?
+ includes=includes,
+ methods=methods), indent=4)
+
+ # generate the real Python code file
+ f = response.file.add()
+ assert proto_file.name.endswith('.proto')
+ f.name = proto_file.name.replace('.proto', '_gw.py')
+ f.content = generate_gw_code(proto_file.name,
+ methods, type_map, stub_map, includes)
+
+
+if __name__ == '__main__':
+
+ if len(sys.argv) >= 2:
+ # read input from file, to allow troubleshooting
+ with open(sys.argv[1], 'r') as f:
+ data = f.read()
+ else:
+ # read input from stdin
+ data = sys.stdin.read()
+ # with file('/tmp/buf', 'wb') as f:
+ # f.write(data)
+
+ # parse request
+ request = plugin.CodeGeneratorRequest()
+ request.ParseFromString(data)
+
+ # create response object
+ response = plugin.CodeGeneratorResponse()
+
+ # generate the output and the response
+ generate_code(request, response)
+
+ # serialize the response
+ output = response.SerializeToString()
+
+ # write response to stdout
+ sys.stdout.write(output)
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/Makefile b/lib/xos-api/xosapi/chameleon_client/protos/Makefile
new file mode 100644
index 0000000..b272a1a
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/Makefile
@@ -0,0 +1,40 @@
+#
+# Copyright 2017 the original author or 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.
+#
+
+# Makefile to build all protobuf and gRPC related artifacts
+
+default: build
+
+PROTO_FILES := $(wildcard *.proto)
+PROTO_PB2_FILES := $(foreach f,$(PROTO_FILES),$(subst .proto,_pb2.py,$(f)))
+PROTO_DESC_FILES := $(foreach f,$(PROTO_FILES),$(subst .proto,.desc,$(f)))
+
+PROTOC_PREFIX := /usr/local
+PROTOC_LIBDIR := $(PROTOC_PREFIX)/lib
+
+build: $(PROTO_PB2_FILES)
+
+%_pb2.py: %.proto Makefile
+ @echo "Building protocol buffer artifacts from $<"
+ env LD_LIBRARY_PATH=$(PROTOC_LIBDIR) python -m grpc.tools.protoc \
+ -I. \
+ --python_out=. \
+ --grpc_python_out=. \
+ $<
+
+clean:
+ rm -f $(PROTO_PB2_FILES) $(PROTO_DESC_FILES)
+
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/__init__.py b/lib/xos-api/xosapi/chameleon_client/protos/__init__.py
new file mode 100644
index 0000000..1a13cdf
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/__init__.py
@@ -0,0 +1,15 @@
+
+# Copyright 2017 the original author or 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.
+#
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/annotations.proto b/lib/xos-api/xosapi/chameleon_client/protos/annotations.proto
new file mode 100644
index 0000000..2ed81a6
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/annotations.proto
@@ -0,0 +1,34 @@
+// Copyright (c) 2015, Google Inc.
+// Modififications (C) 2018, Open Networking Foundation
+//
+// 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.
+
+// See NOTE in http.proto for the modifications made to these files
+
+syntax = "proto3";
+
+package googleapi;
+
+import "http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py
new file mode 100644
index 0000000..5abc663
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: annotations.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
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+import http_pb2 as http__pb2
+from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='annotations.proto',
+ package='googleapi',
+ syntax='proto3',
+ serialized_pb=_b('\n\x11\x61nnotations.proto\x12\tgoogleapi\x1a\nhttp.proto\x1a google/protobuf/descriptor.proto:D\n\x04http\x12\x1e.google.protobuf.MethodOptions\x18\xb0\xca\xbc\" \x01(\x0b\x32\x13.googleapi.HttpRuleBn\n\x0e\x63om.google.apiB\x10\x41nnotationsProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3')
+ ,
+ dependencies=[http__pb2.DESCRIPTOR,google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,])
+
+
+HTTP_FIELD_NUMBER = 72295728
+http = _descriptor.FieldDescriptor(
+ name='http', full_name='googleapi.http', index=0,
+ number=72295728, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=True, extension_scope=None,
+ options=None, file=DESCRIPTOR)
+
+DESCRIPTOR.extensions_by_name['http'] = http
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+http.message_type = http__pb2._HTTPRULE
+google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(http)
+
+DESCRIPTOR.has_options = True
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\016com.google.apiB\020AnnotationsProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI'))
+# @@protoc_insertion_point(module_scope)
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py
new file mode 100644
index 0000000..972edf5
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/http.proto b/lib/xos-api/xosapi/chameleon_client/protos/http.proto
new file mode 100644
index 0000000..99d0ce3
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/http.proto
@@ -0,0 +1,353 @@
+// Copyright 2018 Google LLC
+// Modififications (C) 2018, Open Networking Foundation
+//
+// 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.
+
+// NOTE: On the provenance of and modifications to http.proto and
+// annotations.proto
+//
+// TL;DR: The files http.proto and annotations.proto are originally from here:
+// https://github.com/googleapis/googleapis
+// They have been modified slightly to avoid a namespace conflict.
+//
+// Long winded explanation:
+// These files are designed to interact with Google's first party API's, and
+// the recommended way to use them is to compiled them to code with protoc and
+// included in your codebase before being used. Due to the fact that we're not
+// using them that way, and because of how Chameleon and XOS work (dynamically
+// defining our own API's), we have to ship these *.proto files as a part of
+// our artifacts.
+//
+// The problems start when you try to include these specific .proto files in
+// python. The protoc compiler includes the `google.protobuf` classes, which
+// python can look up in the standard python library path. Unfortunately these
+// files are namespaced with `google.api` in the path and aren't shipped with
+// protoc. This leads to a path conflict - you can't have two library paths
+// start with the same path component (`google` in this case) without getting
+// an "ImportError: No module named ..." on one of the paths when you import
+// them.
+//
+// Historically, various confusing hacks were implemented to override and
+// special-case the python `include` directive to include a file at a different
+// path than was specified. These hacks also failed when updating the base OS,
+// and would likely continue to fail in other, stranger ways as we update the
+// codebase. Specifically, Python 3 reimplemented these features in the
+// importlib section of the standard library, so there's little confidence our
+// hacks would continue to work. As an aside, there are various protobuf
+// `options` statements to deal with this sort of issue in other languages (see
+// the `go_package` and `java_package` below ) but these don't currently exist
+// for python: https://github.com/google/protobuf/issues/973
+//
+// To avoid this entire psychotic namespace hellscape, it's much easier to
+// modify these files to remove the google.api path component, and have them
+// included directly at a path of our own choice.
+
+syntax = "proto3";
+
+package googleapi;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parmeters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// `HttpRule` defines the mapping of an RPC method to one or more HTTP
+// REST API methods. The mapping specifies how different portions of the RPC
+// request message are mapped to URL path, URL query parameters, and
+// HTTP request body. The mapping is typically specified as an
+// `google.api.http` annotation on the RPC method,
+// see "google/api/annotations.proto" for details.
+//
+// The mapping consists of a field specifying the path template and
+// method kind. The path template can refer to fields in the request
+// message, as in the example below which describes a REST GET
+// operation on a resource collection of messages:
+//
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}";
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // mapped to the URL
+// SubMessage sub = 2; // `sub.subfield` is url-mapped
+// }
+// message Message {
+// string text = 1; // content of the resource
+// }
+//
+// The same http annotation can alternatively be expressed inside the
+// `GRPC API Configuration` YAML file.
+//
+// http:
+// rules:
+// - selector: <proto_package_name>.Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// This definition enables an automatic, bidrectional mapping of HTTP
+// JSON to RPC. Example:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))`
+//
+// In general, not only fields but also field paths can be referenced
+// from a path pattern. Fields mapped to the path pattern cannot be
+// repeated and must have a primitive (non-message) type.
+//
+// Any fields in the request message which are not bound by the path
+// pattern automatically become (optional) HTTP query
+// parameters. Assume the following definition of the request message:
+//
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http).get = "/v1/messages/{message_id}";
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // mapped to the URL
+// int64 revision = 2; // becomes a parameter
+// SubMessage sub = 3; // `sub.subfield` becomes a parameter
+// }
+//
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))`
+//
+// Note that fields which are mapped to HTTP parameters must have a
+// primitive type or a repeated primitive type. Message types are not
+// allowed. In the case of a repeated type, the parameter can be
+// repeated in the URL, as in `...?param=A¶m=B`.
+//
+// For HTTP method kinds which allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// put: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | RPC
+// -----|-----
+// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// put: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | RPC
+// -----|-----
+// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice of
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+//
+// This enables the following two alternative HTTP JSON to RPC
+// mappings:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")`
+//
+// # Rules for HTTP mapping
+//
+// The rules for mapping HTTP path, query parameters, and body fields
+// to the request message are as follows:
+//
+// 1. The `body` field specifies either `*` or a field path, or is
+// omitted. If omitted, it indicates there is no HTTP request body.
+// 2. Leaf fields (recursive expansion of nested messages in the
+// request) can be classified into three types:
+// (a) Matched in the URL template.
+// (b) Covered by body (if body is `*`, everything except (a) fields;
+// else everything under the body field)
+// (c) All other fields.
+// 3. URL query parameters found in the HTTP request are mapped to (c) fields.
+// 4. Any body sent with an HTTP request can contain only (b) fields.
+//
+// The syntax of the path template is as follows:
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single path segment. The syntax `**` matches zero
+// or more path segments, which must be the last part of the path except the
+// `Verb`. The syntax `LITERAL` matches literal text in the path.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path, all characters
+// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the
+// Discovery Document as `{var}`.
+//
+// If a variable contains one or more path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path, all
+// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables
+// show up in the Discovery Document as `{+var}`.
+//
+// NOTE: While the single segment variable matches the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2
+// Simple String Expansion, the multi segment variable **does not** match
+// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs.
+//
+// NOTE: the field paths in variables and in the `body` must not refer to
+// repeated fields or map fields.
+message HttpRule {
+ // Selects methods to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Used for listing and getting information about resources.
+ string get = 2;
+
+ // Used for updating a resource.
+ string put = 3;
+
+ // Used for creating a resource.
+ string post = 4;
+
+ // Used for deleting a resource.
+ string delete = 5;
+
+ // Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP body, or
+ // `*` for mapping all fields not captured by the path pattern to the HTTP
+ // body. NOTE: the referred field must not be a repeated field and must be
+ // present at the top-level of request message type.
+ string body = 7;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py
new file mode 100644
index 0000000..f420e61
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: http.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
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='http.proto',
+ package='googleapi',
+ syntax='proto3',
+ serialized_pb=_b('\n\nhttp.proto\x12\tgoogleapi\"S\n\x04Http\x12\"\n\x05rules\x18\x01 \x03(\x0b\x32\x13.googleapi.HttpRule\x12\'\n\x1f\x66ully_decode_reserved_expansion\x18\x02 \x01(\x08\"\xe8\x01\n\x08HttpRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\r\n\x03get\x18\x02 \x01(\tH\x00\x12\r\n\x03put\x18\x03 \x01(\tH\x00\x12\x0e\n\x04post\x18\x04 \x01(\tH\x00\x12\x10\n\x06\x64\x65lete\x18\x05 \x01(\tH\x00\x12\x0f\n\x05patch\x18\x06 \x01(\tH\x00\x12.\n\x06\x63ustom\x18\x08 \x01(\x0b\x32\x1c.googleapi.CustomHttpPatternH\x00\x12\x0c\n\x04\x62ody\x18\x07 \x01(\t\x12\x30\n\x13\x61\x64\x64itional_bindings\x18\x0b \x03(\x0b\x32\x13.googleapi.HttpRuleB\t\n\x07pattern\"/\n\x11\x43ustomHttpPattern\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\tBj\n\x0e\x63om.google.apiB\tHttpProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xf8\x01\x01\xa2\x02\x04GAPIb\x06proto3')
+)
+
+
+
+
+_HTTP = _descriptor.Descriptor(
+ name='Http',
+ full_name='googleapi.Http',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='rules', full_name='googleapi.Http.rules', 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='fully_decode_reserved_expansion', full_name='googleapi.Http.fully_decode_reserved_expansion', index=1,
+ number=2, type=8, cpp_type=7, label=1,
+ has_default_value=False, default_value=False,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=25,
+ serialized_end=108,
+)
+
+
+_HTTPRULE = _descriptor.Descriptor(
+ name='HttpRule',
+ full_name='googleapi.HttpRule',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='selector', full_name='googleapi.HttpRule.selector', 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='get', full_name='googleapi.HttpRule.get', 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='put', full_name='googleapi.HttpRule.put', index=2,
+ number=3, 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='post', full_name='googleapi.HttpRule.post', index=3,
+ number=4, 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='delete', full_name='googleapi.HttpRule.delete', index=4,
+ number=5, 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='patch', full_name='googleapi.HttpRule.patch', index=5,
+ number=6, 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='custom', full_name='googleapi.HttpRule.custom', index=6,
+ number=8, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='body', full_name='googleapi.HttpRule.body', index=7,
+ number=7, 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='additional_bindings', full_name='googleapi.HttpRule.additional_bindings', index=8,
+ number=11, 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,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ _descriptor.OneofDescriptor(
+ name='pattern', full_name='googleapi.HttpRule.pattern',
+ index=0, containing_type=None, fields=[]),
+ ],
+ serialized_start=111,
+ serialized_end=343,
+)
+
+
+_CUSTOMHTTPPATTERN = _descriptor.Descriptor(
+ name='CustomHttpPattern',
+ full_name='googleapi.CustomHttpPattern',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='kind', full_name='googleapi.CustomHttpPattern.kind', 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='path', full_name='googleapi.CustomHttpPattern.path', 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,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=345,
+ serialized_end=392,
+)
+
+_HTTP.fields_by_name['rules'].message_type = _HTTPRULE
+_HTTPRULE.fields_by_name['custom'].message_type = _CUSTOMHTTPPATTERN
+_HTTPRULE.fields_by_name['additional_bindings'].message_type = _HTTPRULE
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+ _HTTPRULE.fields_by_name['get'])
+_HTTPRULE.fields_by_name['get'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+ _HTTPRULE.fields_by_name['put'])
+_HTTPRULE.fields_by_name['put'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+ _HTTPRULE.fields_by_name['post'])
+_HTTPRULE.fields_by_name['post'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+ _HTTPRULE.fields_by_name['delete'])
+_HTTPRULE.fields_by_name['delete'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+ _HTTPRULE.fields_by_name['patch'])
+_HTTPRULE.fields_by_name['patch'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+ _HTTPRULE.fields_by_name['custom'])
+_HTTPRULE.fields_by_name['custom'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+DESCRIPTOR.message_types_by_name['Http'] = _HTTP
+DESCRIPTOR.message_types_by_name['HttpRule'] = _HTTPRULE
+DESCRIPTOR.message_types_by_name['CustomHttpPattern'] = _CUSTOMHTTPPATTERN
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Http = _reflection.GeneratedProtocolMessageType('Http', (_message.Message,), dict(
+ DESCRIPTOR = _HTTP,
+ __module__ = 'http_pb2'
+ # @@protoc_insertion_point(class_scope:googleapi.Http)
+ ))
+_sym_db.RegisterMessage(Http)
+
+HttpRule = _reflection.GeneratedProtocolMessageType('HttpRule', (_message.Message,), dict(
+ DESCRIPTOR = _HTTPRULE,
+ __module__ = 'http_pb2'
+ # @@protoc_insertion_point(class_scope:googleapi.HttpRule)
+ ))
+_sym_db.RegisterMessage(HttpRule)
+
+CustomHttpPattern = _reflection.GeneratedProtocolMessageType('CustomHttpPattern', (_message.Message,), dict(
+ DESCRIPTOR = _CUSTOMHTTPPATTERN,
+ __module__ = 'http_pb2'
+ # @@protoc_insertion_point(class_scope:googleapi.CustomHttpPattern)
+ ))
+_sym_db.RegisterMessage(CustomHttpPattern)
+
+
+DESCRIPTOR.has_options = True
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\016com.google.apiB\tHttpProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\370\001\001\242\002\004GAPI'))
+# @@protoc_insertion_point(module_scope)
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py
new file mode 100644
index 0000000..972edf5
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/schema.proto b/lib/xos-api/xosapi/chameleon_client/protos/schema.proto
new file mode 100644
index 0000000..e10c5de
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/schema.proto
@@ -0,0 +1,45 @@
+// Copyright 2017 the original author or 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.
+
+syntax = "proto3";
+
+package schema;
+
+import "google/protobuf/empty.proto";
+
+// Contains the name and content of a *.proto file
+message ProtoFile {
+ string file_name = 1; // name of proto file
+ string proto = 2; // content of proto file
+ bytes descriptor = 3; // compiled descriptor for proto (zlib compressed)
+}
+
+// Proto files and compiled descriptors for this interface
+message Schemas {
+
+ // Proto files
+ repeated ProtoFile protos = 1;
+
+ // Name of proto file to generae swagger.json from
+ string swagger_from = 2;
+
+}
+
+// Schema services
+service SchemaService {
+
+ // Return active grpc schemas
+ rpc GetSchema(google.protobuf.Empty) returns (Schemas) {}
+
+}
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py
new file mode 100644
index 0000000..06909b4
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: schema.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
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='schema.proto',
+ package='schema',
+ syntax='proto3',
+ serialized_pb=_b('\n\x0cschema.proto\x12\x06schema\x1a\x1bgoogle/protobuf/empty.proto\"A\n\tProtoFile\x12\x11\n\tfile_name\x18\x01 \x01(\t\x12\r\n\x05proto\x18\x02 \x01(\t\x12\x12\n\ndescriptor\x18\x03 \x01(\x0c\"B\n\x07Schemas\x12!\n\x06protos\x18\x01 \x03(\x0b\x32\x11.schema.ProtoFile\x12\x14\n\x0cswagger_from\x18\x02 \x01(\t2G\n\rSchemaService\x12\x36\n\tGetSchema\x12\x16.google.protobuf.Empty\x1a\x0f.schema.Schemas\"\x00\x62\x06proto3')
+ ,
+ dependencies=[google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,])
+
+
+
+
+_PROTOFILE = _descriptor.Descriptor(
+ name='ProtoFile',
+ full_name='schema.ProtoFile',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='file_name', full_name='schema.ProtoFile.file_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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='proto', full_name='schema.ProtoFile.proto', 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='descriptor', full_name='schema.ProtoFile.descriptor', index=2,
+ number=3, type=12, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b(""),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=53,
+ serialized_end=118,
+)
+
+
+_SCHEMAS = _descriptor.Descriptor(
+ name='Schemas',
+ full_name='schema.Schemas',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='protos', full_name='schema.Schemas.protos', 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,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='swagger_from', full_name='schema.Schemas.swagger_from', 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,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=120,
+ serialized_end=186,
+)
+
+_SCHEMAS.fields_by_name['protos'].message_type = _PROTOFILE
+DESCRIPTOR.message_types_by_name['ProtoFile'] = _PROTOFILE
+DESCRIPTOR.message_types_by_name['Schemas'] = _SCHEMAS
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+ProtoFile = _reflection.GeneratedProtocolMessageType('ProtoFile', (_message.Message,), dict(
+ DESCRIPTOR = _PROTOFILE,
+ __module__ = 'schema_pb2'
+ # @@protoc_insertion_point(class_scope:schema.ProtoFile)
+ ))
+_sym_db.RegisterMessage(ProtoFile)
+
+Schemas = _reflection.GeneratedProtocolMessageType('Schemas', (_message.Message,), dict(
+ DESCRIPTOR = _SCHEMAS,
+ __module__ = 'schema_pb2'
+ # @@protoc_insertion_point(class_scope:schema.Schemas)
+ ))
+_sym_db.RegisterMessage(Schemas)
+
+
+
+_SCHEMASERVICE = _descriptor.ServiceDescriptor(
+ name='SchemaService',
+ full_name='schema.SchemaService',
+ file=DESCRIPTOR,
+ index=0,
+ options=None,
+ serialized_start=188,
+ serialized_end=259,
+ methods=[
+ _descriptor.MethodDescriptor(
+ name='GetSchema',
+ full_name='schema.SchemaService.GetSchema',
+ index=0,
+ containing_service=None,
+ input_type=google_dot_protobuf_dot_empty__pb2._EMPTY,
+ output_type=_SCHEMAS,
+ options=None,
+ ),
+])
+_sym_db.RegisterServiceDescriptor(_SCHEMASERVICE)
+
+DESCRIPTOR.services_by_name['SchemaService'] = _SCHEMASERVICE
+
+# @@protoc_insertion_point(module_scope)
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py
new file mode 100644
index 0000000..65c9afa
--- /dev/null
+++ b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+# Copyright 2017 the original author or 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.
+#
+
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
+from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
+import schema_pb2 as schema__pb2
+
+
+class SchemaServiceStub(object):
+ """Schema services
+ """
+
+ def __init__(self, channel):
+ """Constructor.
+
+ Args:
+ channel: A grpc.Channel.
+ """
+ self.GetSchema = channel.unary_unary(
+ '/schema.SchemaService/GetSchema',
+ request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+ response_deserializer=schema__pb2.Schemas.FromString,
+ )
+
+
+class SchemaServiceServicer(object):
+ """Schema services
+ """
+
+ def GetSchema(self, request, context):
+ """Return active grpc schemas
+ """
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+
+def add_SchemaServiceServicer_to_server(servicer, server):
+ rpc_method_handlers = {
+ 'GetSchema': grpc.unary_unary_rpc_method_handler(
+ servicer.GetSchema,
+ request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+ response_serializer=schema__pb2.Schemas.SerializeToString,
+ ),
+ }
+ generic_handler = grpc.method_handlers_generic_handler(
+ 'schema.SchemaService', rpc_method_handlers)
+ server.add_generic_rpc_handlers((generic_handler,))
diff --git a/lib/xos-api/xosapi/xos_grpc_client.py b/lib/xos-api/xosapi/xos_grpc_client.py
index 13e969d..a80843f 100644
--- a/lib/xos-api/xosapi/xos_grpc_client.py
+++ b/lib/xos-api/xosapi/xos_grpc_client.py
@@ -36,7 +36,7 @@
sys.path = [currentdir] + sys.path
from xosconfig import Config
-import chameleon.grpc_client.grpc_client as chameleon_client
+import chameleon_client.grpc_client as chameleon_client
from multistructlog import create_logger
log = create_logger(Config().get("logging"))