summaryrefslogtreecommitdiffstats
path: root/cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'cyborg_enhancement/mitaka_version/cyborg/cyborg/api/controllers')
-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
10 files changed, 1134 insertions, 0 deletions
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