summaryrefslogtreecommitdiffstats
path: root/cyborg_enhancement/mitaka_version/cyborg/cyborg/api
diff options
context:
space:
mode:
Diffstat (limited to 'cyborg_enhancement/mitaka_version/cyborg/cyborg/api')
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/__init__.py0
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/app.py66
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/config.py40
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/__init__.py0
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/base.py33
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/link.py48
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/root.py72
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/__init__.py73
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/accelerators.py233
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/deployables.py210
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/ports.py269
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/types.py161
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/utils.py35
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/expose.py40
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/hooks.py112
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/__init__.py24
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/auth_token.py64
-rw-r--r--cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/parsable_error.py72
18 files changed, 1552 insertions, 0 deletions
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/__init__.py
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/app.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/app.py
new file mode 100644
index 0000000..95862da
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/app.py
@@ -0,0 +1,66 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import pecan
+
+from oslo_config import cfg
+
+from cyborg.api import config
+from cyborg.api import hooks
+from cyborg.api import middleware
+
+
+def get_pecan_config():
+ # Set up the pecan configuration
+ filename = config.__file__.replace('.pyc', '.py')
+ return pecan.configuration.conf_from_file(filename)
+
+
+def setup_app(pecan_config=None, extra_hooks=None):
+ app_hooks = [hooks.ConfigHook(),
+ hooks.ConductorAPIHook(),
+ hooks.ContextHook(pecan_config.app.acl_public_routes),
+ hooks.PublicUrlHook()]
+ if extra_hooks:
+ app_hooks.extend(extra_hooks)
+
+ if not pecan_config:
+ pecan_config = get_pecan_config()
+
+ pecan.configuration.set_config(dict(pecan_config), overwrite=True)
+
+ app = pecan.make_app(
+ pecan_config.app.root,
+ static_root=pecan_config.app.static_root,
+ debug=False,
+ force_canonical=getattr(pecan_config.app, 'force_canonical', True),
+ hooks=app_hooks,
+ wrap_app=middleware.ParsableErrorMiddleware
+ )
+
+ app = middleware.AuthTokenMiddleware(
+ app, dict(cfg.CONF),
+ public_api_routes=pecan_config.app.acl_public_routes)
+
+ return app
+
+
+class VersionSelectorApplication(object):
+ def __init__(self):
+ pc = get_pecan_config()
+ self.v1 = setup_app(pecan_config=pc)
+
+ def __call__(self, environ, start_response):
+ return self.v1(environ, start_response)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/config.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/config.py
new file mode 100644
index 0000000..32a0d27
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/config.py
@@ -0,0 +1,40 @@
+# Copyright 2017 Huawei Technologies 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.
+
+# Server Specific Configurations
+# See https://pecan.readthedocs.org/en/latest/configuration.html#server-configuration # noqa
+server = {
+ 'port': '6666',
+ 'host': '0.0.0.0'
+}
+
+# Pecan Application Configurations
+# See https://pecan.readthedocs.org/en/latest/configuration.html#application-configuration # noqa
+app = {
+ 'root': 'cyborg.api.controllers.root.RootController',
+ 'modules': ['cyborg.api'],
+ 'static_root': '%(confdir)s/public',
+ 'debug': False,
+ 'acl_public_routes': [
+ '/',
+ '/v1'
+ ]
+}
+
+# WSME Configurations
+# See https://wsme.readthedocs.org/en/latest/integrate.html#configuration
+wsme = {
+ 'debug': False
+}
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/__init__.py
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/base.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/base.py
new file mode 100644
index 0000000..9131d3a
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/base.py
@@ -0,0 +1,33 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import datetime
+
+import wsme
+from wsme import types as wtypes
+
+
+class APIBase(wtypes.Base):
+ created_at = wsme.wsattr(datetime.datetime, readonly=True)
+ """The time in UTC at which the object is created"""
+
+ updated_at = wsme.wsattr(datetime.datetime, readonly=True)
+ """The time in UTC at which the object is updated"""
+
+ def as_dict(self):
+ """Render this object as a dict of its fields."""
+ return dict((k, getattr(self, k))
+ for k in self.fields
+ if hasattr(self, k) and getattr(self, k) != wsme.Unset)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/link.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/link.py
new file mode 100644
index 0000000..fe39c69
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/link.py
@@ -0,0 +1,48 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import pecan
+from wsme import types as wtypes
+
+from cyborg.api.controllers import base
+
+
+def build_url(resource, resource_args, bookmark=False, base_url=None):
+ if base_url is None:
+ base_url = pecan.request.public_url
+
+ template = '%(url)s/%(res)s' if bookmark else '%(url)s/v1/%(res)s'
+ template += '%(args)s' if resource_args.startswith('?') else '/%(args)s'
+ return template % {'url': base_url, 'res': resource, 'args': resource_args}
+
+
+class Link(base.APIBase):
+ """A link representation."""
+
+ href = wtypes.text
+ """The url of a link."""
+
+ rel = wtypes.text
+ """The name of a link."""
+
+ type = wtypes.text
+ """Indicates the type of document/link."""
+
+ @staticmethod
+ def make_link(rel_name, url, resource, resource_args,
+ bookmark=False, type=wtypes.Unset):
+ href = build_url(resource, resource_args,
+ bookmark=bookmark, base_url=url)
+ return Link(href=href, rel=rel_name, type=type)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/root.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/root.py
new file mode 100644
index 0000000..3361bfe
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/root.py
@@ -0,0 +1,72 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import pecan
+from pecan import rest
+from wsme import types as wtypes
+
+from cyborg.api.controllers import base
+from cyborg.api.controllers import v1
+from cyborg.api import expose
+
+
+VERSION1 = 'v1'
+
+
+class Root(base.APIBase):
+ name = wtypes.text
+ """The name of the API"""
+
+ description = wtypes.text
+ """Some information about this API"""
+
+ @staticmethod
+ def convert():
+ root = Root()
+ root.name = 'OpenStack Cyborg API'
+ root.description = (
+ 'Cyborg (previously known as Nomad) is an '
+ 'OpenStack project that aims to provide a general '
+ 'purpose management framework for acceleration '
+ 'resources (i.e. various types of accelerators '
+ 'such as Crypto cards, GPU, FPGA, NVMe/NOF SSDs, '
+ 'ODP, DPDK/SPDK and so on).')
+ return root
+
+
+class RootController(rest.RestController):
+ _versions = [VERSION1]
+ """All supported API versions"""
+
+ _default_version = VERSION1
+ """The default API version"""
+
+ v1 = v1.Controller()
+
+ @expose.expose(Root)
+ def get(self):
+ return Root.convert()
+
+ @pecan.expose()
+ def _route(self, args, request=None):
+ """Overrides the default routing behavior.
+
+ It redirects the request to the default version of the cyborg API
+ if the version number is not specified in the url.
+ """
+
+ if args[0] and args[0] not in self._versions:
+ args = [self._default_version] + args
+ return super(RootController, self)._route(args)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/__init__.py
new file mode 100644
index 0000000..661e7a0
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/__init__.py
@@ -0,0 +1,73 @@
+# Copyright 2017 Huawei Technologies 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.
+
+"""Version 1 of the Cyborg API"""
+
+import pecan
+from pecan import rest
+from wsme import types as wtypes
+
+from cyborg.api.controllers import base
+from cyborg.api.controllers import link
+from cyborg.api.controllers.v1 import accelerators
+from cyborg.api.controllers.v1 import ports
+from cyborg.api.controllers.v1 import deployables
+from cyborg.api import expose
+
+
+class V1(base.APIBase):
+ """The representation of the version 1 of the API."""
+
+ id = wtypes.text
+ """The ID of the version"""
+
+ accelerator = [link.Link]
+ """Links to the accelerator resource"""
+
+ port = [link.Link]
+ """Links to the port resource"""
+
+ @staticmethod
+ def convert():
+ v1 = V1()
+ v1.id = 'v1'
+ v1.accelerator = [
+ link.Link.make_link('self', pecan.request.public_url,
+ 'accelerator', ''),
+ link.Link.make_link('bookmark', pecan.request.public_url,
+ 'accelerator', '', bookmark=True)
+ ]
+ v1.port = [
+ link.Link.make_link('self', pecan.request.public_url,
+ 'port', ''),
+ link.Link.make_link('bookmark', pecan.request.public_url,
+ 'port', '', bookmark=True)
+ ]
+ return v1
+
+
+class Controller(rest.RestController):
+ """Version 1 API controller root"""
+
+ accelerators = accelerators.AcceleratorsController()
+ ports = ports.PortsController()
+ deployables = deployables.DeployablesController()
+
+ @expose.expose(V1)
+ def get(self):
+ return V1.convert()
+
+
+__all__ = ('Controller',)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/accelerators.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/accelerators.py
new file mode 100644
index 0000000..e31d580
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/accelerators.py
@@ -0,0 +1,233 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import pecan
+from pecan import rest
+from six.moves import http_client
+import wsme
+from wsme import types as wtypes
+
+from cyborg.api.controllers import base
+from cyborg.api.controllers import link
+from cyborg.api.controllers.v1 import types
+from cyborg.api import expose
+from cyborg.common import policy
+from cyborg import objects
+from cyborg.api.controllers.v1 import utils as api_utils
+from cyborg.common import exception
+
+
+class Accelerator(base.APIBase):
+ """API representation of a accelerator.
+
+ This class enforces type checking and value constraints, and converts
+ between the internal object model and the API representation of
+ a accelerator.
+ """
+
+ uuid = types.uuid
+ """The UUID of the accelerator"""
+
+ name = wtypes.text
+ """The name of the accelerator"""
+
+ description = wtypes.text
+ """The description of the accelerator"""
+
+ project_id = types.uuid
+ """The project UUID of the accelerator"""
+
+ user_id = types.uuid
+ """The user UUID of the accelerator"""
+
+ device_type = wtypes.text
+ """The device type of the accelerator"""
+
+ acc_type = wtypes.text
+ """The type of the accelerator"""
+
+ acc_capability = wtypes.text
+ """The capability of the accelerator"""
+
+ vendor_id = wtypes.text
+ """The vendor id of the accelerator"""
+
+ product_id = wtypes.text
+ """The product id of the accelerator"""
+
+ remotable = wtypes.IntegerType()
+ """Whether the accelerator is remotable"""
+
+ links = wsme.wsattr([link.Link], readonly=True)
+ """A list containing a self link"""
+
+ def __init__(self, **kwargs):
+ super(Accelerator, self).__init__(**kwargs)
+ self.fields = []
+ for field in objects.Accelerator.fields:
+ self.fields.append(field)
+ setattr(self, field, kwargs.get(field, wtypes.Unset))
+
+ @classmethod
+ def convert_with_links(cls, obj_acc):
+ api_acc = cls(**obj_acc.as_dict())
+ url = pecan.request.public_url
+ api_acc.links = [
+ link.Link.make_link('self', url, 'accelerators', api_acc.uuid),
+ link.Link.make_link('bookmark', url, 'accelerators', api_acc.uuid,
+ bookmark=True)
+ ]
+ return api_acc
+
+
+class AcceleratorCollection(base.APIBase):
+ """API representation of a collection of accelerators."""
+
+ accelerators = [Accelerator]
+ """A list containing accelerator objects"""
+
+ @classmethod
+ def convert_with_links(cls, obj_accs):
+ collection = cls()
+ collection.accelerators = [Accelerator.convert_with_links(obj_acc)
+ for obj_acc in obj_accs]
+ return collection
+
+
+class AcceleratorPatchType(types.JsonPatchType):
+
+ _api_base = Accelerator
+
+ @staticmethod
+ def internal_attrs():
+ defaults = types.JsonPatchType.internal_attrs()
+ return defaults + ['/project_id', '/user_id', '/device_type',
+ '/acc_type', '/acc_capability', '/vendor_id',
+ '/product_id', '/remotable']
+
+
+class AcceleratorsControllerBase(rest.RestController):
+
+ _resource = None
+
+ def _get_resource(self, uuid):
+ self._resource = objects.Accelerator.get(pecan.request.context, uuid)
+ return self._resource
+
+
+class AcceleratorsController(AcceleratorsControllerBase):
+ """REST controller for Accelerators."""
+
+ @policy.authorize_wsgi("cyborg:accelerator", "create", False)
+ @expose.expose(Accelerator, body=types.jsontype,
+ status_code=http_client.CREATED)
+ def post(self, acc):
+ """Create a new accelerator.
+
+ :param acc: an accelerator within the request body.
+ """
+ context = pecan.request.context
+ obj_acc = objects.Accelerator(context, **acc)
+ new_acc = pecan.request.conductor_api.accelerator_create(
+ context, obj_acc )
+ # Set the HTTP Location Header
+ pecan.response.location = link.build_url('accelerators', new_acc.uuid)
+ return Accelerator.convert_with_links(new_acc)
+
+ @policy.authorize_wsgi("cyborg:accelerator", "get")
+ @expose.expose(Accelerator, types.uuid)
+ def get_one(self, uuid):
+ """Retrieve information about the given accelerator.
+
+ :param uuid: UUID of an accelerator.
+ """
+ obj_acc = self._resource or self._get_resource(uuid)
+ return Accelerator.convert_with_links(obj_acc)
+
+ @expose.expose(AcceleratorCollection, int, types.uuid, wtypes.text,
+ wtypes.text, types.boolean)
+ def get_all(self, limit=None, marker=None, sort_key='id', sort_dir='asc',
+ all_tenants=None):
+ """Retrieve a list of accelerators.
+
+ :param limit: Optional, to determinate the maximum number of
+ accelerators to return.
+ :param marker: Optional, to display a list of accelerators after this
+ marker.
+ :param sort_key: Optional, to sort the returned accelerators list by
+ this specified key value.
+ :param sort_dir: Optional, to return a list of accelerators with this
+ sort direction.
+ :param all_tenants: Optional, allows administrators to see the
+ accelerators owned by all tenants, otherwise only
+ the accelerators associated with the calling
+ tenant are included in the response.
+ """
+ context = pecan.request.context
+ project_only = True
+ if context.is_admin and all_tenants:
+ project_only = False
+
+ marker_obj = None
+ if marker:
+ marker_obj = objects.Accelerator.get(context, marker)
+
+ obj_accs = objects.Accelerator.list(context, limit, marker_obj,
+ sort_key, sort_dir, project_only)
+ return AcceleratorCollection.convert_with_links(obj_accs)
+
+ @policy.authorize_wsgi("cyborg:accelerator", "update")
+ @expose.expose(Accelerator, types.uuid, body=[AcceleratorPatchType])
+ def patch(self, uuid, patch):
+ """Update an accelerator.
+
+ :param uuid: UUID of an accelerator.
+ :param patch: a json PATCH document to apply to this accelerator.
+ """
+ obj_acc = self._resource or self._get_resource(uuid)
+ try:
+ api_acc = Accelerator(
+ **api_utils.apply_jsonpatch(obj_acc.as_dict(), patch))
+ except api_utils.JSONPATCH_EXCEPTIONS as e:
+ raise exception.PatchError(patch=patch, reason=e)
+
+ # Update only the fields that have changed
+ for field in objects.Accelerator.fields:
+ try:
+ patch_val = getattr(api_acc, field)
+ except AttributeError:
+ # Ignore fields that aren't exposed in the API
+ continue
+ if patch_val == wtypes.Unset:
+ patch_val = None
+ if obj_acc[field] != patch_val:
+ obj_acc[field] = patch_val
+
+ context = pecan.request.context
+ new_acc = pecan.request.conductor_api.accelerator_update(context,
+ obj_acc)
+ return Accelerator.convert_with_links(new_acc)
+
+ @policy.authorize_wsgi("cyborg:accelerator", "delete")
+ @expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT)
+ def delete(self, uuid):
+ """Delete an accelerator.
+
+ :param uuid: UUID of an accelerator.
+ """
+ obj_acc = self._resource or self._get_resource(uuid)
+ context = pecan.request.context
+ pecan.request.conductor_api.accelerator_delete(context, obj_acc)
+
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/deployables.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/deployables.py
new file mode 100644
index 0000000..8a4a12f
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/deployables.py
@@ -0,0 +1,210 @@
+# Copyright 2018 Huawei Technologies 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.
+
+import pecan
+from pecan import rest
+from six.moves import http_client
+import wsme
+from wsme import types as wtypes
+
+from cyborg.api.controllers import base
+from cyborg.api.controllers import link
+from cyborg.api.controllers.v1 import types
+from cyborg.api.controllers.v1 import utils as api_utils
+from cyborg.api import expose
+from cyborg.common import exception
+from cyborg.common import policy
+from cyborg import objects
+
+
+class Deployable(base.APIBase):
+ """API representation of a deployable.
+ This class enforces type checking and value constraints, and converts
+ between the internal object model and the API representation of
+ a deployable.
+ """
+
+ uuid = types.uuid
+ """The UUID of the deployable"""
+
+ name = wtypes.text
+ """The name of the deployable"""
+
+ parent_uuid = types.uuid
+ """The parent UUID of the deployable"""
+
+ root_uuid = types.uuid
+ """The root UUID of the deployable"""
+
+ pcie_address = wtypes.text
+ """The pcie address of the deployable"""
+
+ host = wtypes.text
+ """The host on which the deployable is located"""
+
+ board = wtypes.text
+ """The board of the deployable"""
+
+ vendor = wtypes.text
+ """The vendor of the deployable"""
+
+ version = wtypes.text
+ """The version of the deployable"""
+
+ type = wtypes.text
+ """The type of the deployable"""
+
+ assignable = types.boolean
+ """Whether the deployable is assignable"""
+
+ instance_uuid = types.uuid
+ """The UUID of the instance which deployable is assigned to"""
+
+ availability = wtypes.text
+ """The availability of the deployable"""
+
+ links = wsme.wsattr([link.Link], readonly=True)
+ """A list containing a self link"""
+
+ def __init__(self, **kwargs):
+ super(Deployable, self).__init__(**kwargs)
+ self.fields = []
+ for field in objects.Deployable.fields:
+ self.fields.append(field)
+ setattr(self, field, kwargs.get(field, wtypes.Unset))
+
+ @classmethod
+ def convert_with_links(cls, obj_dep):
+ api_dep = cls(**obj_dep.as_dict())
+ url = pecan.request.public_url
+ api_dep.links = [
+ link.Link.make_link('self', url, 'deployables', api_dep.uuid),
+ link.Link.make_link('bookmark', url, 'deployables', api_dep.uuid,
+ bookmark=True)
+ ]
+ return api_dep
+
+
+class DeployableCollection(base.APIBase):
+ """API representation of a collection of deployables."""
+
+ deployables = [Deployable]
+ """A list containing deployable objects"""
+
+ @classmethod
+ def convert_with_links(cls, obj_deps):
+ collection = cls()
+ collection.deployables = [Deployable.convert_with_links(obj_dep)
+ for obj_dep in obj_deps]
+ return collection
+
+
+class DeployablePatchType(types.JsonPatchType):
+
+ _api_base = Deployable
+
+ @staticmethod
+ def internal_attrs():
+ defaults = types.JsonPatchType.internal_attrs()
+ return defaults + ['/pcie_address', '/host', '/type']
+
+
+class DeployablesController(rest.RestController):
+ """REST controller for Deployables."""
+
+ @policy.authorize_wsgi("cyborg:deployable", "create", False)
+ @expose.expose(Deployable, body=types.jsontype,
+ status_code=http_client.CREATED)
+ def post(self, dep):
+ """Create a new deployable.
+ :param dep: a deployable within the request body.
+ """
+ context = pecan.request.context
+ obj_dep = objects.Deployable(context, **dep)
+ new_dep = pecan.request.conductor_api.deployable_create(context,
+ obj_dep)
+ # Set the HTTP Location Header
+ pecan.response.location = link.build_url('deployables', new_dep.uuid)
+ return Deployable.convert_with_links(new_dep)
+
+ @policy.authorize_wsgi("cyborg:deployable", "get_one")
+ @expose.expose(Deployable, types.uuid)
+ def get_one(self, uuid):
+ """Retrieve information about the given deployable.
+ :param uuid: UUID of a deployable.
+ """
+
+ obj_dep = objects.Deployable.get(pecan.request.context, uuid)
+ return Deployable.convert_with_links(obj_dep)
+
+ @policy.authorize_wsgi("cyborg:deployable", "get_all")
+ @expose.expose(DeployableCollection, types.uuid, wtypes.text,
+ wtypes.text, types.boolean)
+ def get_all(self, root_uuid=None, host=None, type=None, assignable=None):
+ """Retrieve a list of deployables."""
+ filters = {}
+ if root_uuid:
+ filters["root_uuid"] = root_uuid
+ if host:
+ filters["host"] = host
+ if type:
+ filters["type"] = type
+ if assignable:
+ filters["assignable"] = assignable
+ obj_deps = objects.Deployable.get_by_filter(pecan.request.context,
+ filters=filters)
+ return DeployableCollection.convert_with_links(obj_deps)
+
+ @policy.authorize_wsgi("cyborg:deployable", "update")
+ @expose.expose(Deployable, types.uuid, body=[DeployablePatchType])
+ def patch(self, uuid, patch):
+ """Update a deployable.
+ :param uuid: UUID of a deployable.
+ :param patch: a json PATCH document to apply to this deployable.
+ """
+ context = pecan.request.context
+ obj_dep = objects.Deployable.get(context, uuid)
+
+ try:
+ api_dep = Deployable(
+ **api_utils.apply_jsonpatch(obj_dep.as_dict(), patch))
+ except api_utils.JSONPATCH_EXCEPTIONS as e:
+ raise exception.PatchError(patch=patch, reason=e)
+
+ # Update only the fields that have changed
+ for field in objects.Deployable.fields:
+ try:
+ patch_val = getattr(api_dep, field)
+ except AttributeError:
+ # Ignore fields that aren't exposed in the API
+ continue
+ if patch_val == wtypes.Unset:
+ patch_val = None
+ if obj_dep[field] != patch_val:
+ obj_dep[field] = patch_val
+
+ new_dep = pecan.request.conductor_api.deployable_update(context,
+ obj_dep)
+ return Deployable.convert_with_links(new_dep)
+
+ @policy.authorize_wsgi("cyborg:deployable", "delete")
+ @expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT)
+ def delete(self, uuid):
+ """Delete a deployable.
+ :param uuid: UUID of a deployable.
+ """
+ context = pecan.request.context
+ obj_dep = objects.Deployable.get(context, uuid)
+ pecan.request.conductor_api.deployable_delete(context, obj_dep) \ No newline at end of file
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/ports.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/ports.py
new file mode 100644
index 0000000..7d6c1dd
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/ports.py
@@ -0,0 +1,269 @@
+# Copyright 2018 Lenovo Research 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.
+
+import pecan
+from pecan import rest
+from six.moves import http_client
+import wsme
+from wsme import types as wtypes
+
+from cyborg.api.controllers import base
+from cyborg.api.controllers import link
+from cyborg.api.controllers.v1 import types
+from cyborg.api import expose
+from pecan import expose as pexpose
+from cyborg.common import policy
+from cyborg import objects
+from cyborg.api.controllers.v1 import utils as api_utils
+from cyborg.common import exception
+
+from oslo_log import log as logging
+
+LOG = logging.getLogger(__name__)
+
+class Port(base.APIBase):
+ """API representation of a port.
+
+ This class enforces type checking and value constraints, and converts
+ between the internal object model and the API representation of
+ a port.
+ """
+
+ uuid = types.uuid
+ computer_id = types.uuid
+ phy_port_name = wtypes.text
+ pci_slot = wtypes.text
+ product_id = wtypes.text
+ vendor_id = wtypes.text
+ is_used = wtypes.IntegerType()
+ accelerator_id = types.uuid
+ bind_instance_id = types.uuid
+ bind_port_id = types.uuid
+ device_type = wtypes.text
+
+ links = wsme.wsattr([link.Link], readonly=True)
+ """A list containing a self link"""
+
+ def __init__(self, **kwargs):
+ self.fields = []
+ for field in objects.Port.fields:
+ self.fields.append(field)
+ setattr(self, field, kwargs.get(field, wtypes.Unset))
+
+ @classmethod
+ def convert_with_links(cls, rpc_acc):
+ port = Port(**rpc_acc.as_dict())
+ url = pecan.request.public_url
+ port.links = [
+ link.Link.make_link('self', url, 'ports',
+ port.uuid),
+ link.Link.make_link('bookmark', url, 'ports',
+ port.uuid, bookmark=True)
+ ]
+
+ return port
+
+
+
+class PortCollection(base.APIBase):
+ """API representation of a collection of ports."""
+
+ ports = [Port]
+ """A list containing port objects"""
+
+ @classmethod
+ def convert_with_links(cls, rpc_ports):
+ collection = cls()
+ collection.ports = [Port.convert_with_links(obj_port)
+ for obj_port in rpc_ports]
+ return collection
+
+
+class PortPatchType(types.JsonPatchType):
+
+ _api_base = Port
+
+ @staticmethod
+ def internal_attrs():
+ defaults = types.JsonPatchType.internal_attrs()
+ return defaults + ['/computer_id', '/phy_port_name', '/pci_slot',
+ '/vendor_id', '/product_id']
+
+
+class PortsControllerBase(rest.RestController):
+ _resource = None
+ def _get_resource(self, uuid):
+ self._resource = objects.Port.get(pecan.request.context, uuid)
+ return self._resource
+
+
+class BindPortController(PortsControllerBase):
+ # url path: /v1/ports/bind/{uuid}
+
+ @expose.expose(Port, body=types.jsontype)
+ def put(self, uuid, patch):
+ """bind a existing port to a logical neutron port.
+ : param uuid: UUID of a port.
+ : param patch: a json type to apply to this port.
+ """
+ context = pecan.request.context
+ obj_port = self._resource or self._get_resource(uuid)
+ # object with user modified properties.
+ mod_port = objects.Port(context, **patch)
+
+ # update fields used in bind.
+ obj_port["accelerator_id"] = mod_port["accelerator_id"]
+ obj_port["bind_instance_id"] = mod_port["bind_instance_id"]
+ obj_port["bind_port_id"] = mod_port["bind_port_id"]
+ obj_port["is_used"] = mod_port["is_used"]
+ obj_port["device_type"] = mod_port["device_type"]
+
+ LOG.debug(obj_port)
+ new_port = pecan.request.conductor_api.port_update(context, obj_port)
+ return Port.convert_with_links(new_port)
+
+class UnBindPortController(PortsControllerBase):
+ # url path: /v1/ports/bind/{uuid}
+
+ @expose.expose(Port, body=types.jsontype)
+ def put(self, uuid):
+ """unbind a existing port, set some areas to null in DB.
+ : param uuid: UUID of a port.
+ : param patch: a json type to apply to this port.
+ """
+ context = pecan.request.context
+ obj_port = self._resource or self._get_resource(uuid)
+
+ # update fields used in unbind.
+ obj_port["accelerator_id"] = None
+ obj_port["bind_instance_id"] = None
+ obj_port["bind_port_id"] = None
+ obj_port["is_used"] = 0
+ obj_port["device_type"] = None
+
+ new_port = pecan.request.conductor_api.port_update(context, obj_port)
+ return Port.convert_with_links(new_port)
+
+
+class PortsController(PortsControllerBase):
+ """REST controller for Ports.
+ url path: /v2.0/ports/
+ """
+ bind = BindPortController()
+ unbind = UnBindPortController()
+
+ @policy.authorize_wsgi("cyborg:port", "create", False)
+ @expose.expose(Port, body=types.jsontype,
+ status_code=http_client.CREATED)
+ def post(self, port):
+ """Create a new port.
+
+ :param port: an port within the request body.
+ """
+ context = pecan.request.context
+ rpc_port = objects.Port(context, **port)
+ new_port = pecan.request.conductor_api.port_create(
+ context, rpc_port)
+ # Set the HTTP Location Header
+ pecan.response.location = link.build_url('ports',
+ new_port.uuid)
+ return Port.convert_with_links(new_port)
+
+ #@policy.authorize_wsgi("cyborg:port", "get")
+ @expose.expose(Port, types.uuid)
+ def get_one(self, uuid):
+ """Retrieve information about the given uuid port.
+ : param uuid: UUID of a port.
+ """
+ rpc_port = self._get_resource(uuid)
+ if rpc_port == None:
+ return pecan.abort(404, detail='The uuid Not Found.')
+ else:
+ return Port.convert_with_links(rpc_port)
+
+ @expose.expose(PortCollection, int, types.uuid, wtypes.text,
+ wtypes.text, types.boolean)
+ def get_all(self, limit = None, marker = None, sort_key='id',
+ sort_dir='asc'):
+ """Retrieve a list of ports.
+ : param limit: Optional, to determine the maximum number of
+ ports to return.
+ : param marker: Optional, to display a list of ports after
+ this marker.
+ : param sort_dir: Optional, to return a list of ports with this
+ sort direction.
+ : param all_tenants: Optional, allows administrators to see the
+ ports owned by all tenants, otherwise only the ports
+ associated with the calling tenant are included in the response."""
+
+ context = pecan.request.context
+ marker_obj = None;
+ if marker:
+ marker_obj = objects.Port.get(context, marker)
+
+ rpc_ports = objects.Port.list(
+ context, limit, marker_obj, sort_key, sort_dir)
+
+ return PortCollection.convert_with_links(rpc_ports)
+
+ #@policy.authorize_wsgi("cyborg:port", "update")
+ @expose.expose(Port, types.uuid, body=[PortPatchType])
+ def put(self, uuid, patch):
+ """Update an port's property.
+ : param uuid: UUID of a port.
+ : param patch: a json PATCH document to apply to this port.
+ """
+ obj_port = self._resource or self._get_resource(uuid)
+ try:
+ api_port = Port(**api_utils.apply_jsonpatch(obj_port.as_dict(), patch))
+ except api_utils.JSONPATCH_EXCEPTIONS as e:
+ raise exception.PatchError(patch=patch, reason=e)
+
+ #update only the fields that have changed.
+ for field in objects.Port.fields:
+ try:
+ patch_val = getattr(api_port, field)
+ except AttributeError:
+ # Ignore fields that aren't exposed in the API
+ continue
+
+ if patch_val == wtypes.Unset:
+ patch_val = None
+ if obj_port[field] != patch_val:
+ obj_port[field] = patch_val
+
+ context = pecan.request.context
+ new_port = pecan.request.conductor_api.port_update(context, obj_port)
+ return Port.convert_with_links(new_port)
+
+
+ #@policy.authorize_wsgi("cyborg:port", "delete")
+ @expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT)
+ def delete(self, uuid):
+ """Delete a port.
+ :param uuid: UUID of the port."""
+
+ rpc_port = self._resource or self._get_resource(uuid)
+ if rpc_port == None:
+ status_code = http_client.NOT_FOUND
+ context = pecan.request.context
+ pecan.request.conductor_api.port_delete(context, rpc_port)
+
+
+
+
+
+
+
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/types.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/types.py
new file mode 100644
index 0000000..61ce387
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/types.py
@@ -0,0 +1,161 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import json
+
+from oslo_utils import uuidutils
+from wsme import types as wtypes
+import wsme
+
+import inspect
+from oslo_utils import strutils
+from cyborg.common.i18n import _
+from cyborg.common import exception
+
+
+class UUIDType(wtypes.UserType):
+ """A simple UUID type."""
+
+ basetype = wtypes.text
+ name = 'uuid'
+
+ @staticmethod
+ def validate(value):
+ if not uuidutils.is_uuid_like(value):
+ raise exception.InvalidUUID(uuid=value)
+ return value
+
+ @staticmethod
+ def frombasetype(value):
+ if value is None:
+ return None
+ return UUIDType.validate(value)
+
+
+class JsonType(wtypes.UserType):
+ """A simple JSON type."""
+
+ basetype = wtypes.text
+ name = 'json'
+
+ @staticmethod
+ def validate(value):
+ try:
+ json.dumps(value)
+ except TypeError:
+ raise exception.InvalidJsonType(value=value)
+ else:
+ return value
+
+ @staticmethod
+ def frombasetype(value):
+ return JsonType.validate(value)
+
+
+class BooleanType(wtypes.UserType):
+ """A simple boolean type."""
+
+ basetype = wtypes.text
+ name = 'boolean'
+
+ @staticmethod
+ def validate(value):
+ try:
+ return strutils.bool_from_string(value, strict=True)
+ except ValueError as e:
+ # raise Invalid to return 400 (BadRequest) in the API
+ raise exception.Invalid(e)
+
+ @staticmethod
+ def frombasetype(value):
+ if value is None:
+ return None
+ return BooleanType.validate(value)
+
+
+uuid = UUIDType()
+jsontype = JsonType()
+boolean = BooleanType()
+
+
+class JsonPatchType(wtypes.Base):
+ """A complex type that represents a single json-patch operation."""
+
+ path = wtypes.wsattr(wtypes.StringType(pattern='^(/[\w-]+)+$'),
+ mandatory=True)
+ op = wtypes.wsattr(wtypes.Enum(str, 'add', 'replace', 'remove'),
+ mandatory=True)
+ value = wtypes.wsattr(jsontype, default=wtypes.Unset)
+
+ # The class of the objects being patched. Override this in subclasses.
+ # Should probably be a subclass of cyborg.api.controllers.base.APIBase.
+ _api_base = None
+
+ # Attributes that are not required for construction, but which may not be
+ # removed if set. Override in subclasses if needed.
+ _extra_non_removable_attrs = set()
+
+ # Set of non-removable attributes, calculated lazily.
+ _non_removable_attrs = None
+
+ @staticmethod
+ def internal_attrs():
+ """Returns a list of internal attributes.
+
+ Internal attributes can't be added, replaced or removed. This
+ method may be overwritten by derived class.
+
+ """
+ return ['/created_at', '/id', '/links', '/updated_at', '/uuid']
+
+ @classmethod
+ def non_removable_attrs(cls):
+ """Returns a set of names of attributes that may not be removed.
+
+ Attributes whose 'mandatory' property is True are automatically added
+ to this set. To add additional attributes to the set, override the
+ field _extra_non_removable_attrs in subclasses, with a set of the form
+ {'/foo', '/bar'}.
+ """
+ if cls._non_removable_attrs is None:
+ cls._non_removable_attrs = cls._extra_non_removable_attrs.copy()
+ if cls._api_base:
+ fields = inspect.getmembers(cls._api_base,
+ lambda a: not inspect.isroutine(a))
+ for name, field in fields:
+ if getattr(field, 'mandatory', False):
+ cls._non_removable_attrs.add('/%s' % name)
+ return cls._non_removable_attrs
+
+ @staticmethod
+ def validate(patch):
+ _path = '/' + patch.path.split('/')[1]
+ if _path in patch.internal_attrs():
+ msg = _("'%s' is an internal attribute and can not be updated")
+ raise wsme.exc.ClientSideError(msg % patch.path)
+
+ if patch.path in patch.non_removable_attrs() and patch.op == 'remove':
+ msg = _("'%s' is a mandatory attribute and can not be removed")
+ raise wsme.exc.ClientSideError(msg % patch.path)
+
+ if patch.op != 'remove':
+ if patch.value is wsme.Unset:
+ msg = _("'add' and 'replace' operations need a value")
+ raise wsme.exc.ClientSideError(msg)
+
+ ret = {'path': patch.path, 'op': patch.op}
+ if patch.value is not wsme.Unset:
+ ret['value'] = patch.value
+ return ret
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/utils.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/utils.py
new file mode 100644
index 0000000..6c88d3e
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers/v1/utils.py
@@ -0,0 +1,35 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import jsonpatch
+import wsme
+
+
+from cyborg.common.i18n import _
+
+
+JSONPATCH_EXCEPTIONS = (jsonpatch.JsonPatchException,
+ jsonpatch.JsonPointerException,
+ KeyError)
+
+
+def apply_jsonpatch(doc, patch):
+ for p in patch:
+ if p['op'] == 'add' and p['path'].count('/') == 1:
+ if p['path'].lstrip('/') not in doc:
+ msg = _('Adding a new attribute (%s) to the root of '
+ ' the resource is not allowed')
+ raise wsme.exc.ClientSideError(msg % p['path'])
+ return jsonpatch.apply_patch(doc, jsonpatch.JsonPatch(patch)) \ No newline at end of file
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/expose.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/expose.py
new file mode 100644
index 0000000..bcc92f4
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/expose.py
@@ -0,0 +1,40 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import wsmeext.pecan as wsme_pecan
+from pecan import expose as p_expose
+
+default_kargs = {
+ 'template': 'json',
+ 'content_type': 'application/json'
+}
+
+def expose(*args, **kwargs):
+ """Ensure that only JSON, and not XML, is supported."""
+ if 'rest_content_types' not in kwargs:
+ kwargs['rest_content_types'] = ('json',)
+ return wsme_pecan.wsexpose(*args, **kwargs)
+
+def content_expose(*args, **kwargs):
+ """Helper function so we don't have to specify json for everything."""
+ kwargs.setdefault('template', default_kargs['template'])
+ kwargs.setdefault('content_type', default_kargs['content_type'])
+ return p_expose(*args, **kwargs)
+
+def when(index, *args, **kwargs):
+ """Helper function so we don't have to specify json for everything."""
+ kwargs.setdefault('template', default_kargs['template'])
+ kwargs.setdefault('content_type', default_kargs['content_type'])
+ return index.when(*args, **kwargs)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/hooks.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/hooks.py
new file mode 100644
index 0000000..6793982
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/hooks.py
@@ -0,0 +1,112 @@
+# Copyright 2017 Huawei Technologies 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_config import cfg
+from oslo_context import context
+from pecan import hooks
+
+from cyborg.common import policy
+from cyborg.conductor import rpcapi
+
+
+class ConfigHook(hooks.PecanHook):
+ """Attach the config object to the request so controllers can get to it."""
+
+ def before(self, state):
+ state.request.cfg = cfg.CONF
+
+
+class PublicUrlHook(hooks.PecanHook):
+ """Attach the right public_url to the request.
+
+ Attach the right public_url to the request so resources can create
+ links even when the API service is behind a proxy or SSL terminator.
+ """
+
+ def before(self, state):
+ state.request.public_url = (
+ cfg.CONF.api.public_endpoint or state.request.host_url)
+
+
+class ConductorAPIHook(hooks.PecanHook):
+ """Attach the conductor_api object to the request."""
+
+ def __init__(self):
+ self.conductor_api = rpcapi.ConductorAPI()
+
+ def before(self, state):
+ state.request.conductor_api = self.conductor_api
+
+
+class ContextHook(hooks.PecanHook):
+ """Configures a request context and attaches it to the request.
+
+ The following HTTP request headers are used:
+
+ X-User-Id or X-User:
+ Used for context.user.
+
+ X-Tenant-Id or X-Tenant:
+ Used for context.tenant.
+
+ X-Auth-Token:
+ Used for context.auth_token.
+
+ X-Roles:
+ Used for setting context.is_admin flag to either True or False.
+ The flag is set to True, if X-Roles contains either an administrator
+ or admin substring. Otherwise it is set to False.
+
+ """
+
+ def __init__(self, public_api_routes):
+ self.public_api_routes = public_api_routes
+ super(ContextHook, self).__init__()
+
+ def before(self, state):
+ headers = state.request.headers
+ '''
+ creds = {
+ 'user_name': headers.get('X-User-Name'),
+ 'user': headers.get('X-User-Id'),
+ 'project_name': headers.get('X-Project-Name'),
+ 'tenant': headers.get('X-Project-Id'),
+ 'domain': headers.get('X-User-Domain-Id'),
+ 'domain_name': headers.get('X-User-Domain-Name'),
+ 'auth_token': headers.get('X-Auth-Token'),
+ 'roles': headers.get('X-Roles', '').split(','),
+ }'''
+
+ creds = {
+ 'user': headers.get('X-User-Id'),
+ 'tenant': headers.get('X-Project-Id'),
+ 'domain': headers.get('X-User-Domain-Id',''),
+ 'auth_token': headers.get('X-Auth-Token'),
+ 'roles': headers.get('X-Roles', '').split(','),
+ }
+
+
+ is_admin = policy.authorize('is_admin', creds, creds)
+ state.request.context = context.RequestContext(
+ is_admin=is_admin, **creds)
+
+ def after(self, state):
+ if state.request.context == {}:
+ # An incorrect url path will not create RequestContext
+ return
+ # RequestContext will generate a request_id if no one
+ # passing outside, so it always contain a request_id.
+ request_id = state.request.context.request_id
+ state.response.headers['Openstack-Request-Id'] = request_id
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/__init__.py
new file mode 100644
index 0000000..95cc740
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/__init__.py
@@ -0,0 +1,24 @@
+# Copyright 2017 Huawei Technologies 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 cyborg.api.middleware import auth_token
+from cyborg.api.middleware import parsable_error
+
+
+ParsableErrorMiddleware = parsable_error.ParsableErrorMiddleware
+AuthTokenMiddleware = auth_token.AuthTokenMiddleware
+
+__all__ = ('ParsableErrorMiddleware',
+ 'AuthTokenMiddleware')
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/auth_token.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/auth_token.py
new file mode 100644
index 0000000..95b5323
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/auth_token.py
@@ -0,0 +1,64 @@
+# Copyright 2017 Huawei Technologies 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.
+
+import re
+
+from keystonemiddleware import auth_token
+from oslo_log import log
+
+from cyborg.common import exception
+from cyborg.common.i18n import _
+from cyborg.common import utils
+
+
+LOG = log.getLogger(__name__)
+
+
+class AuthTokenMiddleware(auth_token.AuthProtocol):
+ """A wrapper on Keystone auth_token middleware.
+
+ Does not perform verification of authentication tokens
+ for public routes in the API.
+
+ """
+ def __init__(self, app, conf, public_api_routes=None):
+ public_api_routes = public_api_routes or []
+ self.app = app
+ route_pattern_tpl = '%s(\.json)?$'
+
+ try:
+ self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl)
+ for route_tpl in public_api_routes]
+ except re.error as e:
+ msg = _('Cannot compile public API routes: %s') % e
+
+ LOG.error(msg)
+ raise exception.ConfigInvalid(error_msg=msg)
+
+ super(AuthTokenMiddleware, self).__init__(app, conf)
+
+ def __call__(self, env, start_response):
+ path = utils.safe_rstrip(env.get('PATH_INFO'), '/')
+
+ # The information whether the API call is being performed against the
+ # public API is required for some other components. Saving it to the
+ # WSGI environment is reasonable thereby.
+ env['is_public_api'] = any(map(lambda pattern: re.match(pattern, path),
+ self.public_api_routes))
+
+ if env['is_public_api']:
+ return self.app(env, start_response)
+
+ return super(AuthTokenMiddleware, self).__call__(env, start_response)
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/parsable_error.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/parsable_error.py
new file mode 100644
index 0000000..ba80c22
--- /dev/null
+++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/middleware/parsable_error.py
@@ -0,0 +1,72 @@
+# Copyright 2017 Huawei Technologies 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.
+
+"""
+Middleware to replace the plain text message body of an error
+response with one formatted so the client can parse it.
+
+Based on pecan.middleware.errordocument
+"""
+
+import json
+
+import six
+
+
+class ParsableErrorMiddleware(object):
+ """Replace error body with something the client can parse."""
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ # Request for this state, modified by replace_start_response()
+ # and used when an error is being reported.
+ state = {}
+
+ def replacement_start_response(status, headers, exc_info=None):
+ """Overrides the default response to make errors parsable."""
+ try:
+ status_code = int(status.split(' ')[0])
+ state['status_code'] = status_code
+ except (ValueError, TypeError): # pragma: nocover
+ raise Exception(
+ 'ParsableErrorMiddleware received an invalid '
+ 'status %s' % status)
+
+ if (state['status_code'] // 100) not in (2, 3):
+ # Remove some headers so we can replace them later
+ # when we have the full error message and can
+ # compute the length.
+ headers = [
+ (h, v) for (h, v) in headers
+ if h not in ('Content-Length', 'Content-Type')]
+
+ # Save the headers in case we need to modify them.
+ state['headers'] = headers
+ return start_response(status, headers, exc_info)
+
+ app_iter = self.app(environ, replacement_start_response)
+
+ if (state['status_code'] // 100) not in (2, 3):
+ if six.PY3:
+ app_iter = [i.decode('utf-8') for i in app_iter]
+ body = [json.dumps({'error_message': '\n'.join(app_iter)})]
+ if six.PY3:
+ body = [i.encode('utf-8') for i in body]
+ state['headers'].append(('Content-Type', 'application/json'))
+ state['headers'].append(('Content-Length', str(len(body[0]))))
+ else:
+ body = app_iter
+ return body