Example #1
0
    def destroy(self):
        if not self.name:
            raise exception.ObjectActionError(action='destroy',
                                              reason='name is required')

        if not self.name.startswith(self.CUSTOM_NAMESPACE):
            raise exception.TraitCannotDeleteStandard(name=self.name)

        if self.id is None:
            raise exception.ObjectActionError(action='destroy',
                                              reason='ID attribute not found')

        self._destroy_in_db(self._context, self.id, self.name)
Example #2
0
    def create(self):
        if self.id is not None:
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        if not self.name:
            raise exception.ObjectActionError(action='create',
                                              reason='name is required')
        if self.name in orc.STANDARDS:
            raise exception.ResourceClassExists(resource_class=self.name)

        if not self.name.startswith(orc.CUSTOM_NAMESPACE):
            raise exception.ObjectActionError(action='create',
                                              reason='name must start with ' +
                                              orc.CUSTOM_NAMESPACE)
        updates = {}
        for field in ['name', 'updated_at', 'created_at']:
            value = getattr(self, field, None)
            if value:
                updates[field] = value

        # There is the possibility of a race when adding resource classes, as
        # the ID is generated locally. This loop catches that exception, and
        # retries until either it succeeds, or a different exception is
        # encountered.
        retries = self.RESOURCE_CREATE_RETRY_COUNT
        while retries:
            retries -= 1
            try:
                rc = self._create_in_db(self._context, updates)
                self._from_db_object(self._context, self, rc)
                break
            except db_exc.DBDuplicateEntry as e:
                if 'id' in e.columns:
                    # Race condition for ID creation; try again
                    continue
                # The duplication is on the other unique column, 'name'. So do
                # not retry; raise the exception immediately.
                raise exception.ResourceClassExists(resource_class=self.name)
        else:
            # We have no idea how common it will be in practice for the retry
            # limit to be exceeded. We set it high in the hope that we never
            # hit this point, but added this log message so we know that this
            # specific situation occurred.
            LOG.warning(
                "Exceeded retry limit on ID generation while "
                "creating ResourceClass %(name)s", {'name': self.name})
            msg = "creating resource class %s" % self.name
            raise exception.MaxDBRetriesExceeded(action=msg)
        self._context.rc_cache.clear()
    def create(self):
        if self.id is not None:
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        if self.uuid is None:
            raise exception.ObjectActionError(action='create',
                                              reason='uuid is required')
        if not self.name:
            raise exception.ObjectActionError(action='create',
                                              reason='name is required')

        # These are the only fields we are willing to create with.
        # If there are others, ignore them.
        updates = {
            'name': self.name,
            'uuid': self.uuid,
            'parent_provider_uuid': self.parent_provider_uuid,
        }
        self._create_in_db(self._context, updates)
    def _create_in_db(self, context, updates):
        parent_id = None
        root_id = None
        # User supplied a parent, let's make sure it exists
        parent_uuid = updates.pop('parent_provider_uuid')
        if parent_uuid is not None:
            # Setting parent to ourselves doesn't make any sense
            if parent_uuid == self.uuid:
                raise exception.ObjectActionError(
                    action='create',
                    reason='parent provider UUID cannot be same as UUID. '
                           'Please set parent provider UUID to None if '
                           'there is no parent.')

            parent_ids = res_ctx.provider_ids_from_uuid(context, parent_uuid)
            if parent_ids is None:
                raise exception.ObjectActionError(
                    action='create',
                    reason='parent provider UUID does not exist.')

            parent_id = parent_ids.id
            root_id = parent_ids.root_id
            updates['root_provider_id'] = root_id
            updates['parent_provider_id'] = parent_id
            self.root_provider_uuid = parent_ids.root_uuid

        db_rp = models.ResourceProvider()
        db_rp.update(updates)
        context.session.add(db_rp)
        context.session.flush()

        self.id = db_rp.id
        self.generation = db_rp.generation

        if root_id is None:
            # User did not specify a parent when creating this provider, so the
            # root_provider_id needs to be set to this provider's newly-created
            # internal ID
            db_rp.root_provider_id = db_rp.id
            context.session.add(db_rp)
            context.session.flush()
            self.root_provider_uuid = self.uuid
