From 0cbced27fedb1f918230afd04341fbc33a8cbf06 Mon Sep 17 00:00:00 2001 From: Coco <419546439@qq.com> Date: Wed, 6 Mar 2019 19:51:32 -0500 Subject: [PATCH] Add driver-side OVO functions. 1. Remove hostname field in DriverDevice, because agent will set hostname together for drivers. 2. Add in_use field in DriverAttachHandle. 3. Add functions like list,create,destory for driver ovos. 4. Fix bugs of objects. 5. Remove deployable name global unique constrains. Change-Id: I6a646a38e7c1b7042bc27465e1d912e7ff643651 --- .../versions/ede4e3f1a232_new_db_schema.py | 6 +- cyborg/db/sqlalchemy/api.py | 25 ++--- cyborg/db/sqlalchemy/models.py | 6 +- cyborg/objects/attach_handle.py | 21 +++- cyborg/objects/attribute.py | 10 ++ cyborg/objects/control_path.py | 23 ++++- cyborg/objects/deployable.py | 26 +++-- cyborg/objects/device.py | 11 ++- cyborg/objects/device_profile.py | 2 +- .../driver_objects/driver_attach_handle.py | 36 +++++++ .../driver_objects/driver_attribute.py | 28 ++++++ .../driver_objects/driver_controlpath_id.py | 31 +++++- .../driver_objects/driver_deployable.py | 64 +++++++++++++ .../objects/driver_objects/driver_device.py | 96 ++++++++++++++++++- 14 files changed, 349 insertions(+), 36 deletions(-) diff --git a/cyborg/db/sqlalchemy/alembic/versions/ede4e3f1a232_new_db_schema.py b/cyborg/db/sqlalchemy/alembic/versions/ede4e3f1a232_new_db_schema.py index 22ee422b..90ac458d 100644 --- a/cyborg/db/sqlalchemy/alembic/versions/ede4e3f1a232_new_db_schema.py +++ b/cyborg/db/sqlalchemy/alembic/versions/ede4e3f1a232_new_db_schema.py @@ -50,7 +50,7 @@ def upgrade(): 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('uuid', sa.String(length=36), nullable=False, unique=True), sa.Column('parent_id', sa.Integer(), sa.ForeignKey('deployables.id', ondelete='CASCADE'), nullable=True), @@ -63,8 +63,6 @@ def upgrade(): sa.ForeignKey('devices.id', ondelete="RESTRICT"), nullable=False), sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('uuid', 'name', - name='uniq_deployables0uuid0name'), sa.Index('deployables_parent_id_idx', 'parent_id'), sa.Index('deployables_root_id_idx', 'root_id'), sa.Index('deployables_device_id_idx', 'device_id'), @@ -93,6 +91,7 @@ def upgrade(): 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, unique=True), sa.Column('device_id', sa.Integer(), sa.ForeignKey('devices.id', ondelete="RESTRICT"), nullable=False, index=True), @@ -108,6 +107,7 @@ def upgrade(): 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, unique=True), sa.Column('deployable_id', sa.Integer(), sa.ForeignKey('deployables.id', ondelete="RESTRICT"), nullable=False), diff --git a/cyborg/db/sqlalchemy/api.py b/cyborg/db/sqlalchemy/api.py index c81f83a2..6bc36ea9 100644 --- a/cyborg/db/sqlalchemy/api.py +++ b/cyborg/db/sqlalchemy/api.py @@ -15,8 +15,8 @@ """SQLAlchemy storage backend.""" -import copy import threading +import copy import uuid from oslo_db import api as oslo_db_api @@ -27,16 +27,17 @@ from oslo_log import log from oslo_utils import strutils from oslo_utils import timeutils from oslo_utils import uuidutils -from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm import load_only +from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.sql import func from cyborg.common import exception from cyborg.common.i18n import _ from cyborg.db import api from cyborg.db.sqlalchemy import models -from sqlalchemy import and_ from sqlalchemy import or_ +from sqlalchemy import and_ _CONTEXT = threading.local() LOG = log.getLogger(__name__) @@ -105,8 +106,7 @@ def add_identity_filter(query, value): raise exception.InvalidIdentity(identity=value) -def _paginate_query(context, model, limit=None, marker=None, sort_key=None, - sort_dir=None, query=None): +def _paginate_query(context, model, limit, marker, sort_key, sort_dir, query): sort_keys = ['id'] if sort_key and sort_key not in sort_keys: sort_keys.insert(0, sort_key) @@ -225,7 +225,7 @@ class Connection(api.Connection): def attach_handle_list(self, context): query = model_query(context, models.AttachHandle) - return _paginate_query(context, models.AttachHandle, query=query) + return _paginate_query(context, models.AttachHandle) def attach_handle_update(self, context, uuid, values): if 'uuid' in values: @@ -290,10 +290,11 @@ class Connection(api.Connection): if limit == 0: return [] - query_prefix = model_query(context, models.AttachHandle) + query_prefix = model_query(context, models.ControlpathID) filters = copy.deepcopy(filters) - exact_match_filter_names = ['uuid', 'id', 'device_id'] + exact_match_filter_names = ['uuid', 'id', 'device_id', 'cpid_info', + 'cpid_type'] # Filter the query query_prefix = self._exact_filter(models.ControlpathID, query_prefix, @@ -305,7 +306,7 @@ class Connection(api.Connection): def control_path_list(self, context): query = model_query(context, models.ControlpathID) - return _paginate_query(context, models.ControlpathID, query=query) + return _paginate_query(context, models.ControlpathID) def control_path_update(self, context, uuid, values): if 'uuid' in values: @@ -383,7 +384,7 @@ class Connection(api.Connection): def device_list(self, context): query = model_query(context, models.Device) - return _paginate_query(context, models.Device, query=query) + return _paginate_query(context, models.Device) def device_update(self, context, uuid, values): if 'uuid' in values: @@ -472,7 +473,7 @@ class Connection(api.Connection): def device_profile_list(self, context): query = model_query(context, models.DeviceProfile) - return _paginate_query(context, models.DeviceProfile, query=query) + return _paginate_query(context, models.DeviceProfile) def device_profile_update(self, context, uuid, values): if 'uuid' in values: @@ -886,7 +887,7 @@ class Connection(api.Connection): def quota_reserve(self, context, resources, deltas, expire, until_refresh, max_age, project_id=None, is_allocated_reserve=False): - """Create reservation record in DB according to params""" + """ Create reservation record in DB according to params""" with _session_for_write() as session: if project_id is None: project_id = context.project_id diff --git a/cyborg/db/sqlalchemy/models.py b/cyborg/db/sqlalchemy/models.py index d5644838..3a50eeef 100644 --- a/cyborg/db/sqlalchemy/models.py +++ b/cyborg/db/sqlalchemy/models.py @@ -88,8 +88,6 @@ class Deployable(Base): __tablename__ = 'deployables' __table_args__ = ( - schema.UniqueConstraint('uuid', 'name', - name='uniq_deployables0uuid0name'), Index('deployables_parent_id_idx', 'parent_id'), Index('deployables_root_id_idx', 'root_id'), Index('deployables_device_id_idx', 'device_id'), @@ -97,7 +95,7 @@ class Deployable(Base): ) id = Column(Integer, primary_key=True) - uuid = Column(String(36), nullable=False) + uuid = Column(String(36), nullable=False, unique=True) parent_id = Column(Integer, ForeignKey('deployables.id'), nullable=True) root_id = Column(Integer, ForeignKey('deployables.id'), nullable=True) name = Column(String(255), nullable=False) @@ -125,6 +123,7 @@ class ControlpathID(Base): __tablename__ = 'controlpath_ids' id = Column(Integer, primary_key=True) + uuid = Column(String(36), nullable=False, unique=True) device_id = Column(Integer, ForeignKey('devices.id', ondelete="RESTRICT"), nullable=False, index=True) @@ -143,6 +142,7 @@ class AttachHandle(Base): ) id = Column(Integer, primary_key=True) + uuid = Column(String(36), nullable=False, unique=True) deployable_id = Column(Integer, ForeignKey('deployables.id', ondelete="RESTRICT"), nullable=False) diff --git a/cyborg/objects/attach_handle.py b/cyborg/objects/attach_handle.py index 5fd9f052..876dddfa 100644 --- a/cyborg/objects/attach_handle.py +++ b/cyborg/objects/attach_handle.py @@ -41,12 +41,11 @@ class AttachHandle(base.CyborgObject, object_base.VersionedObjectDictCompat): nullable=False), # attach_info should be JSON here. 'attach_info': object_fields.StringField(nullable=False), - 'in_use': object_fields.BooleanField(nullable=False) + 'in_use': object_fields.BooleanField(nullable=False, default=False) } def create(self, context): """Create a AttachHandle record in the DB.""" - self.in_use = False values = self.obj_get_changes() db_ah = self.dbapi.attach_handle_create(context, values) self._from_db_object(self, db_ah) @@ -70,7 +69,7 @@ class AttachHandle(base.CyborgObject, object_base.VersionedObjectDictCompat): """Return a list of AttachHandle objects.""" if filters: sort_dir = filters.pop('sort_dir', 'desc') - sort_key = filters.pop('sort_key', 'create_at') + sort_key = filters.pop('sort_key', 'created_at') limit = filters.pop('limit', None) marker = filters.pop('marker_obj', None) db_ahs = cls.dbapi.attach_handle_get_by_filters(context, filters, @@ -93,3 +92,19 @@ class AttachHandle(base.CyborgObject, object_base.VersionedObjectDictCompat): """Delete a AttachHandle from the DB.""" self.dbapi.attach_handle_delete(context, self.uuid) self.obj_reset_changes() + + @classmethod + def get_ah_list_by_deployable_id(cls, context, deployable_id): + ah_filter = {'deployable_id': deployable_id} + ah_obj_list = AttachHandle.list(context, ah_filter) + return ah_obj_list + + @classmethod + def get_ah_by_depid_attachinfo(cls, context, deployable_id, attach_info): + ah_filter = {'deployable_id': deployable_id, + 'attach_info': attach_info} + ah_obj_list = AttachHandle.list(context, ah_filter) + if len(ah_obj_list) != 0: + return ah_obj_list[0] + else: + return None diff --git a/cyborg/objects/attribute.py b/cyborg/objects/attribute.py index e6d306ba..ee808e9a 100644 --- a/cyborg/objects/attribute.py +++ b/cyborg/objects/attribute.py @@ -87,3 +87,13 @@ class Attribute(base.CyborgObject, object_base.VersionedObjectDictCompat): def set_key_value_pair(self, set_key, set_value): self.key = set_key self.value = set_value + + @classmethod + def get_by_dep_key(cls, context, dep_id, key): + """Get the only one attribute with deployable_id and the key.""" + attr_filter = {"deployable_id": dep_id, "key": key} + attr_list = cls.get_by_filter(context, attr_filter) + if len(attr_list) != 0: + return attr_list[0] + else: + return None diff --git a/cyborg/objects/control_path.py b/cyborg/objects/control_path.py index be1d3469..7e3d80f1 100644 --- a/cyborg/objects/control_path.py +++ b/cyborg/objects/control_path.py @@ -59,7 +59,7 @@ class ControlpathID(base.CyborgObject, object_base.VersionedObjectDictCompat): """Return a list of ControlpathID objects.""" if filters: sort_dir = filters.pop('sort_dir', 'desc') - sort_key = filters.pop('sort_key', 'create_at') + sort_key = filters.pop('sort_key', 'created_at') limit = filters.pop('limit', None) marker = filters.pop('marker_obj', None) db_cps = cls.dbapi.control_path_get_by_filters(context, filters, @@ -82,3 +82,24 @@ class ControlpathID(base.CyborgObject, object_base.VersionedObjectDictCompat): """Delete a ControlpathID from the DB.""" self.dbapi.control_path_delete(context, self.uuid) self.obj_reset_changes() + + @classmethod + def get_by_device_id(cls, context, device_id): + # control_path is unique for one device. + cpid_filter = {'device_id': device_id} + cpid_obj_list = ControlpathID.list(context, cpid_filter) + if len(cpid_obj_list) != 0: + return cpid_obj_list[0] + else: + return None + + @classmethod + def get_by_device_id_cpidinfo(cls, context, device_id, cpid_info): + cpid_filter = {'device_id': device_id, + 'cpid_info': cpid_info} + # the list could have one value or is empty. + cpid_obj_list = ControlpathID.list(context, cpid_filter) + if len(cpid_obj_list) != 0: + return cpid_obj_list[0] + else: + return None diff --git a/cyborg/objects/deployable.py b/cyborg/objects/deployable.py index 5ee45544..a5e881eb 100644 --- a/cyborg/objects/deployable.py +++ b/cyborg/objects/deployable.py @@ -55,13 +55,10 @@ class Deployable(base.CyborgObject, object_base.VersionedObjectDictCompat): def create(self, context): """Create a Deployable record in the DB.""" - if not hasattr(self, 'parent_id') or self.parent_id is None: - self.root_id = self.id - else: - self.root_id = self._get_parent_root_id(context) + # FIXME: Add parent_uuid and root_uuid constrains when DB change to + # parent_uuid & root_uuid values = self.obj_get_changes() - db_dep = self.dbapi.deployable_create(context, values) self._from_db_object(self, db_dep) self.obj_reset_changes() @@ -72,7 +69,7 @@ class Deployable(base.CyborgObject, object_base.VersionedObjectDictCompat): """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) - # retrieve all the attrobutes for this deployable + # retrieve all the attributes for this deployable if with_attribute_list: query = {"deployable_id": obj_dep.id} attr_get_list = Attribute.get_by_filter(context, @@ -95,7 +92,7 @@ class Deployable(base.CyborgObject, object_base.VersionedObjectDictCompat): """Return a list of Deployable objects.""" if filters: sort_dir = filters.pop('sort_dir', 'desc') - sort_key = filters.pop('sort_key', 'create_at') + sort_key = filters.pop('sort_key', 'created_at') limit = filters.pop('limit', None) marker = filters.pop('marker_obj', None) db_deps = cls.dbapi.deployable_get_by_filters(context, filters, @@ -199,3 +196,18 @@ class Deployable(base.CyborgObject, object_base.VersionedObjectDictCompat): obj.attributes_list = [] return obj + + @classmethod + def get_list_by_device_id(cls, context, device_id): + dep_filter = {'device_id': device_id} + dep_obj_list = Deployable.list(context, dep_filter) + return dep_obj_list + + @classmethod + def get_by_name_deviceid(cls, context, name, device_id): + dep_filter = {'name': name, 'device_id': device_id} + dep_obj_list = Deployable.list(context, dep_filter) + if len(dep_obj_list) != 0: + return dep_obj_list[0] + else: + return None diff --git a/cyborg/objects/device.py b/cyborg/objects/device.py index 012f9114..3d47e398 100644 --- a/cyborg/objects/device.py +++ b/cyborg/objects/device.py @@ -19,6 +19,7 @@ from oslo_versionedobjects import base as object_base from cyborg.db import api as dbapi from cyborg.objects import base from cyborg.objects import fields as object_fields +from cyborg.objects.control_path import ControlpathID LOG = logging.getLogger(__name__) @@ -63,7 +64,7 @@ class Device(base.CyborgObject, object_base.VersionedObjectDictCompat): """Return a list of Device objects.""" if filters: sort_dir = filters.pop('sort_dir', 'desc') - sort_key = filters.pop('sort_key', 'create_at') + sort_key = filters.pop('sort_key', 'created_at') limit = filters.pop('limit', None) marker = filters.pop('marker_obj', None) db_devices = cls.dbapi.device_list_by_filters(context, filters, @@ -85,3 +86,11 @@ class Device(base.CyborgObject, object_base.VersionedObjectDictCompat): """Delete the Device from the DB.""" self.dbapi.device_delete(context, self.uuid) self.obj_reset_changes() + + @classmethod + def get_list_by_hostname(cls, context, hostname): + """get device object list from the hostname. return [] if not + exist.""" + dev_filter = {'hostname': hostname} + device_obj_list = Device.list(context, dev_filter) + return device_obj_list diff --git a/cyborg/objects/device_profile.py b/cyborg/objects/device_profile.py index 702c77d8..ac51483c 100644 --- a/cyborg/objects/device_profile.py +++ b/cyborg/objects/device_profile.py @@ -57,7 +57,7 @@ class DeviceProfile(base.CyborgObject, object_base.VersionedObjectDictCompat): """Return a list of Device_profile objects.""" if filters: sort_dir = filters.pop('sort_dir', 'desc') - sort_key = filters.pop('sort_key', 'create_at') + sort_key = filters.pop('sort_key', 'created_at') limit = filters.pop('limit', None) marker = filters.pop('marker_obj', None) db_device_profiles = cls.dbapi.device_profile_list_by_filters( diff --git a/cyborg/objects/driver_objects/driver_attach_handle.py b/cyborg/objects/driver_objects/driver_attach_handle.py index 4bd021a4..f069a5a8 100644 --- a/cyborg/objects/driver_objects/driver_attach_handle.py +++ b/cyborg/objects/driver_objects/driver_attach_handle.py @@ -16,6 +16,9 @@ from oslo_versionedobjects import base as object_base from cyborg.objects import fields as object_fields from cyborg.objects import base +from cyborg.objects.attach_handle import AttachHandle +from oslo_log import log as logging +LOG = logging.getLogger(__name__) @base.CyborgObjectRegistry.register @@ -31,3 +34,36 @@ class DriverAttachHandle(base.DriverObjectBase, # The status of attach_handle, is in use or not. 'in_use': object_fields.BooleanField(nullable=False, default=False) } + + def create(self, context, deployable_id, cpid_id): + """Create a driver-side AttachHandle object, call AttachHandle + Object to store in DB.""" + attach_handle_obj = AttachHandle(context=context, + deployable_id=deployable_id, + cpid_id=cpid_id, + attach_type=self.attach_type, + attach_info=self.attach_info, + in_use=self.in_use + ) + attach_handle_obj.create(context) + + def destroy(self, context, deployable_id): + ah_obj = AttachHandle.get_ah_by_depid_attachinfo(context, + deployable_id, + self.attach_info) + if ah_obj is not None: + ah_obj.destroy(context) + + @classmethod + def list(cls, context, deployable_id): + """Form a driver-side attach_handle list for one deployable.""" + ah_obj_list = AttachHandle.get_ah_list_by_deployable_id( + context, deployable_id) + driver_ah_obj_list = [] + for ah_obj in ah_obj_list: + driver_ah_obj = cls(context=context, + attach_type=ah_obj.attach_type, + attach_info=ah_obj.attach_info, + in_use=ah_obj.in_use) + driver_ah_obj_list.append(driver_ah_obj) + return driver_ah_obj_list diff --git a/cyborg/objects/driver_objects/driver_attribute.py b/cyborg/objects/driver_objects/driver_attribute.py index 7def8ed3..3981c856 100644 --- a/cyborg/objects/driver_objects/driver_attribute.py +++ b/cyborg/objects/driver_objects/driver_attribute.py @@ -16,6 +16,7 @@ from oslo_versionedobjects import base as object_base from cyborg.objects import base from cyborg.objects import fields as object_fields +from cyborg.objects.attribute import Attribute @base.CyborgObjectRegistry.register @@ -28,3 +29,30 @@ class DriverAttribute(base.DriverObjectBase, 'key': object_fields.StringField(nullable=False), 'value': object_fields.StringField(nullable=False) } + + def create(self, context, deployable_id): + """Convert driver-side Attribute into Attribute Object so as to + store in DB.""" + attr_obj = Attribute() + attr_obj.deployable_id = deployable_id + attr_obj.set_key_value_pair(self.key, self.value) + attr_obj.create(context) + + @classmethod + def destroy(cls, context, deployable_id): + """Delete driver-side attribute list from the DB.""" + attr_obj_list = Attribute.get_by_deployable_id(context, deployable_id) + for attr_obj in attr_obj_list: + attr_obj.destroy(context) + + @classmethod + def list(cls, context, deployable_id): + """Form driver-side attribute list for one deployable.""" + attr_obj_list = Attribute.get_by_deployable_id(context, deployable_id) + driver_attr_obj_list = [] + for attr_obj in attr_obj_list: + driver_attr_obj = cls(context=context, + key=attr_obj.key, + value=attr_obj.value) + driver_attr_obj_list.append(driver_attr_obj) + return driver_attr_obj_list diff --git a/cyborg/objects/driver_objects/driver_controlpath_id.py b/cyborg/objects/driver_objects/driver_controlpath_id.py index ce2585da..8eee3477 100644 --- a/cyborg/objects/driver_objects/driver_controlpath_id.py +++ b/cyborg/objects/driver_objects/driver_controlpath_id.py @@ -16,6 +16,7 @@ from oslo_versionedobjects import base as object_base from cyborg.objects import fields as object_fields from cyborg.objects import base +from cyborg.objects.control_path import ControlpathID @base.CyborgObjectRegistry.register @@ -27,5 +28,33 @@ class DriverControlPathID(base.DriverObjectBase, fields = { 'cpid_type': object_fields.StringField(nullable=False), # PCI BDF, PowerVM device, etc. - 'cpid_info': object_fields.StringField(nullable=False), + 'cpid_info': object_fields.StringField(nullable=False) } + + def create(self, context, device_id): + """Create a driver-side ControlPathID for drivers. Call + ControlpathID object to store in DB.""" + cpid_obj = ControlpathID(context=context, + device_id=device_id, + cpid_type=self.cpid_type, + cpid_info=self.cpid_info) + cpid_obj.create(context) + return cpid_obj + + def destroy(self, context, device_id): + cpid_obj = ControlpathID.get_by_device_id_cpidinfo(context, + device_id, + self.cpid_info) + if cpid_obj is not None: + cpid_obj.destroy(context) + + @classmethod + def get(cls, context, device_id): + # return None when can't found any. + cpid_obj = ControlpathID.get_by_device_id(context, device_id) + driver_cpid_obj = None + if cpid_obj is not None: + driver_cpid_obj = cls(context=context, + cpid_type=cpid_obj.cpid_type, + cpid_info=cpid_obj.cpid_info) + return driver_cpid_obj diff --git a/cyborg/objects/driver_objects/driver_deployable.py b/cyborg/objects/driver_objects/driver_deployable.py index 3c621135..ba6d51be 100644 --- a/cyborg/objects/driver_objects/driver_deployable.py +++ b/cyborg/objects/driver_objects/driver_deployable.py @@ -19,6 +19,8 @@ from cyborg.objects import fields as object_fields from cyborg.objects.driver_objects.driver_attribute import DriverAttribute from cyborg.objects.driver_objects.driver_attach_handle import \ DriverAttachHandle +from cyborg.objects.deployable import Deployable +from cyborg.objects.attach_handle import AttachHandle @base.CyborgObjectRegistry.register @@ -37,3 +39,65 @@ class DriverDeployable(base.DriverObjectBase, 'attach_handle_list': object_fields.ListOfObjectsField( 'DriverAttachHandle', default=[], nullable=True) } + + def create(self, context, device_id, cpid_id): + """Create a driver-side Deployable object into DB. This object will be + stored in seperate db tables: deployable & attach_handle & + attribute table.""" + + # first store in deployable table through Deployable Object. + deployable_obj = Deployable(context=context, + name=self.name, + num_accelerators=self.num_accelerators, + device_id=device_id + ) + deployable_obj.create(context) + # create attribute_list for this deployable + if hasattr(self, 'attribute_list'): + for driver_attr in self.attribute_list: + driver_attr.create(context, deployable_obj.id) + + # create attach_handle_list for this deployable + if hasattr(self, 'attach_handle_list'): + for driver_attach_handle in self.attach_handle_list: + driver_attach_handle.create(context, deployable_obj.id, + cpid_id) + + def destroy(self, context, device_id): + """delete one driver-side deployable by calling existing Deployable + and AttachHandle Object. Use name&host to identify Deployable and + attach_info to identify the AttachHandle""" + + # get deployable_id by name, get only one value. + dep_obj = Deployable.get_by_name_deviceid(context, self.name, + device_id) + # delete attach_handle + if hasattr(self, 'attach_handle_list'): + for driver_ah_obj in self.attach_handle_list: + # get attach_handle_obj, exist and only one. + driver_ah_obj.destroy(context, dep_obj.id) + # delete attribute_list + if hasattr(self, 'attribute_list'): + DriverAttribute.destroy(context, dep_obj.id) + # delete dep_obj + if dep_obj is not None: + dep_obj.destroy(context) + + @classmethod + def list(cls, context, device_id): + """Form driver-side Deployable object list from DB for one device.""" + # get deployable_obj_list for one device_id + dep_obj_list = Deployable.get_list_by_device_id(context, device_id) + driver_dep_obj_list = [] + for dep_obj in dep_obj_list: + # get driver_ah_obj_list for this dep_obj + driver_ah_obj_list = DriverAttachHandle.list(context, dep_obj.id) + # get driver_attr_obj_list fro this dep_obj + driver_attr_obj_list = DriverAttribute.list(context, dep_obj.id) + driver_dep_obj = cls(context=context, + name=dep_obj.name, + num_accelerators=dep_obj.num_accelerators, + attribute_list=driver_attr_obj_list, + attach_handle_list=driver_ah_obj_list) + driver_dep_obj_list.append(driver_dep_obj) + return driver_dep_obj_list diff --git a/cyborg/objects/driver_objects/driver_device.py b/cyborg/objects/driver_objects/driver_device.py index 8372c7bb..66d880ca 100644 --- a/cyborg/objects/driver_objects/driver_device.py +++ b/cyborg/objects/driver_objects/driver_device.py @@ -19,6 +19,10 @@ from cyborg.objects import fields as object_fields from cyborg.objects.driver_objects.driver_deployable import DriverDeployable from cyborg.objects.driver_objects.driver_controlpath_id import \ DriverControlPathID +from cyborg.objects.device import Device +from cyborg.objects.deployable import Deployable +from cyborg.objects.control_path import ControlpathID +from cyborg.objects.attach_handle import AttachHandle @base.CyborgObjectRegistry.register @@ -28,15 +32,14 @@ class DriverDevice(base.DriverObjectBase, VERSION = '1.0' fields = { - # standard borad info: vendor_id, product_id, remotable? 'vendor': object_fields.StringField(nullable=False), 'model': object_fields.StringField(nullable=False), 'type': object_fields.DeviceTypeField(nullable=False), 'std_board_info': object_fields.StringField(nullable=True), - # vendor board info should be a dict: like acc_topology which is used - # for driver-specific resource provider. + # vendor board info should be a dict for driver-specific resource + # provider. 'vendor_board_info': object_fields.StringField(nullable=True), - 'hostname': object_fields.StringField(nullable=False), + # hostname will be set by the agent, so driver don't need to report. # Each controlpath_id corresponds to a different PF. For now # we are sticking with a single cpid. 'controlpath_id': object_fields.ObjectField('DriverControlPathID', @@ -45,3 +48,88 @@ class DriverDevice(base.DriverObjectBase, default=[], nullable=False) } + + def create(self, context, host): + """Create a driver-side Device Object into DB. This object will be + stored in many db tables: device, deployable, attach_handle, + controlpath_id etc. by calling related Object.""" + # first store in device table through Device Object. + + device_obj = Device(context=context, + type=self.type, + vendor=self.vendor, + model=self.model, + hostname=host + ) + if hasattr(self, 'std_board_info'): + device_obj.std_board_info = self.std_board_info + if hasattr(self, 'vendor_board_info'): + device_obj.vendor_board_info = self.vendor_board_info + device_obj.create(context) + + # for the controlpath_id, call driver_controlpath_id to create. + cpid_obj = self.controlpath_id.create(context, device_obj.id) + # for deployable_list, call internal layer object: driver_deployable + # to create. + for driver_deployable in self.deployable_list: + driver_deployable.create(context, device_obj.id, cpid_obj.id) + + def destroy(self, context, host): + """Delete a driver-side Device Object from db. This should + delete the internal layer objects.""" + # get dev_obj_list from hostname + device_obj = self.get_device_obj(context, host) + if hasattr(self.controlpath_id, 'cpid_info'): + cpid_obj = ControlpathID.get_by_device_id_cpidinfo( + context, device_obj.id, self.controlpath_id.cpid_info) + # delete controlpath_id + cpid_obj.destroy(context) + # delete deployable_list first. + for driver_deployable in self.deployable_list: + driver_deployable.destroy(context, device_obj.id) + # delete the device + device_obj.destroy(context) + + def get_device_obj(self, context, host): + """ + :param context: requested context. + :param host: hostname of the node. + :return: a device object of current driver device object. It will + return on value because it has controlpath_id. + """ + # get dev_obj_list from hostname + device_obj_list = Device.get_list_by_hostname(context, host) + # use controlpath_id.cpid_info to identiy one Device. + for device_obj in device_obj_list: + # get cpid_obj, could be empty or only one value. + cpid_obj = ControlpathID.get_by_device_id_cpidinfo( + context, device_obj.id, self.controlpath_id.cpid_info) + # find the one cpid_obj with cpid_info + if cpid_obj is not None: + return device_obj + + @classmethod + def list(cls, context, host): + """Form driver-side device object list from DB for one host. + A list may contains driver_device_object without controlpath_id.(In + the case some of controlpath_id can't store successfully but its + devices stores successfully. + )""" + # get dev_obj_list from hostname + dev_obj_list = Device.get_list_by_hostname(context, host) + driver_dev_obj_list = [] + for dev_obj in dev_obj_list: + cpid = DriverControlPathID.get(context, dev_obj.id) + # NOTE: will not return device without controlpath_id. + if cpid is not None: + driver_dev_obj = \ + cls(context=context, vendor=dev_obj.vendor, + model=dev_obj.model, type=dev_obj.type, + std_board_info=dev_obj.std_board_info, + vendor_board_info=dev_obj.vendor_board_info, + controlpath_id=cpid, + deployable_list=DriverDeployable.list(context, + dev_obj.id) + ) + driver_dev_obj_list.append(driver_dev_obj) + return driver_dev_obj_list