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)
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
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()
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)
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.')
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.')