Example #5
0
    def destroy(self):
        if self.id is None:
            raise exception.ObjectActionError(action='destroy',
                                              reason='ID attribute not found')
        # Never delete any standard resource class.
        if self.id < ResourceClass.MIN_CUSTOM_RESOURCE_CLASS_ID:
            raise exception.ResourceClassCannotDeleteStandard(
                resource_class=self.name)

        self._destroy(self._context, self.id, self.name)
        self._context.rc_cache.clear()
Example #6
0
    def create(self):
        if self.id is not None:
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        if not self.name:
            raise exception.ObjectActionError(action='create',
                                              reason='name is required')

        # FIXME(cdent): duped from resource class
        updates = {}
        for field in ['name', 'updated_at', 'created_at']:
            value = getattr(self, field, None)
            if value:
                updates[field] = value

        try:
            db_trait = self._create_in_db(self._context, updates)
        except db_exc.DBDuplicateEntry:
            raise exception.TraitExists(name=self.name)

        self._from_db_object(self._context, self, db_trait)
Example #7
0
 def save(self):
     if self.id is None:
         raise exception.ObjectActionError(action='save',
                                           reason='ID attribute not found')
     updates = {}
     for field in ['name', 'updated_at', 'created_at']:
         value = getattr(self, field, None)
         if value:
             updates[field] = value
     # Never update any standard resource class.
     if self.id < ResourceClass.MIN_CUSTOM_RESOURCE_CLASS_ID:
         raise exception.ResourceClassCannotUpdateStandard(
             resource_class=self.name)
     self._save(self._context, self.id, self.name, updates)
     self._context.rc_cache.clear()
    def _update_in_db(self, context, id, updates, allow_reparenting):
        # A list of resource providers in the subtree of resource provider to
        # update
        subtree_rps = []
        # The new root RP if changed
        new_root_id = None
        new_root_uuid = None
        if 'parent_provider_uuid' in updates:
            my_ids = res_ctx.provider_ids_from_uuid(context, self.uuid)
            parent_uuid = updates.pop('parent_provider_uuid')
            if parent_uuid is not None:
                parent_ids = res_ctx.provider_ids_from_uuid(
                    context, parent_uuid)
                # User supplied a parent, let's make sure it exists
                if parent_ids is None:
                    raise exception.ObjectActionError(
                        action='create',
                        reason='parent provider UUID does not exist.')
                if (my_ids.parent_id is not None and
                        my_ids.parent_id != parent_ids.id and
                        not allow_reparenting):
                    raise exception.ObjectActionError(
                        action='update',
                        reason='re-parenting a provider is not currently '
                               'allowed.')
                # So the user specified a new parent. We have to make sure
                # that the new parent is not a descendant of the
                # current RP to avoid a loop in the graph. It could be
                # easily checked by traversing the tree from the new parent
                # up to the root and see if we ever hit the current RP
                # along the way. However later we need to update every
                # descendant of the current RP with a possibly new root
                # so we go with the more expensive way and gather every
                # descendant for the current RP and check if the new
                # parent is part of that set.
                subtree_rps = self.get_subtree(context)
                subtree_rp_uuids = {rp.uuid for rp in subtree_rps}
                if parent_uuid in subtree_rp_uuids:
                    raise exception.ObjectActionError(
                        action='update',
                        reason='creating loop in the provider tree is '
                               'not allowed.')

                updates['root_provider_id'] = parent_ids.root_id
                updates['parent_provider_id'] = parent_ids.id
                self.root_provider_uuid = parent_ids.root_uuid
                new_root_id = parent_ids.root_id
                new_root_uuid = parent_ids.root_uuid
            else:
                if my_ids.parent_id is not None:
                    if not allow_reparenting:
                        raise exception.ObjectActionError(
                            action='update',
                            reason='un-parenting a provider is not currently '
                                   'allowed.')

                    # we don't need to do loop detection but we still need to
                    # collect the RPs from the subtree so that the new root
                    # value is updated in the whole subtree below.
                    subtree_rps = self.get_subtree(context)

                    # this RP becomes a new root RP
                    updates['root_provider_id'] = my_ids.id
                    updates['parent_provider_id'] = None
                    self.root_provider_uuid = my_ids.uuid
                    new_root_id = my_ids.id
                    new_root_uuid = my_ids.uuid

        db_rp = context.session.query(models.ResourceProvider).filter_by(
            id=id).first()
        db_rp.update(updates)
        context.session.add(db_rp)

        # We should also update the root providers of the resource providers
        # that are in our subtree
        for rp in subtree_rps:
            # If the parent is not updated, this clause is skipped since the
            # `subtree_rps` has no element.
            rp.root_provider_uuid = new_root_uuid
            db_rp = context.session.query(
                models.ResourceProvider).filter_by(id=rp.id).first()
            data = {'root_provider_id': new_root_id}
            db_rp.update(data)
            context.session.add(db_rp)

        try:
            context.session.flush()
        except sqla_exc.IntegrityError:
            # NOTE(jaypipes): Another thread snuck in and deleted the parent
            # for this resource provider in between the above check for a valid
            # parent provider and here...
            raise exception.ObjectActionError(
                action='update',
                reason='parent provider UUID does not exist.')
