Пример #1
0
class ARQ(base.CyborgObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    dbapi = dbapi.get_instance()
    fields = {
        'id': object_fields.IntegerField(nullable=False),
        'uuid': object_fields.UUIDField(nullable=False),
        'state': object_fields.ARQStateField(nullable=False),
        'device_profile': object_fields.ObjectField('DeviceProfile',
                                                    nullable=True),
        'hostname': object_fields.StringField(nullable=True),
        'device_rp_uuid': object_fields.UUIDField(nullable=True),
        'device_instance_uuid': object_fields.UUIDField(nullable=True),
        'attach_handle': object_fields.ObjectField('AttachHandle',
                                                   nullable=True),
    }

    @staticmethod
    def _from_db_object(arq, db_extarq):
        """Converts an ARQ to a formal object.

        :param arq: An object of the class ARQ
        :param db_extarq: A DB model of the object
        :return: The object of the class with the database entity added
        """
        device_profile_id = db_extarq.pop('device_profile_id', None)
        attach_handle_id = db_extarq.pop('attach_handle_id', None)

        for field in arq.fields:
            # if field == 'device_profile':
            #     arq._load_device_profile(device_profile_id)
            # if field == 'attach_handle':
            #     arq._load_device_profile(attach_handle_id)
            arq[field] = db_extarq[field]

        arq.obj_reset_changes()
        return arq

    def _load_device_profile(self, device_profile_id):
        self.device_profile = objects.DeviceProfile.\
            get_by_id(self._context, device_profile_id)

    def _load_attach_handle(self, attach_handle_id):
        self.attach_handle = objects.AttachHandle.\
            get_by_id(self._context, attach_handle_id)
Пример #2
0
class MyObj(base.CyborgPersistentObject, base.CyborgObject,
            base.CyborgObjectDictCompat):
    VERSION = '1.6'
    fields = {
        'foo': fields.IntegerField(default=1),
        'bar': fields.StringField(),
        'missing': fields.StringField(),
        'readonly': fields.IntegerField(read_only=True),
        'rel_object': fields.ObjectField('MyOwnedObject', nullable=True),
        'rel_objects': fields.ListOfObjectsField('MyOwnedObject',
                                                 nullable=True),
        'mutable_default': fields.ListOfStringsField(default=[]),
    }

    @staticmethod
    def _from_db_object(context, obj, db_obj):
        self = MyObj()
        self.foo = db_obj['foo']
        self.bar = db_obj['bar']
        self.missing = db_obj['missing']
        self.readonly = 1
        self._context = context
        return self

    def obj_load_attr(self, attrname):
        setattr(self, attrname, 'loaded!')

    def query(cls, context):
        obj = cls(context=context, foo=1, bar='bar')
        obj.obj_reset_changes()
        return obj

    def marco(self):
        return 'polo'

    def _update_test(self):
        self.bar = 'updated'

    def save(self):
        self.obj_reset_changes()

    def refresh(self):
        self.foo = 321
        self.bar = 'refreshed'
        self.obj_reset_changes()

    def modify_save_modify(self):
        self.bar = 'meow'
        self.save()
        self.foo = 42
        self.rel_object = MyOwnedObject(baz=42)

    def obj_make_compatible(self, primitive, target_version):
        super(MyObj, self).obj_make_compatible(primitive, target_version)
        # NOTE(danms): Simulate an older version that had a different
        # format for the 'bar' attribute
        if target_version == '1.1' and 'bar' in primitive:
            primitive['bar'] = 'old%s' % primitive['bar']
Пример #3
0
class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat,
             utils.FactoryMixin, ExtARQJobMixin):
    """ExtARQ is a wrapper around ARQ with Cyborg-private fields.
       Each ExtARQ object contains exactly one ARQ object as a field.
       But, in the db layer, ExtARQ and ARQ are represented together
       as a row in a single table. Both share a single UUID.
       ExtARQ version is bumped up either if any of its fields change
       or if the ARQ version changes.
    """
    # Version 1.0: Initial version
    # 1.1: v2 API and Nova integration
    VERSION = '1.1'

    dbapi = dbapi.get_instance()

    fields = {
        'arq': object_fields.ObjectField('ARQ'),
        # Cyborg-private fields
        # Left substate open now, fill them out during design/implementation
        # later.
        'substate': object_fields.StringField(),
        'deployable_uuid': object_fields.UUIDField(nullable=True),

        # The dp group is copied in to the extarq, so that any changes or
        # deletions to the device profile do not affect running VMs.
        'device_profile_group':
        object_fields.DictOfStringsField(nullable=True),
        # For bound ARQs, we keep the attach handle ID here so that
        # it is easy to deallocate on unbind or delete.
        'attach_handle_id': object_fields.IntegerField(nullable=True),
    }

    def create(self, context, device_profile_id=None):
        """Create an ExtARQ record in the DB."""
        if 'device_profile_name' not in self.arq and not device_profile_id:
            raise exception.ObjectActionError(
                action='create',
                reason='Device profile name is required in ARQ')
        self.arq.state = constants.ARQ_INITIAL
        self.substate = constants.ARQ_INITIAL
        values = self.obj_get_changes()
        arq_obj = values.pop('arq', None)
        if arq_obj is not None:
            values.update(arq_obj.as_dict())

        # Pass devprof id to db layer, to avoid repeated queries
        if device_profile_id is not None:
            values['device_profile_id'] = device_profile_id

        db_extarq = self.dbapi.extarq_create(context, values)
        self._from_db_object(self, db_extarq, context)
        return self

    @classmethod
    def get(cls, context, uuid, lock=False):
        """Find a DB ExtARQ and return an Obj ExtARQ."""
        db_extarq = cls.dbapi.extarq_get(context, uuid)
        obj_arq = objects.ARQ(context)
        obj_extarq = cls(context)
        obj_extarq['arq'] = obj_arq
        obj_extarq = cls._from_db_object(obj_extarq, db_extarq, context)
        return obj_extarq

    @classmethod
    def list(cls, context, uuid_range=None):
        """Return a list of ExtARQ objects."""
        db_extarqs = cls.dbapi.extarq_list(context, uuid_range)
        obj_extarq_list = cls._from_db_object_list(db_extarqs, context)
        return obj_extarq_list

    def save(self, context):
        """Update an ExtARQ record in the DB."""
        updates = self.obj_get_changes()
        db_extarq = self.dbapi.extarq_update(context, self.arq.uuid, updates)
        self._from_db_object(self, db_extarq, context)

    def update_state(self, context, state, scope=None):
        """Update an ExtARQ state record in the DB."""
        updates = self.obj_get_changes()
        updates["state"] = state
        db_extarq = self.dbapi.extarq_update(context, self.arq.uuid, updates,
                                             scope)
        self._from_db_object(self, db_extarq, context)

    def update_check_state(self, context, state, scope=None):
        if self.arq.state == state:
            LOG.info("ExtARQ(%s) state is %s, no need to update",
                     self.arq.uuid, state)
            return False
        old = self.arq.state
        scope = scope or ARQ_STATES_TRANSFORM_MATRIX[state]
        self.update_state(context, state, scope)
        ea = ExtARQ.get(context, self.arq.uuid, lock=True)
        if not ea:
            raise exception.ResourceNotFound(resources='ExtARQ',
                                             msg="Can not find ExtARQ(%s)" %
                                             self.arq.uuid)
        current = ea.arq.state
        if state != current:
            msg = ("Failed to change ARQ state from %s to %s, the current "
                   "state is %s" % (old, state, current))
            LOG.error(msg)
            raise exception.ARQBadState(state=current,
                                        uuid=self.arq.uuid,
                                        expected=list(state))
        return True

    def destroy(self, context):
        """Delete an ExtARQ from the DB."""
        self.dbapi.extarq_delete(context, self.arq.uuid)
        self.obj_reset_changes()

    @classmethod
    def delete_by_uuid(cls, context, arq_uuid_list):
        """Delete a list of ARQs based on their UUIDs.

        This is not idempotent, i.e., if the first call to delete an
        ARQ has succeeded, second and later calls to delete the same ARQ
        will get errored out, but it will raise the exception only after
        all input arq being operated.
        """
        unexisted = []
        for uuid in arq_uuid_list:
            try:
                obj_extarq = objects.ExtARQ.get(context, uuid)
                # TODO() Defer deletion to conductor
                if obj_extarq.arq.state != constants.ARQ_INITIAL:
                    obj_extarq.unbind(context)
                obj_extarq.destroy(context)
            except exception.ResourceNotFound:
                unexisted.append(uuid)
                continue
        if unexisted:
            LOG.warning('There are unexisted arqs: %s', unexisted)
            raise exception.ResourceNotFound(resource='ARQ',
                                             msg='with uuids %s' % unexisted)

    @classmethod
    def delete_by_instance(cls, context, instance_uuid):
        """Delete all ARQs for given instance.

        This is idempotent, i.e., it would have the same effect if called
        repeatedly with the same instance UUID. In other words, it would
        not raise an error on the second and later attempts even if the
        first one has deleted the ARQs.
        """
        obj_extarqs = [
            extarq for extarq in objects.ExtARQ.list(context)
            if extarq.arq['instance_uuid'] == instance_uuid
        ]
        for obj_extarq in obj_extarqs:
            LOG.info('Deleting obj_extarq uuid %s for instance %s',
                     obj_extarq.arq['uuid'], obj_extarq.arq['instance_uuid'])
            obj_extarq.unbind(context)
            obj_extarq.destroy(context)

    def _get_glance_connection(self):
        default_user = '******'
        try:
            auth_user = CONF.image.username or default_user
        except Exception:
            auth_user = default_user
        return connection.Connection(cloud=auth_user)

    def _allocate_attach_handle(self, context, deployable):
        try:
            ah = AttachHandle.allocate(context, deployable.id)
            self.attach_handle_id = ah.id
        except Exception as e:
            LOG.error(
                "Failed to allocate attach handle for ARQ %s"
                "from deployable %s. Reason: %s", self.arq.uuid,
                deployable.uuid, str(e))
            # TODO(Shaohe) Rollback? We have _update_placement,
            # should cancel it.
            self.update_check_state(context, constants.ARQ_BIND_FAILED)
            raise
        LOG.info('Attach handle(%s) for ARQ(%s) successfully.', ah.uuid,
                 self.arq.uuid)

    def bind(self, context, deployable):
        self._allocate_attach_handle(context, deployable)
        # ARQ state changes get committed here
        self.update_check_state(context, constants.ARQ_BOUND)
        LOG.info('Update ARQ %s state to "Bound" successfully.', self.arq.uuid)
        # TODO(Shaohe) rollback self._unbind and self._delete
        # if (self.arq.state == constants.ARQ_DELETING
        #         or self.arq.state == ARQ_UNBOUND):

    def unbind(self, context):
        arq = self.arq
        arq.hostname = None
        arq.device_rp_uuid = None
        arq.instance_uuid = None
        arq.state = constants.ARQ_UNBOUND

        # Unbind: mark attach handles as freed
        ah_id = self.attach_handle_id
        if ah_id:
            attach_handle = AttachHandle.get_by_id(context, ah_id)
            attach_handle.deallocate(context)
        self.attach_handle_id = None
        self.save(context)

    @classmethod
    def _fill_obj_extarq_fields(cls, context, db_extarq):
        """ExtARQ object has some fields that are not present
           in db_extarq. We fill them out here.
        """
        # From the 2 fields in the ExtARQ, we obtain other fields.
        devprof_id = db_extarq['device_profile_id']
        devprof_group_id = db_extarq['device_profile_group_id']

        devprof = DeviceProfile.get_by_id(context, devprof_id)
        db_extarq['device_profile_name'] = devprof['name']

        db_extarq['attach_handle_type'] = ''
        db_extarq['attach_handle_info'] = ''
        if db_extarq['state'] == 'Bound':  # TODO() Do proper bind
            db_ah = cls.dbapi.attach_handle_get_by_id(
                context, db_extarq['attach_handle_id'])
            if db_ah is not None:
                db_extarq['attach_handle_type'] = db_ah['attach_type']
                db_extarq['attach_handle_info'] = db_ah['attach_info']
            else:
                raise exception.ResourceNotFound(resource='Attach Handle',
                                                 msg='with uuid=%s' %
                                                 db_extarq['attach_handle_id'])

        if db_extarq['deployable_id']:
            dep = objects.Deployable.get_by_id(context,
                                               db_extarq['deployable_id'])
            db_extarq['deployable_uuid'] = dep.uuid
        else:
            LOG.debug('Setting deployable UUID to zeroes for db_extarq %s',
                      db_extarq['uuid'])
            db_extarq['deployable_uuid'] = (
                '00000000-0000-0000-0000-000000000000')

        groups = devprof['groups']
        db_extarq['device_profile_group'] = groups[devprof_group_id]

        return db_extarq

    @classmethod
    def _from_db_object(cls, extarq, db_extarq, context):
        """Converts an ExtARQ to a formal object.
        :param extarq: An object of the class ExtARQ
        :param db_extarq: A DB model of the object
        :return: The object of the class with the database entity added
        """
        cls._fill_obj_extarq_fields(context, db_extarq)

        for field in extarq.fields:
            if field != 'arq':
                extarq[field] = db_extarq.get(field)
        extarq.arq = objects.ARQ()
        extarq.arq._from_db_object(extarq.arq, db_extarq)
        extarq.obj_reset_changes()
        return extarq

    @classmethod
    def _from_db_object_list(cls, db_objs, context):
        """Converts a list of ExtARQs to a list of formal objects."""
        objs = []
        for db_obj in db_objs:
            extarq = cls(context)
            obj = cls._from_db_object(extarq, db_obj, context)
            objs.append(obj)
        return objs

    def obj_get_changes(self):
        """Returns a dict of changed fields and their new values."""
        changes = {}
        for key in self.obj_what_changed():
            if key != 'arq':
                changes[key] = getattr(self, key)

        for key in self.arq.obj_what_changed():
            changes[key] = getattr(self.arq, key)

        return changes
