def delete(self, context, group, force=False): if not group.host: self.update_quota(context, group, -1, group.project_id) LOG.debug( "No host for consistency group %s. Deleting from " "the database.", group.id) group.destroy() return if not force and group.status not in ([ c_fields.ConsistencyGroupStatus.AVAILABLE, c_fields.ConsistencyGroupStatus.ERROR ]): msg = _("Consistency group status must be available or error, " "but current status is: %s") % group.status raise exception.InvalidConsistencyGroup(reason=msg) cgsnapshots = objects.CGSnapshotList.get_all_by_group( context.elevated(), group.id) if cgsnapshots: msg = _("Consistency group %s still has dependent " "cgsnapshots.") % group.id LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) volumes = self.db.volume_get_all_by_group(context.elevated(), group.id) if volumes and not force: msg = _("Consistency group %s still contains volumes. " "The force flag is required to delete it.") % group.id LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) for volume in volumes: if volume['attach_status'] == "attached": msg = _("Volume in consistency group %s is attached. " "Need to detach first.") % group.id LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) snapshots = objects.SnapshotList.get_all_for_volume( context, volume['id']) if snapshots: msg = _("Volume in consistency group still has " "dependent snapshots.") LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) group.status = c_fields.ConsistencyGroupStatus.DELETING group.terminated_at = timeutils.utcnow() group.save() self.volume_rpcapi.delete_consistencygroup(context, group)
def delete(self, context, group, force=False): if not group['host']: self.update_quota(context, group['id'], -1, group['project_id']) msg = ("No host for consistency group %s. Deleting from " "the database.") % group['id'] LOG.debug(msg) self.db.consistencygroup_destroy(context.elevated(), group['id']) return if not force and group['status'] not in ["available", "error"]: msg = _("Consistency group status must be available or error, " "but current status is: %s") % group['status'] raise exception.InvalidConsistencyGroup(reason=msg) cgsnaps = self.db.cgsnapshot_get_all_by_group(context.elevated(), group['id']) if cgsnaps: msg = _("Consistency group %s still has dependent " "cgsnapshots.") % group['id'] LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) volumes = self.db.volume_get_all_by_group(context.elevated(), group['id']) if volumes and not force: msg = _("Consistency group %s still contains volumes. " "The force flag is required to delete it.") % group['id'] LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) for volume in volumes: if volume['attach_status'] == "attached": msg = _("Volume in consistency group %s is attached. " "Need to detach first.") % group['id'] LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) snapshots = self.db.snapshot_get_all_for_volume( context, volume['id']) if snapshots: msg = _("Volume in consistency group still has " "dependent snapshots.") LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) now = timeutils.utcnow() self.db.consistencygroup_update(context, group['id'], { 'status': 'deleting', 'terminated_at': now }) self.volume_rpcapi.delete_consistencygroup(context, group)
def _create_cg_from_cgsnapshot(self, context, group, cgsnapshot): try: snapshots = objects.SnapshotList.get_all_for_cgsnapshot( context, cgsnapshot.id) if not snapshots: msg = _("Cgsnahost is empty. No consistency group " "will be created.") raise exception.InvalidConsistencyGroup(reason=msg) for snapshot in snapshots: kwargs = {} kwargs['availability_zone'] = group.availability_zone kwargs['cgsnapshot'] = cgsnapshot kwargs['consistencygroup'] = group kwargs['snapshot'] = snapshot volume_type_id = snapshot.volume_type_id if volume_type_id: kwargs['volume_type'] = volume_types.get_volume_type( context, volume_type_id) # Since cgsnapshot is passed in, the following call will # create a db entry for the volume, but will not call the # volume manager to create a real volume in the backend yet. # If error happens, taskflow will handle rollback of quota # and removal of volume entry in the db. try: self.volume_api.create(context, snapshot.volume_size, None, None, **kwargs) except exception.CinderException: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error occurred when creating volume " "entry from snapshot in the process of " "creating consistency group %(group)s " "from cgsnapshot %(cgsnap)s."), { 'group': group.id, 'cgsnap': cgsnapshot.id }) except Exception: with excutils.save_and_reraise_exception(): try: group.destroy() finally: LOG.error( _LE("Error occurred when creating consistency " "group %(group)s from cgsnapshot " "%(cgsnap)s."), { 'group': group.id, 'cgsnap': cgsnapshot.id }) volumes = self.db.volume_get_all_by_group(context, group.id) for vol in volumes: # Update the host field for the volume. self.db.volume_update(context, vol['id'], {'host': group.get('host')}) self.volume_rpcapi.create_consistencygroup_from_src( context, group, cgsnapshot)
def _create_cg_from_source_cg(self, context, group, source_cg): try: source_vols = self.db.volume_get_all_by_group(context, source_cg.id) if not source_vols: msg = _("Source CG is empty. No consistency group " "will be created.") raise exception.InvalidConsistencyGroup(reason=msg) for source_vol in source_vols: kwargs = {} kwargs['availability_zone'] = group.availability_zone kwargs['source_cg'] = source_cg kwargs['consistencygroup'] = group kwargs['source_volume'] = source_vol volume_type_id = source_vol.get('volume_type_id') if volume_type_id: kwargs['volume_type'] = volume_types.get_volume_type( context, volume_type_id) # Since source_cg is passed in, the following call will # create a db entry for the volume, but will not call the # volume manager to create a real volume in the backend yet. # If error happens, taskflow will handle rollback of quota # and removal of volume entry in the db. try: self.volume_api.create(context, source_vol['size'], None, None, **kwargs) except exception.CinderException: with excutils.save_and_reraise_exception(): LOG.error(_LE("Error occurred when creating cloned " "volume in the process of creating " "consistency group %(group)s from " "source CG %(source_cg)s."), {'group': group.id, 'source_cg': source_cg.id}) except Exception: with excutils.save_and_reraise_exception(): try: group.destroy() finally: LOG.error(_LE("Error occurred when creating consistency " "group %(group)s from source CG " "%(source_cg)s."), {'group': group.id, 'source_cg': source_cg.id}) volumes = self.db.volume_get_all_by_group(context, group.id) for vol in volumes: # Update the host field for the volume. self.db.volume_update(context, vol['id'], {'host': group.host}) self.volume_rpcapi.create_consistencygroup_from_src(context, group, None, source_cg)
def delete(self, context, group, force=False): if not group.host: self.update_quota(context, group, -1, group.project_id) LOG.debug("No host for consistency group %s. Deleting from " "the database.", group.id) group.destroy() return if force: expected = {} else: expected = {'status': (c_fields.ConsistencyGroupStatus.AVAILABLE, c_fields.ConsistencyGroupStatus.ERROR)} filters = [~db.cg_has_cgsnapshot_filter(), ~db.cg_has_volumes_filter(attached_or_with_snapshots=force), ~db.cg_creating_from_src(cg_id=group.id)] values = {'status': c_fields.ConsistencyGroupStatus.DELETING} if not group.conditional_update(values, expected, filters): if force: reason = _('Consistency group must not have attached volumes, ' 'volumes with snapshots, or dependent cgsnapshots') else: reason = _('Consistency group status must be available or ' 'error and must not have volumes or dependent ' 'cgsnapshots') msg = (_('Cannot delete consistency group %(id)s. %(reason)s, and ' 'it cannot be the source for an ongoing CG or CG ' 'Snapshot creation.') % {'id': group.id, 'reason': reason}) raise exception.InvalidConsistencyGroup(reason=msg) self.volume_rpcapi.delete_consistencygroup(context, group)
def _check_update(self, group, name, description, add_volumes, remove_volumes, allow_empty=False): if allow_empty: if (name is None and description is None and not add_volumes and not remove_volumes): msg = (_("Cannot update consistency group %(group_id)s " "because no valid name, description, add_volumes, " "or remove_volumes were provided.") % {'group_id': group.id}) raise exception.InvalidConsistencyGroup(reason=msg) else: if not (name or description or add_volumes or remove_volumes): msg = (_("Cannot update consistency group %(group_id)s " "because no valid name, description, add_volumes, " "or remove_volumes were provided.") % {'group_id': group.id}) raise exception.InvalidConsistencyGroup(reason=msg)
def create_from_src(self, context, name, description=None, cgsnapshot_id=None, source_cgid=None): check_policy(context, 'create') kwargs = { 'user_id': context.user_id, 'project_id': context.project_id, 'status': c_fields.ConsistencyGroupStatus.CREATING, 'name': name, 'description': description, 'cgsnapshot_id': cgsnapshot_id, 'source_cgid': source_cgid, } group = None try: group = objects.ConsistencyGroup(context=context, **kwargs) group.create(cg_snap_id=cgsnapshot_id, cg_id=source_cgid) except exception.ConsistencyGroupNotFound: with excutils.save_and_reraise_exception(): LOG.error(_LE("Source CG %(source_cg)s not found when " "creating consistency group %(cg)s from " "source."), {'cg': name, 'source_cg': source_cgid}) except exception.CgSnapshotNotFound: with excutils.save_and_reraise_exception(): LOG.error(_LE("CG snapshot %(cgsnap)s not found when creating " "consistency group %(cg)s from source."), {'cg': name, 'cgsnap': cgsnapshot_id}) except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Error occurred when creating consistency group" " %(cg)s from cgsnapshot %(cgsnap)s."), {'cg': name, 'cgsnap': cgsnapshot_id}) # Update quota for consistencygroups self.update_quota(context, group, 1) if not group.host: msg = _("No host to create consistency group %s.") % group.id LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) if cgsnapshot_id: self._create_cg_from_cgsnapshot(context, group, cgsnapshot_id) elif source_cgid: self._create_cg_from_source_cg(context, group, source_cgid) return group
def create_from_src(self, context, name, description, cgsnapshot_id): check_policy(context, 'create') cgsnapshot = None orig_cg = None if cgsnapshot_id: cgsnapshot = self.db.cgsnapshot_get(context, cgsnapshot_id) if cgsnapshot: orig_cg = self.db.consistencygroup_get( context, cgsnapshot['consistencygroup_id']) options = { 'user_id': context.user_id, 'project_id': context.project_id, 'status': "creating", 'name': name, 'description': description, 'cgsnapshot_id': cgsnapshot_id } if orig_cg: options['volume_type_id'] = orig_cg.get('volume_type_id') options['availability_zone'] = orig_cg.get('availability_zone') options['host'] = orig_cg.get('host') group = None try: group = self.db.consistencygroup_create(context, options) except Exception: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error occurred when creating consistency group" " %(cg)s from cgsnapshot %(cgsnap)s."), { 'cg': name, 'cgsnap': cgsnapshot_id }) # Update quota for consistencygroups self.update_quota(context, group['id'], 1) if not group['host']: msg = _("No host to create consistency group %s.") % group['id'] LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) self._create_cg_from_cgsnapshot(context, group, cgsnapshot) return group
def _extract_consistencygroup(consistencygroup): """Extracts the consistencygroup id from the provided consistencygroup. This function validates the input consistencygroup dict and checks that the status of that consistencygroup is valid for creating a volume in. """ consistencygroup_id = None if consistencygroup is not None: if consistencygroup['status'] not in CG_PROCEED_STATUS: msg = _("Originating consistencygroup status must be one" " of '%s' values") msg = msg % (", ".join(CG_PROCEED_STATUS)) raise exception.InvalidConsistencyGroup(reason=msg) consistencygroup_id = consistencygroup['id'] return consistencygroup_id
def _create_cgsnapshot(self, context, group, name, description): volumes = self.db.volume_get_all_by_group(context.elevated(), group.id) if not volumes: msg = _("Consistency group is empty. No cgsnapshot " "will be created.") raise exception.InvalidConsistencyGroup(reason=msg) options = { 'consistencygroup_id': group.id, 'user_id': context.user_id, 'project_id': context.project_id, 'status': "creating", 'name': name, 'description': description } cgsnapshot = None cgsnapshot_id = None try: cgsnapshot = objects.CGSnapshot(context, **options) cgsnapshot.create() cgsnapshot_id = cgsnapshot.id snap_name = cgsnapshot.name snap_desc = cgsnapshot.description self.volume_api.create_snapshots_in_db(context, volumes, snap_name, snap_desc, True, cgsnapshot_id) except Exception: with excutils.save_and_reraise_exception(): try: if cgsnapshot: cgsnapshot.destroy() finally: LOG.error( _LE("Error occurred when creating cgsnapshot" " %s."), cgsnapshot_id) self.volume_rpcapi.create_cgsnapshot(context, cgsnapshot) return cgsnapshot
def update(self, context, group, name, description, add_volumes, remove_volumes, allow_empty=False): """Update consistency group.""" add_volumes_list = [] remove_volumes_list = [] if add_volumes: add_volumes = add_volumes.strip(',') add_volumes_list = add_volumes.split(',') if remove_volumes: remove_volumes = remove_volumes.strip(',') remove_volumes_list = remove_volumes.split(',') invalid_uuids = [] for uuid in add_volumes_list: if uuid in remove_volumes_list: invalid_uuids.append(uuid) if invalid_uuids: msg = _("UUIDs %s are in both add and remove volume " "list.") % invalid_uuids raise exception.InvalidVolume(reason=msg) # Validate name. if name == group.name: name = None # Validate description. if description == group.description: description = None self._check_update(group, name, description, add_volumes, remove_volumes, allow_empty) fields = {'updated_at': timeutils.utcnow()} # Update name and description in db now. No need to # to send them over through an RPC call. if allow_empty: if name is not None: fields['name'] = name if description is not None: fields['description'] = description else: if name: fields['name'] = name if description: fields['description'] = description # NOTE(geguileo): We will use the updating status in the CG as a lock # mechanism to prevent volume add/remove races with other API, while we # figure out if we really need to add or remove volumes. if add_volumes or remove_volumes: fields['status'] = c_fields.ConsistencyGroupStatus.UPDATING # We cannot modify the members of this CG if the CG is being used # to create another CG or a CGsnapshot is being created filters = [ ~db.cg_creating_from_src(cg_id=group.id), ~db.cgsnapshot_creating_from_src() ] else: filters = [] expected = {'status': c_fields.ConsistencyGroupStatus.AVAILABLE} if not group.conditional_update(fields, expected, filters): msg = _("Cannot update consistency group %s, status must be " "available, and it cannot be the source for an ongoing " "CG or CG Snapshot creation.") % group.id raise exception.InvalidConsistencyGroup(reason=msg) # Now the CG is "locked" for updating try: # Validate volumes in add_volumes and remove_volumes. add_volumes_new = self._validate_add_volumes( context, group.volumes, add_volumes_list, group) remove_volumes_new = self._validate_remove_volumes( group.volumes, remove_volumes_list, group) self._check_update(group, name, description, add_volumes_new, remove_volumes_new, allow_empty) except Exception: # If we have an error on the volume_lists we must return status to # available as we were doing before removing API races with excutils.save_and_reraise_exception(): group.status = c_fields.ConsistencyGroupStatus.AVAILABLE group.save() # Do an RPC call only if the update request includes # adding/removing volumes. add_volumes_new and remove_volumes_new # are strings of volume UUIDs separated by commas with no spaces # in between. if add_volumes_new or remove_volumes_new: self.volume_rpcapi.update_consistencygroup( context, group, add_volumes=add_volumes_new, remove_volumes=remove_volumes_new) # If there are no new volumes to add or remove and we had changed # the status to updating, turn it back to available elif group.status == c_fields.ConsistencyGroupStatus.UPDATING: group.status = c_fields.ConsistencyGroupStatus.AVAILABLE group.save()
def update(self, context, group, name, description, add_volumes, remove_volumes): """Update consistency group.""" if group.status != c_fields.ConsistencyGroupStatus.AVAILABLE: msg = _("Consistency group status must be available, " "but current status is: %s.") % group.status raise exception.InvalidConsistencyGroup(reason=msg) add_volumes_list = [] remove_volumes_list = [] if add_volumes: add_volumes = add_volumes.strip(',') add_volumes_list = add_volumes.split(',') if remove_volumes: remove_volumes = remove_volumes.strip(',') remove_volumes_list = remove_volumes.split(',') invalid_uuids = [] for uuid in add_volumes_list: if uuid in remove_volumes_list: invalid_uuids.append(uuid) if invalid_uuids: msg = _("UUIDs %s are in both add and remove volume " "list.") % invalid_uuids raise exception.InvalidVolume(reason=msg) volumes = self.db.volume_get_all_by_group(context, group.id) # Validate name. if not name or name == group.name: name = None # Validate description. if not description or description == group.description: description = None # Validate volumes in add_volumes and remove_volumes. add_volumes_new = "" remove_volumes_new = "" if add_volumes_list: add_volumes_new = self._validate_add_volumes( context, volumes, add_volumes_list, group) if remove_volumes_list: remove_volumes_new = self._validate_remove_volumes( volumes, remove_volumes_list, group) if (not name and not description and not add_volumes_new and not remove_volumes_new): msg = (_("Cannot update consistency group %(group_id)s " "because no valid name, description, add_volumes, " "or remove_volumes were provided.") % { 'group_id': group.id }) raise exception.InvalidConsistencyGroup(reason=msg) fields = {'updated_at': timeutils.utcnow()} # Update name and description in db now. No need to # to send them over through an RPC call. if name: fields['name'] = name if description: fields['description'] = description if not add_volumes_new and not remove_volumes_new: # Only update name or description. Set status to available. fields['status'] = 'available' else: fields['status'] = 'updating' group.update(fields) group.save() # Do an RPC call only if the update request includes # adding/removing volumes. add_volumes_new and remove_volumes_new # are strings of volume UUIDs separated by commas with no spaces # in between. if add_volumes_new or remove_volumes_new: self.volume_rpcapi.update_consistencygroup( context, group, add_volumes=add_volumes_new, remove_volumes=remove_volumes_new)
def create_from_src(self, context, name, description=None, cgsnapshot_id=None, source_cgid=None): check_policy(context, 'create') cgsnapshot = None orig_cg = None if cgsnapshot_id: try: cgsnapshot = objects.CGSnapshot.get_by_id( context, cgsnapshot_id) except exception.CgSnapshotNotFound: with excutils.save_and_reraise_exception(): LOG.error( _LE("CG snapshot %(cgsnap)s not found when " "creating consistency group %(cg)s from " "source."), { 'cg': name, 'cgsnap': cgsnapshot_id }) else: orig_cg = cgsnapshot.consistencygroup source_cg = None if source_cgid: try: source_cg = objects.ConsistencyGroup.get_by_id( context, source_cgid) except exception.ConsistencyGroupNotFound: with excutils.save_and_reraise_exception(): LOG.error( _LE("Source CG %(source_cg)s not found when " "creating consistency group %(cg)s from " "source."), { 'cg': name, 'source_cg': source_cgid }) kwargs = { 'user_id': context.user_id, 'project_id': context.project_id, 'status': c_fields.ConsistencyGroupStatus.CREATING, 'name': name, 'description': description, 'cgsnapshot_id': cgsnapshot_id, 'source_cgid': source_cgid, } if orig_cg: kwargs['volume_type_id'] = orig_cg.volume_type_id kwargs['availability_zone'] = orig_cg.availability_zone kwargs['host'] = orig_cg.host if source_cg: kwargs['volume_type_id'] = source_cg.volume_type_id kwargs['availability_zone'] = source_cg.availability_zone kwargs['host'] = source_cg.host group = None try: group = objects.ConsistencyGroup(context=context, **kwargs) group.create() except Exception: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error occurred when creating consistency group" " %(cg)s from cgsnapshot %(cgsnap)s."), { 'cg': name, 'cgsnap': cgsnapshot_id }) # Update quota for consistencygroups self.update_quota(context, group, 1) if not group.host: msg = _("No host to create consistency group %s.") % group.id LOG.error(msg) raise exception.InvalidConsistencyGroup(reason=msg) if cgsnapshot: self._create_cg_from_cgsnapshot(context, group, cgsnapshot) elif source_cg: self._create_cg_from_source_cg(context, group, source_cg) return group
def _create_cg_from_source_cg(self, context, group, source_cgid): try: source_cg = objects.ConsistencyGroup.get_by_id( context, source_cgid) source_vols = self.db.volume_get_all_by_group( context, source_cg.id) if not source_vols: msg = _("Source CG is empty. No consistency group " "will be created.") raise exception.InvalidConsistencyGroup(reason=msg) try: values = {'volumes': len(source_vols)} QUOTAS.limit_check(context, project_id=context.project_id, **values) except exception.OverQuota as e: group.destroy() quotas = e.kwargs['quotas'] raise exception.VolumeLimitExceeded(allowed=e.kwargs['overs'], limit=quotas['volumes']) for source_vol in source_vols: kwargs = {} kwargs['availability_zone'] = group.availability_zone kwargs['source_cg'] = source_cg kwargs['consistencygroup'] = group kwargs['source_volume'] = source_vol volume_type_id = source_vol.get('volume_type_id') if volume_type_id: kwargs['volume_type'] = ( objects.VolumeType.get_by_name_or_id( context, volume_type_id)) # Since source_cg is passed in, the following call will # create a db entry for the volume, but will not call the # volume manager to create a real volume in the backend yet. # If error happens, taskflow will handle rollback of quota # and removal of volume entry in the db. try: self.volume_api.create(context, source_vol['size'], None, None, **kwargs) except exception.CinderException: with excutils.save_and_reraise_exception(): LOG.error( "Error occurred when creating cloned " "volume in the process of creating " "consistency group %(group)s from " "source CG %(source_cg)s.", { 'group': group.id, 'source_cg': source_cg.id }) except Exception: with excutils.save_and_reraise_exception(): try: new_vols = self.db.volume_get_all_by_group( context, group.id) for vol in new_vols: self.volume_api.delete(context, vol, force=True) group.destroy() finally: LOG.error( "Error occurred when creating consistency " "group %(group)s from source CG " "%(source_cg)s.", { 'group': group.id, 'source_cg': source_cg.id }) volumes = self.db.volume_get_all_by_group(context, group.id) for vol in volumes: # Update the host field for the volume. self.db.volume_update(context, vol['id'], {'host': group.host}) self.volume_rpcapi.create_consistencygroup_from_src( context, group, None, source_cg)