Implement FPGA data model and corresponding API
This patch implements the Deployables data model and provides corresponding CRUD REST/RPC API. Since the CREATE/UPDATE/DELETE method of Deployables should only be used by internal component, currently keep these in code is just for convenient test purpose, which will be removed later. blueprint cyborg-fpga-modelling Change-Id: I8a028954bd27d015c0b062f730673ea39701774d
This commit is contained in:
parent
b22761ab0d
commit
aee55527a9
|
@ -22,6 +22,7 @@ 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 deployables
|
||||
from cyborg.api import expose
|
||||
|
||||
|
||||
|
@ -51,6 +52,7 @@ class Controller(rest.RestController):
|
|||
"""Version 1 API controller root"""
|
||||
|
||||
accelerators = accelerators.AcceleratorsController()
|
||||
deployables = deployables.DeployablesController()
|
||||
|
||||
@expose.expose(V1)
|
||||
def get(self):
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
# 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, int, types.uuid, wtypes.text,
|
||||
wtypes.text, types.boolean)
|
||||
def get_all(self):
|
||||
"""Retrieve a list of deployables."""
|
||||
obj_deps = objects.Deployable.list(pecan.request.context)
|
||||
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)
|
|
@ -94,6 +94,10 @@ class AcceleratorAlreadyExists(CyborgException):
|
|||
_msg_fmt = _("Accelerator with uuid %(uuid)s already exists.")
|
||||
|
||||
|
||||
class DeployableAlreadyExists(CyborgException):
|
||||
_msg_fmt = _("Deployable with uuid %(uuid)s already exists.")
|
||||
|
||||
|
||||
class Invalid(CyborgException):
|
||||
_msg_fmt = _("Invalid parameters.")
|
||||
code = http_client.BAD_REQUEST
|
||||
|
@ -139,15 +143,23 @@ class AcceleratorNotFound(NotFound):
|
|||
_msg_fmt = _("Accelerator %(uuid)s could not be found.")
|
||||
|
||||
|
||||
class DeployableNotFound(NotFound):
|
||||
_msg_fmt = _("Deployable %(uuid)s could not be found.")
|
||||
|
||||
|
||||
class Conflict(CyborgException):
|
||||
_msg_fmt = _('Conflict.')
|
||||
code = http_client.CONFLICT
|
||||
|
||||
|
||||
class DuplicateName(Conflict):
|
||||
class DuplicateAcceleratorName(Conflict):
|
||||
_msg_fmt = _("An accelerator with name %(name)s already exists.")
|
||||
|
||||
|
||||
class DuplicateDeployableName(Conflict):
|
||||
_msg_fmt = _("A deployable with name %(name)s already exists.")
|
||||
|
||||
|
||||
class PlacementEndpointNotFound(NotFound):
|
||||
message = _("Placement API endpoint not found")
|
||||
|
||||
|
@ -164,3 +176,7 @@ class PlacementInventoryNotFound(NotFound):
|
|||
class PlacementInventoryUpdateConflict(Conflict):
|
||||
message = _("Placement inventory update conflict for resource provider "
|
||||
"%(resource_provider)s, resource class %(resource_class)s.")
|
||||
|
||||
|
||||
class ObjectActionError(CyborgException):
|
||||
_msg_fmt = _('Object action %(action)s failed because: %(reason)s')
|
||||
|
|
|
@ -88,9 +88,27 @@ accelerator_policies = [
|
|||
description='Update accelerator records'),
|
||||
]
|
||||
|
||||
deployable_policies = [
|
||||
policy.RuleDefault('cyborg:deployable:get_one',
|
||||
'rule:allow',
|
||||
description='Show deployable detail'),
|
||||
policy.RuleDefault('cyborg:deployable:get_all',
|
||||
'rule:allow',
|
||||
description='Retrieve all deployable records'),
|
||||
policy.RuleDefault('cyborg:deployable:create',
|
||||
'rule:admin_api',
|
||||
description='Create deployable records'),
|
||||
policy.RuleDefault('cyborg:deployable:delete',
|
||||
'rule:admin_api',
|
||||
description='Delete deployable records'),
|
||||
policy.RuleDefault('cyborg:deployable:update',
|
||||
'rule:admin_api',
|
||||
description='Update deployable records'),
|
||||
]
|
||||
|
||||
|
||||
def list_policies():
|
||||
return default_policies + accelerator_policies
|
||||
return default_policies + accelerator_policies + deployable_policies
|
||||
|
||||
|
||||
@lockutils.synchronized('policy_enforcer', 'cyborg-')
|
||||
|
|
|
@ -65,3 +65,57 @@ class ConductorManager(object):
|
|||
:param obj_acc: an accelerator object to delete.
|
||||
"""
|
||||
obj_acc.destroy(context)
|
||||
|
||||
def deployable_create(self, context, obj_dep):
|
||||
"""Create a new deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param obj_dep: a changed (but not saved) obj_dep object.
|
||||
:returns: created obj_dep object.
|
||||
"""
|
||||
obj_dep.create(context)
|
||||
return obj_dep
|
||||
|
||||
def deployable_update(self, context, obj_dep):
|
||||
"""Update a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param obj_dep: a deployable object to update.
|
||||
:returns: updated deployable object.
|
||||
"""
|
||||
obj_dep.save(context)
|
||||
return obj_dep
|
||||
|
||||
def deployable_delete(self, context, obj_dep):
|
||||
"""Delete a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param obj_dep: a deployable object to delete.
|
||||
"""
|
||||
obj_dep.destroy(context)
|
||||
|
||||
def deployable_get(self, context, uuid):
|
||||
"""Retrieve a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param uuid: UUID of a deployable.
|
||||
:returns: requested deployable object.
|
||||
"""
|
||||
return objects.Deployable.get(context, uuid)
|
||||
|
||||
def deployable_get_by_host(self, context, host):
|
||||
"""Retrieve a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param host: host on which the deployable is located.
|
||||
:returns: requested deployable object.
|
||||
"""
|
||||
return objects.Deployable.get_by_host(context, host)
|
||||
|
||||
def deployable_list(self, context):
|
||||
"""Retrieve a list of deployables.
|
||||
|
||||
:param context: request context.
|
||||
:returns: a list of deployable objects.
|
||||
"""
|
||||
return objects.Deployable.list(context)
|
||||
|
|
|
@ -75,3 +75,61 @@ class ConductorAPI(object):
|
|||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
cctxt.call(context, 'accelerator_delete', obj_acc=obj_acc)
|
||||
|
||||
def deployable_create(self, context, obj_dep):
|
||||
"""Signal to conductor service to create a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param obj_dep: a created (but not saved) deployable object.
|
||||
:returns: created deployable object.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'deployable_create', obj_dep=obj_dep)
|
||||
|
||||
def deployable_update(self, context, obj_dep):
|
||||
"""Signal to conductor service to update a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param obj_dep: a deployable object to update.
|
||||
:returns: updated deployable object.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'deployable_update', obj_dep=obj_dep)
|
||||
|
||||
def deployable_delete(self, context, obj_dep):
|
||||
"""Signal to conductor service to delete a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param obj_dep: a deployable object to delete.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
cctxt.call(context, 'deployable_delete', obj_dep=obj_dep)
|
||||
|
||||
def deployable_get(self, context, uuid):
|
||||
"""Signal to conductor service to get a deployable.
|
||||
|
||||
:param context: request context.
|
||||
:param uuid: UUID of a deployable.
|
||||
:returns: requested deployable object.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'deployable_get', uuid=uuid)
|
||||
|
||||
def deployable_get_by_host(self, context, host):
|
||||
"""Signal to conductor service to get a deployable by host.
|
||||
|
||||
:param context: request context.
|
||||
:param host: host on which the deployable is located.
|
||||
:returns: requested deployable object.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'deployable_get_by_host', host=host)
|
||||
|
||||
def deployable_list(self, context):
|
||||
"""Signal to conductor service to get a list of deployables.
|
||||
|
||||
:param context: request context.
|
||||
:returns: a list of deployable objects.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'deployable_list')
|
||||
|
|
|
@ -60,5 +60,30 @@ class Connection(object):
|
|||
"""Update an accelerator."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def accelerator_destroy(self, context, uuid):
|
||||
def accelerator_delete(self, context, uuid):
|
||||
"""Delete an accelerator."""
|
||||
|
||||
# deployable
|
||||
@abc.abstractmethod
|
||||
def deployable_create(self, context, values):
|
||||
"""Create a new deployable."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def deployable_get(self, context, uuid):
|
||||
"""Get requested deployable."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def deployable_get_by_host(self, context, host):
|
||||
"""Get requested deployable by host."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def deployable_list(self, context):
|
||||
"""Get requested list of deployables."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def deployable_update(self, context, uuid, values):
|
||||
"""Update a deployable."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def deployable_delete(self, context, uuid):
|
||||
"""Delete a deployable."""
|
||||
|
|
|
@ -46,6 +46,35 @@ def upgrade():
|
|||
sa.Column('product_id', sa.Text(), nullable=False),
|
||||
sa.Column('remotable', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('uuid', name='uniq_accelerators0uuid'),
|
||||
mysql_ENGINE='InnoDB',
|
||||
mysql_DEFAULT_CHARSET='UTF8'
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
'deployables',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('uuid', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('parent_uuid', sa.String(length=36),
|
||||
sa.ForeignKey('deployables.uuid'), nullable=True),
|
||||
sa.Column('root_uuid', sa.String(length=36),
|
||||
sa.ForeignKey('deployables.uuid'), nullable=True),
|
||||
sa.Column('pcie_address', sa.Text(), nullable=False),
|
||||
sa.Column('host', sa.Text(), nullable=False),
|
||||
sa.Column('board', sa.Text(), nullable=False),
|
||||
sa.Column('vendor', sa.Text(), nullable=False),
|
||||
sa.Column('version', sa.Text(), nullable=False),
|
||||
sa.Column('type', sa.Text(), nullable=False),
|
||||
sa.Column('assignable', sa.Boolean(), nullable=False),
|
||||
sa.Column('instance_uuid', sa.String(length=36), nullable=True),
|
||||
sa.Column('availability', sa.Text(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('uuid', name='uniq_deployables0uuid'),
|
||||
sa.Index('deployables_parent_uuid_idx', 'parent_uuid'),
|
||||
sa.Index('deployables_root_uuid_idx', 'root_uuid'),
|
||||
mysql_ENGINE='InnoDB',
|
||||
mysql_DEFAULT_CHARSET='UTF8'
|
||||
)
|
||||
|
|
|
@ -153,7 +153,7 @@ class Connection(api.Connection):
|
|||
return self._do_update_accelerator(context, uuid, values)
|
||||
except db_exc.DBDuplicateEntry as e:
|
||||
if 'name' in e.columns:
|
||||
raise exception.DuplicateName(name=values['name'])
|
||||
raise exception.DuplicateAcceleratorName(name=values['name'])
|
||||
|
||||
@oslo_db_api.retry_on_deadlock
|
||||
def _do_update_accelerator(self, context, uuid, values):
|
||||
|
@ -169,10 +169,78 @@ class Connection(api.Connection):
|
|||
return ref
|
||||
|
||||
@oslo_db_api.retry_on_deadlock
|
||||
def accelerator_destroy(self, context, uuid):
|
||||
def accelerator_delete(self, context, uuid):
|
||||
with _session_for_write():
|
||||
query = model_query(context, models.Accelerator)
|
||||
query = add_identity_filter(query, uuid)
|
||||
count = query.delete()
|
||||
if count != 1:
|
||||
raise exception.AcceleratorNotFound(uuid=uuid)
|
||||
|
||||
def deployable_create(self, context, values):
|
||||
if not values.get('uuid'):
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
|
||||
deployable = models.Deployable()
|
||||
deployable.update(values)
|
||||
|
||||
with _session_for_write() as session:
|
||||
try:
|
||||
session.add(deployable)
|
||||
session.flush()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.DeployableAlreadyExists(uuid=values['uuid'])
|
||||
return deployable
|
||||
|
||||
def deployable_get(self, context, uuid):
|
||||
query = model_query(
|
||||
context,
|
||||
models.Deployable).filter_by(uuid=uuid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.DeployableNotFound(uuid=uuid)
|
||||
|
||||
def deployable_get_by_host(self, context, host):
|
||||
query = model_query(
|
||||
context,
|
||||
models.Deployable).filter_by(host=host)
|
||||
return query.all()
|
||||
|
||||
def deployable_list(self, context):
|
||||
query = model_query(context, models.Deployable)
|
||||
return query.all()
|
||||
|
||||
def deployable_update(self, context, uuid, values):
|
||||
if 'uuid' in values:
|
||||
msg = _("Cannot overwrite UUID for an existing Deployable.")
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
|
||||
try:
|
||||
return self._do_update_deployable(context, uuid, values)
|
||||
except db_exc.DBDuplicateEntry as e:
|
||||
if 'name' in e.columns:
|
||||
raise exception.DuplicateDeployableName(name=values['name'])
|
||||
|
||||
@oslo_db_api.retry_on_deadlock
|
||||
def _do_update_deployable(self, context, uuid, values):
|
||||
with _session_for_write():
|
||||
query = model_query(context, models.Deployable)
|
||||
query = add_identity_filter(query, uuid)
|
||||
try:
|
||||
ref = query.with_lockmode('update').one()
|
||||
except NoResultFound:
|
||||
raise exception.DeployableNotFound(uuid=uuid)
|
||||
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
@oslo_db_api.retry_on_deadlock
|
||||
def deployable_delete(self, context, uuid):
|
||||
with _session_for_write():
|
||||
query = model_query(context, models.Deployable)
|
||||
query = add_identity_filter(query, uuid)
|
||||
query.update({'root_uuid': None})
|
||||
count = query.delete()
|
||||
if count != 1:
|
||||
raise exception.DeployableNotFound(uuid=uuid)
|
||||
|
|
|
@ -19,7 +19,7 @@ from oslo_db import options as db_options
|
|||
from oslo_db.sqlalchemy import models
|
||||
import six.moves.urllib.parse as urlparse
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Column, String, Integer
|
||||
from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Index
|
||||
from sqlalchemy import schema
|
||||
|
||||
from cyborg.common import paths
|
||||
|
@ -72,3 +72,32 @@ class Accelerator(Base):
|
|||
vendor_id = Column(String(255), nullable=False)
|
||||
product_id = Column(String(255), nullable=False)
|
||||
remotable = Column(Integer, nullable=False)
|
||||
|
||||
|
||||
class Deployable(Base):
|
||||
"""Represents the deployables."""
|
||||
|
||||
__tablename__ = 'deployables'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint('uuid', name='uniq_deployables0uuid'),
|
||||
Index('deployables_parent_uuid_idx', 'parent_uuid'),
|
||||
Index('deployables_root_uuid_idx', 'root_uuid'),
|
||||
table_args()
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
name = Column(String(255), nullable=False)
|
||||
parent_uuid = Column(String(36),
|
||||
ForeignKey('deployables.uuid'), nullable=True)
|
||||
root_uuid = Column(String(36),
|
||||
ForeignKey('deployables.uuid'), nullable=True)
|
||||
pcie_address = Column(String(255), nullable=False)
|
||||
host = Column(String(255), nullable=False)
|
||||
board = Column(String(255), nullable=False)
|
||||
vendor = Column(String(255), nullable=False)
|
||||
version = Column(String(255), nullable=False)
|
||||
type = Column(String(255), nullable=False)
|
||||
assignable = Column(Boolean, nullable=False)
|
||||
instance_uuid = Column(String(36), nullable=True)
|
||||
availability = Column(String(255), nullable=False)
|
||||
|
|
|
@ -26,3 +26,4 @@ def register_all():
|
|||
# function in order for it to be registered by services that may
|
||||
# need to receive it via RPC.
|
||||
__import__('cyborg.objects.accelerator')
|
||||
__import__('cyborg.objects.deployable')
|
||||
|
|
|
@ -73,5 +73,5 @@ class Accelerator(base.CyborgObject, object_base.VersionedObjectDictCompat):
|
|||
|
||||
def destroy(self, context):
|
||||
"""Delete the Accelerator from the DB."""
|
||||
self.dbapi.accelerator_destroy(context, self.uuid)
|
||||
self.dbapi.accelerator_delete(context, self.uuid)
|
||||
self.obj_reset_changes()
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
# 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.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_versionedobjects import base as object_base
|
||||
|
||||
from cyborg.common import exception
|
||||
from cyborg.db import api as dbapi
|
||||
from cyborg.objects import base
|
||||
from cyborg.objects import fields as object_fields
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@base.CyborgObjectRegistry.register
|
||||
class Deployable(base.CyborgObject, object_base.VersionedObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
dbapi = dbapi.get_instance()
|
||||
|
||||
fields = {
|
||||
'uuid': object_fields.UUIDField(nullable=False),
|
||||
'name': object_fields.StringField(nullable=False),
|
||||
'parent_uuid': object_fields.UUIDField(nullable=True),
|
||||
'root_uuid': object_fields.UUIDField(nullable=True),
|
||||
'pcie_address': object_fields.StringField(nullable=False),
|
||||
'host': object_fields.StringField(nullable=False),
|
||||
'board': object_fields.StringField(nullable=False),
|
||||
'vendor': object_fields.StringField(nullable=False),
|
||||
'version': object_fields.StringField(nullable=False),
|
||||
'type': object_fields.StringField(nullable=False),
|
||||
'assignable': object_fields.BooleanField(nullable=False),
|
||||
'instance_uuid': object_fields.UUIDField(nullable=True),
|
||||
'availability': object_fields.StringField(nullable=False),
|
||||
}
|
||||
|
||||
def _get_parent_root_uuid(self):
|
||||
obj_dep = Deployable.get(None, self.parent_uuid)
|
||||
return obj_dep.root_uuid
|
||||
|
||||
def create(self, context):
|
||||
"""Create a Deployable record in the DB."""
|
||||
if 'uuid' not in self:
|
||||
raise exception.ObjectActionError(action='create',
|
||||
reason='uuid is required')
|
||||
|
||||
if self.parent_uuid is None:
|
||||
self.root_uuid = self.uuid
|
||||
else:
|
||||
self.root_uuid = self._get_parent_root_uuid()
|
||||
|
||||
values = self.obj_get_changes()
|
||||
db_dep = self.dbapi.deployable_create(context, values)
|
||||
self._from_db_object(self, db_dep)
|
||||
|
||||
@classmethod
|
||||
def get(cls, context, uuid):
|
||||
"""Find a DB Deployable and return an Obj Deployable."""
|
||||
db_dep = cls.dbapi.deployable_get(context, uuid)
|
||||
obj_dep = cls._from_db_object(cls(context), db_dep)
|
||||
return obj_dep
|
||||
|
||||
@classmethod
|
||||
def get_by_host(cls, context, host):
|
||||
"""Get a Deployable by host."""
|
||||
db_deps = cls.dbapi.deployable_get_by_host(context, host)
|
||||
return cls._from_db_object_list(db_deps, context)
|
||||
|
||||
@classmethod
|
||||
def list(cls, context):
|
||||
"""Return a list of Deployable objects."""
|
||||
db_deps = cls.dbapi.deployable_list(context)
|
||||
return cls._from_db_object_list(db_deps, context)
|
||||
|
||||
def save(self, context):
|
||||
"""Update a Deployable record in the DB."""
|
||||
updates = self.obj_get_changes()
|
||||
db_dep = self.dbapi.deployable_update(context, self.uuid, updates)
|
||||
self._from_db_object(self, db_dep)
|
||||
|
||||
def destroy(self, context):
|
||||
"""Delete a Deployable from the DB."""
|
||||
self.dbapi.deployable_delete(context, self.uuid)
|
||||
self.obj_reset_changes()
|
|
@ -16,17 +16,9 @@
|
|||
from oslo_versionedobjects import fields as object_fields
|
||||
|
||||
|
||||
class IntegerField(object_fields.IntegerField):
|
||||
pass
|
||||
|
||||
|
||||
class UUIDField(object_fields.UUIDField):
|
||||
pass
|
||||
|
||||
|
||||
class StringField(object_fields.StringField):
|
||||
pass
|
||||
|
||||
|
||||
class DateTimeField(object_fields.DateTimeField):
|
||||
pass
|
||||
# Import fields from oslo_versionedobjects
|
||||
IntegerField = object_fields.IntegerField
|
||||
UUIDField = object_fields.UUIDField
|
||||
StringField = object_fields.StringField
|
||||
DateTimeField = object_fields.DateTimeField
|
||||
BooleanField = object_fields.BooleanField
|
||||
|
|
Loading…
Reference in New Issue