Example #9
0
    def _update_in_db(self, context, id, updates):
        # A list of resource providers in the same tree with the
        # resource provider to update
        same_tree = []
        if 'parent_provider_uuid' in updates:
            # TODO(jaypipes): For now, "re-parenting" and "un-parenting" are
            # not possible. If the provider already had a parent, we don't
            # allow changing that parent due to various issues, including:
            #
            # * if the new parent is a descendant of this resource provider, we
            #   introduce the possibility of a loop in the graph, which would
            #   be very bad
            # * potentially orphaning heretofore-descendants
            #
            # So, for now, let's just prevent re-parenting...
            my_ids = res_ctx.provider_ids_from_uuid(context, self.uuid)
            parent_uuid = updates.pop('parent_provider_uuid')
            if parent_uuid is not None:
                parent_ids = res_ctx.provider_ids_from_uuid(
                    context, parent_uuid)
                # User supplied a parent, let's make sure it exists
                if parent_ids is None:
                    raise exception.ObjectActionError(
                        action='create',
                        reason='parent provider UUID does not exist.')
                if (my_ids.parent_id is not None
                        and my_ids.parent_id != parent_ids.id):
                    raise exception.ObjectActionError(
                        action='update',
                        reason='re-parenting a provider is not currently '
                        'allowed.')
                if my_ids.parent_uuid is None:
                    # So the user specifies a parent for an RP that doesn't
                    # have one. We have to check that by this new parent we
                    # don't create a loop in the tree. Basically the new parent
                    # cannot be the RP itself or one of its descendants.
                    # However as the RP's current parent is None the above
                    # condition is the same as "the new parent cannot be any RP
                    # from the current RP tree".
                    same_tree = get_all_by_filters(
                        context, filters={'in_tree': self.uuid})
                    rp_uuids_in_the_same_tree = [rp.uuid for rp in same_tree]
                    if parent_uuid in rp_uuids_in_the_same_tree:
                        raise exception.ObjectActionError(
                            action='update',
                            reason='creating loop in the provider tree is '
                            'not allowed.')

                updates['root_provider_id'] = parent_ids.root_id
                updates['parent_provider_id'] = parent_ids.id
                self.root_provider_uuid = parent_ids.root_uuid
            else:
                if my_ids.parent_id is not None:
                    raise exception.ObjectActionError(
                        action='update',
                        reason='un-parenting a provider is not currently '
                        'allowed.')

        db_rp = context.session.query(
            models.ResourceProvider).filter_by(id=id).first()
        db_rp.update(updates)
        context.session.add(db_rp)

        # We should also update the root providers of resource providers
        # originally in the same tree. If re-parenting is supported,
        # this logic should be changed to update only descendents of the
        # re-parented resource providers, not all the providers in the tree.
        for rp in same_tree:
            # If the parent is not updated, this clause is skipped since the
            # `same_tree` has no element.
            rp.root_provider_uuid = parent_ids.root_uuid
            db_rp = context.session.query(
                models.ResourceProvider).filter_by(id=rp.id).first()
            data = {'root_provider_id': parent_ids.root_id}
            db_rp.update(data)
            context.session.add(db_rp)

        try:
            context.session.flush()
        except sqla_exc.IntegrityError:
            # NOTE(jaypipes): Another thread snuck in and deleted the parent
            # for this resource provider in between the above check for a valid
            # parent provider and here...
            raise exception.ObjectActionError(
                action='update', reason='parent provider UUID does not exist.')