Пример #4
0
class DriverDevice(base.DriverObjectBase,
                   object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        '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 for driver-specific resource
        # provider.
        'vendor_board_info': object_fields.StringField(nullable=True),
        # 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',
                                                    nullable=False),
        'deployable_list': object_fields.ListOfObjectsField('DriverDeployable',
                                                            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
Пример #5
0
class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat):
    """ ExtARQ is a wrapper around ARQ with Cyborg-private fields.
        Each ExtARQ object contains exactly one ARQ object as a field.
        But, in the db layer, ExtARQ and ARQ are represented together
        as a row in a single table. Both share a single UUID.

        ExtARQ version is bumped up either if any of its fields change
        or if the ARQ version changes.
    """
    # Version 1.0: Initial version
    VERSION = '1.0'

    dbapi = dbapi.get_instance()

    fields = {
        'arq': object_fields.ObjectField('ARQ'),
        # Cyborg-private fields
        # Left substate open now, fill them out during design/implementation
        # later.
        'substate': object_fields.StringField(nullable=True),
    }

    def create(self, context, device_profile_id=None):
        """Create an ExtARQ record in the DB."""
        if 'device_profile' not in self.arq and not device_profile_id:
            raise exception.ObjectActionError(
                action='create', reason='Device profile is required in ARQ')
        self.arq.state = constants.ARQINITIAL
        self.substate = constants.ARQINITIAL
        values = self.obj_get_changes()
        arq_obj = values.pop('arq', None)
        if arq_obj is not None:
            values.update(arq_obj.as_dict())

        # Pass devprof id to db layer, to avoid repeated queries
        if device_profile_id is not None:
            values['device_profile_id'] = device_profile_id

        db_extarq = self.dbapi.extarq_create(context, values)
        self._from_db_object(self, db_extarq)
        return self

    @classmethod
    def get(cls, context, uuid):
        """Find a DB ExtARQ and return an Obj ExtARQ."""
        db_extarq = cls.dbapi.extarq_get(context, uuid)
        obj_arq = objects.ARQ(context)
        obj_extarq = ExtARQ(context)
        obj_extarq['arq'] = obj_arq
        obj_extarq = cls._from_db_object(obj_extarq, db_extarq)
        return obj_extarq

    @classmethod
    def list(cls, context, limit, marker, sort_key, sort_dir):
        """Return a list of ExtARQ objects."""
        db_extarqs = cls.dbapi.extarq_list(context, limit, marker, sort_key,
                                           sort_dir)
        obj_extarq_list = cls._from_db_object_list(db_extarqs, context)
        return obj_extarq_list

    def save(self, context):
        """Update an ExtARQ record in the DB."""
        updates = self.obj_get_changes()
        db_extarq = self.dbapi.extarq_update(context, self.arq.uuid, updates)
        self._from_db_object(self, db_extarq)

    def destroy(self, context):
        """Delete an ExtARQ from the DB."""
        self.dbapi.extarq_delete(context, self.arq.uuid)
        self.obj_reset_changes()

    def bind(self, context, host_name, devrp_uuid, instance_uuid):
        """ Given a device rp UUID, get the deployable UUID and
            an attach handle.
        """
        # For the fake device, we just set the state to 'Bound'
        # TODO(wangzhh): Move bind logic and unbind logic to the agent later.
        arq = self.arq
        arq.host_name = host_name
        arq.device_rp_uuid = devrp_uuid
        arq.instance_uuid = instance_uuid
        arq.state = constants.ARQBOUND

        self.save(context)

    def unbind(self, context):
        arq = self.arq
        arq.host_name = ''
        arq.device_rp_uuid = ''
        arq.instance_uuid = ''
        arq.state = constants.ARQUNBOUND

        self.save(context)

    @staticmethod
    def _from_db_object(extarq, db_extarq):
        """Converts an ExtARQ to a formal object.

        :param extarq: An object of the class ExtARQ
        :param db_extarq: A DB model of the object
        :return: The object of the class with the database entity added
        """
        for field in extarq.fields:
            if field != 'arq':
                extarq[field] = db_extarq[field]
        extarq.arq = objects.ARQ()
        extarq.arq._from_db_object(extarq.arq, db_extarq)
        extarq.obj_reset_changes()
        return extarq

    def obj_get_changes(self):
        """Returns a dict of changed fields and their new values."""
        changes = {}
        for key in self.obj_what_changed():
            if key != 'arq':
                changes[key] = getattr(self, key)

        for key in self.arq.obj_what_changed():
            changes[key] = getattr(self.arq, key)

        return changes