def create(self, context, name, description, cg_volume_types=None, availability_zone=None): check_policy(context, 'create') volume_type_list = None if cg_volume_types: volume_type_list = cg_volume_types.split(',') req_volume_types = [] if volume_type_list: req_volume_types = (self.db.volume_types_get_by_name_or_id( context, volume_type_list)) if not req_volume_types: volume_type = volume_types.get_default_volume_type() req_volume_types.append(volume_type) req_volume_type_ids = "" for voltype in req_volume_types: if voltype: req_volume_type_ids = ( req_volume_type_ids + voltype.get('id') + ",") if len(req_volume_type_ids) == 0: req_volume_type_ids = None availability_zone = self._extract_availability_zone(availability_zone) options = {'user_id': context.user_id, 'project_id': context.project_id, 'availability_zone': availability_zone, 'status': "creating", 'name': name, 'description': description, 'volume_type_id': req_volume_type_ids} group = None try: group = self.db.consistencygroup_create(context, options) except Exception: with excutils.save_and_reraise_exception(): LOG.error(_("Error occurred when creating consistency group" " %s."), name) request_spec_list = [] filter_properties_list = [] for req_volume_type in req_volume_types: request_spec = {'volume_type': req_volume_type.copy(), 'consistencygroup_id': group['id']} filter_properties = {} request_spec_list.append(request_spec) filter_properties_list.append(filter_properties) # Update quota for consistencygroups self.update_quota(context, group['id']) self._cast_create_consistencygroup(context, group['id'], request_spec_list, filter_properties_list) return group
def execute(self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, backup_source_volume): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume) policy.enforce_action(context, ACTION) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) size = self._extract_size(size, source_volume, snapshot) self._check_image_metadata(context, image_id, size) availability_zone = self._extract_availability_zone(availability_zone, snapshot, source_volume) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoid any dependency upon the encrypted volume type. if not volume_type and not source_volume and not snapshot: volume_type = volume_types.get_default_volume_type() volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot, backup_source_volume) encryption_key_id = self._get_encryption_key_id(key_manager, context, volume_type_id, snapshot, source_volume, backup_source_volume) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) specs = qos_specs['qos_specs'] if not specs: # to make sure we don't pass empty dict specs = None self._check_metadata_properties(metadata) return { 'size': size, 'snapshot_id': snapshot_id, 'source_volid': source_volid, 'availability_zone': availability_zone, 'volume_type': volume_type, 'volume_type_id': volume_type_id, 'encryption_key_id': encryption_key_id, 'qos_specs': specs, }
def test_default_volume_type_missing_in_db(self): """Ensures proper exception raised if default volume type is not in database. """ session = db_api.get_session() default_vol_type = volume_types.get_default_volume_type() self.assertEqual(default_vol_type, {})
def test_get_default_volume_type(self): """ Ensures default volume type can be retrieved """ volume_types.create(self.ctxt, fake_flags.def_vol_type, {}) default_vol_type = volume_types.get_default_volume_type() self.assertEqual(default_vol_type.get('name'), fake_flags.def_vol_type)
def test_default_volume_type_missing_in_db(self): """Test default volume type is missing in database. Ensures proper exception raised if default volume type is not in database. """ default_vol_type = volume_types.get_default_volume_type() self.assertEqual({}, default_vol_type)
def test_get_default_volume_type(self): """Ensures default volume type can be retrieved.""" type_ref = volume_types.create(self.ctxt, conf_fixture.def_vol_type, {}) default_vol_type = volume_types.get_default_volume_type() self.assertEqual(default_vol_type.get('name'), conf_fixture.def_vol_type)
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None, scheduler_hints=None, source_replica=None, consistencygroup=None, cgsnapshot=None, multiattach=False, source_cg=None): if not volume_type: volume_type = volume_types.get_default_volume_type() if self._is_lunr_volume_type(context, volume_type): # Lunr has size limits by volume type. Fail here instead of # getting an 'error' volume. self._validate_lunr_volume_type(volume_type, size) if not CONF.lunr_copy_image_enabled: image_id = None if not CONF.lunr_volume_clone_enabled: source_volume = None if snapshot: if self._is_lunr_volume_type(context, snapshot['volume_type_id']): snapshot['volume_type_id'] = volume_type['id'] if source_volume: if self._is_lunr_volume_type(context, source_volume['volume_type_id']): source_volume['volume_type_id'] = volume_type['id'] kwargs = {} if snapshot is not None: kwargs['snapshot'] = snapshot if image_id is not None: kwargs['image_id'] = image_id if volume_type is not None: kwargs['volume_type'] = volume_type if metadata is not None: kwargs['metadata'] = metadata if availability_zone is not None: kwargs['availability_zone'] = availability_zone if source_volume is not None: kwargs['source_volume'] = source_volume if scheduler_hints is not None: kwargs['scheduler_hints'] = scheduler_hints if multiattach is not None: kwargs['multiattach'] = multiattach if source_replica is not None: kwargs['source_replica'] = source_replica if consistencygroup is not None: kwargs['consistencygroup'] = consistencygroup if cgsnapshot is not None: kwargs['cgsnapshot'] = cgsnapshot if source_cg is not None: kwargs['source_cg'] = source_cg return super(API, self).create(context, size, name, description, **kwargs)
def show(self, req, id): """Return a single volume type item.""" context = req.environ['cinder.context'] # get default volume type if id is not None and id == 'default': vol_type = volume_types.get_default_volume_type() if not vol_type: msg = _("Default volume type can not be found.") raise exception.VolumeTypeNotFound(message=msg) req.cache_resource(vol_type, name='types') else: # Not found exception will be handled at wsgi level vol_type = volume_types.get_volume_type(context, id) req.cache_resource(vol_type, name='types') return self._view_builder.show(req, vol_type)
def show(self, req, id): """Return a single volume type item.""" context = req.environ['cinder.context'] # get default volume type if id is not None and id == 'default': vol_type = volume_types.get_default_volume_type() if not vol_type: msg = _("Default volume type can not be found.") raise exc.HTTPNotFound(explanation=msg) req.cache_resource(vol_type, name='types') else: try: vol_type = volume_types.get_volume_type(context, id) req.cache_resource(vol_type, name='types') except exception.VolumeTypeNotFound as error: raise exc.HTTPNotFound(explanation=error.msg) return self._view_builder.show(req, vol_type)
def execute(self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, consistencygroup, cgsnapshot, group, group_snapshot, backup): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume, backup=backup) context.authorize(policy.CREATE_POLICY) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) backup_id = self._extract_backup(backup) size = self._extract_size(size, source_volume, snapshot, backup) consistencygroup_id = self._extract_consistencygroup(consistencygroup) cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot) group_id = self._extract_group(group) image_meta = self._get_image_metadata(context, image_id, size) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoids any dependency upon the encrypted volume type. def_vol_type = volume_types.get_default_volume_type() if not volume_type and not source_volume and not snapshot: image_volume_type = self._get_image_volume_type(context, image_id) volume_type = (image_volume_type if image_volume_type else def_vol_type) availability_zones, refresh_az = self._extract_availability_zones( availability_zone, snapshot, source_volume, group, volume_type=volume_type) volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot) encryption_key_id = self._get_encryption_key_id( key_manager, context, volume_type_id, snapshot, source_volume, image_meta) if encryption_key_id is not None and volume_type is not None: extra_specs = volume_type.get('extra_specs', {}) if extra_specs.get('multiattach', '') == '<is> True': msg = _('Multiattach cannot be used with encrypted volumes.') raise exception.InvalidVolume(reason=msg) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) if qos_specs['qos_specs']: specs = qos_specs['qos_specs'].get('specs', {}) # Determine default replication status extra_specs = volume_types.get_volume_type_extra_specs( volume_type_id) if not specs: # to make sure we don't pass empty dict specs = None extra_specs = None if vol_utils.is_replicated_spec(extra_specs): replication_status = fields.ReplicationStatus.ENABLED else: replication_status = fields.ReplicationStatus.DISABLED return { 'size': size, 'snapshot_id': snapshot_id, 'source_volid': source_volid, 'volume_type': volume_type, 'volume_type_id': volume_type_id, 'encryption_key_id': encryption_key_id, 'qos_specs': specs, 'consistencygroup_id': consistencygroup_id, 'cgsnapshot_id': cgsnapshot_id, 'group_id': group_id, 'replication_status': replication_status, 'refresh_az': refresh_az, 'backup_id': backup_id, 'availability_zones': availability_zones }
def create( self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None, ): exclusive_options = (snapshot, image_id, source_volume) exclusive_options_set = sum(1 for option in exclusive_options if option is not None) if exclusive_options_set > 1: msg = _("May specify only one of snapshot, imageRef " "or source volume") raise exception.InvalidInput(reason=msg) check_policy(context, "create") if snapshot is not None: if snapshot["status"] != "available": msg = _("status must be available") raise exception.InvalidSnapshot(reason=msg) if not size: size = snapshot["volume_size"] elif size < snapshot["volume_size"]: msg = _("Volume size cannot be lesser than" " the Snapshot size") raise exception.InvalidInput(reason=msg) snapshot_id = snapshot["id"] else: snapshot_id = None if source_volume is not None: if source_volume["status"] == "error": msg = _("Unable to clone volumes that are in an error state") raise exception.InvalidSourceVolume(reason=msg) if not size: size = source_volume["size"] else: if size < source_volume["size"]: msg = _("Clones currently must be " ">= original volume size.") raise exception.InvalidInput(reason=msg) source_volid = source_volume["id"] else: source_volid = None def as_int(s): try: return int(s) except (ValueError, TypeError): return s # tolerate size as stringified int size = as_int(size) if not isinstance(size, int) or size <= 0: msg = _("Volume size '%s' must be an integer and greater than 0") % size raise exception.InvalidInput(reason=msg) if image_id and not (source_volume or snapshot): # check image existence image_meta = self.image_service.show(context, image_id) image_size_in_gb = (int(image_meta["size"]) + GB - 1) / GB # check image size is not larger than volume size. if image_size_in_gb > size: msg = _("Size of specified image is larger than volume size.") raise exception.InvalidInput(reason=msg) try: reservations = QUOTAS.reserve(context, volumes=1, gigabytes=size) except exception.OverQuota as e: overs = e.kwargs["overs"] usages = e.kwargs["usages"] quotas = e.kwargs["quotas"] def _consumed(name): return usages[name]["reserved"] + usages[name]["in_use"] if "gigabytes" in overs: msg = _( "Quota exceeded for %(s_pid)s, tried to create " "%(s_size)sG volume (%(d_consumed)dG of %(d_quota)dG " "already consumed)" ) LOG.warn( msg % { "s_pid": context.project_id, "s_size": size, "d_consumed": _consumed("gigabytes"), "d_quota": quotas["gigabytes"], } ) raise exception.VolumeSizeExceedsAvailableQuota() elif "volumes" in overs: msg = _( "Quota exceeded for %(s_pid)s, tried to create " "volume (%(d_consumed)d volumes " "already consumed)" ) LOG.warn(msg % {"s_pid": context.project_id, "d_consumed": _consumed("volumes")}) raise exception.VolumeLimitExceeded(allowed=quotas["volumes"]) if availability_zone is None: availability_zone = FLAGS.storage_availability_zone if not volume_type and not source_volume: volume_type = volume_types.get_default_volume_type() if not volume_type and source_volume: volume_type_id = source_volume["volume_type_id"] else: volume_type_id = volume_type.get("id") self._check_metadata_properties(context, metadata) options = { "size": size, "user_id": context.user_id, "project_id": context.project_id, "snapshot_id": snapshot_id, "availability_zone": availability_zone, "status": "creating", "attach_status": "detached", "display_name": name, "display_description": description, "volume_type_id": volume_type_id, "metadata": metadata, "source_volid": source_volid, } try: volume = self.db.volume_create(context, options) QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): try: self.db.volume_destroy(context, volume["id"]) finally: QUOTAS.rollback(context, reservations) request_spec = { "volume_properties": options, "volume_type": volume_type, "volume_id": volume["id"], "snapshot_id": volume["snapshot_id"], "image_id": image_id, "source_volid": volume["source_volid"], } filter_properties = {} self._cast_create_volume(context, request_spec, filter_properties) return volume
def test_volume_db_delete_last_type(self): default = volume_types.get_default_volume_type() self.assertRaises(exception.VolumeTypeDeletionError, db.volume_type_destroy, self.ctxt, default['id'])
def execute(self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, source_replica, consistencygroup, cgsnapshot): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume) policy.enforce_action(context, ACTION) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) source_replicaid = self._extract_source_replica(source_replica) size = self._extract_size(size, source_volume, snapshot) consistencygroup_id = self._extract_consistencygroup(consistencygroup) cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot) self._check_image_metadata(context, image_id, size) availability_zone = self._extract_availability_zone( availability_zone, snapshot, source_volume) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoids any dependency upon the encrypted volume type. def_vol_type = volume_types.get_default_volume_type() if not volume_type and not source_volume and not snapshot: volume_type = def_vol_type # When creating a clone of a replica (replication test), we can't # use the volume type of the replica, therefore, we use the default. # NOTE(ronenkat): this assumes the default type is not replicated. if source_replicaid: volume_type = def_vol_type volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot) if image_id and volume_types.is_encrypted(context, volume_type_id): msg = _('Create encrypted volumes with type %(type)s ' 'from image %(image)s is not supported.') msg = msg % { 'type': volume_type_id, 'image': image_id, } raise exception.InvalidInput(reason=msg) encryption_key_id = self._get_encryption_key_id( key_manager, context, volume_type_id, snapshot, source_volume) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) specs = qos_specs['qos_specs'] if not specs: # to make sure we don't pass empty dict specs = None self._check_metadata_properties(metadata) return { 'size': size, 'snapshot_id': snapshot_id, 'source_volid': source_volid, 'availability_zone': availability_zone, 'volume_type': volume_type, 'volume_type_id': volume_type_id, 'encryption_key_id': encryption_key_id, 'qos_specs': specs, 'source_replicaid': source_replicaid, 'consistencygroup_id': consistencygroup_id, 'cgsnapshot_id': cgsnapshot_id, }
def execute( self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, backup_source_volume, source_replica, consistencygroup, ): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume) policy.enforce_action(context, ACTION) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) source_replicaid = self._extract_source_replica(source_replica) size = self._extract_size(size, source_volume, snapshot) consistencygroup_id = self._extract_consistencygroup(consistencygroup) self._check_image_metadata(context, image_id, size) availability_zone = self._extract_availability_zone(availability_zone, snapshot, source_volume) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoid any dependency upon the encrypted volume type. def_vol_type = volume_types.get_default_volume_type() if not volume_type and not source_volume and not snapshot: volume_type = def_vol_type # When creating a clone of a replica (replication test), we can't # use the volume type of the replica, therefore, we use the default. # NOTE(ronenkat): this assumes the default type is not replicated. if source_replicaid: volume_type = def_vol_type volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot, backup_source_volume) encryption_key_id = self._get_encryption_key_id( key_manager, context, volume_type_id, snapshot, source_volume, backup_source_volume ) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) specs = qos_specs["qos_specs"] if not specs: # to make sure we don't pass empty dict specs = None self._check_metadata_properties(metadata) return { "size": size, "snapshot_id": snapshot_id, "source_volid": source_volid, "availability_zone": availability_zone, "volume_type": volume_type, "volume_type_id": volume_type_id, "encryption_key_id": encryption_key_id, "qos_specs": specs, "source_replicaid": source_replicaid, "consistencygroup_id": consistencygroup_id, }
def test_get_default_volume_type_under_non_default(self): cfg.CONF.set_default('default_volume_type', None) self.assertEqual({}, volume_types.get_default_volume_type())
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None): exclusive_options = (snapshot, image_id, source_volume) exclusive_options_set = sum(1 for option in exclusive_options if option is not None) if exclusive_options_set > 1: msg = (_("May specify only one of snapshot, imageRef " "or source volume")) raise exception.InvalidInput(reason=msg) check_policy(context, 'create') if snapshot is not None: if snapshot['status'] != "available": msg = _("status must be available") raise exception.InvalidSnapshot(reason=msg) if not size: size = snapshot['volume_size'] elif size < snapshot['volume_size']: msg = _("Volume size cannot be lesser than" " the Snapshot size") raise exception.InvalidInput(reason=msg) snapshot_id = snapshot['id'] else: snapshot_id = None if source_volume is not None: if source_volume['status'] == "error": msg = _("Unable to clone volumes that are in an error state") raise exception.InvalidSourceVolume(reason=msg) if not size: size = source_volume['size'] else: if size < source_volume['size']: msg = _("Clones currently must be " ">= original volume size.") raise exception.InvalidInput(reason=msg) source_volid = source_volume['id'] else: source_volid = None def as_int(s): try: return int(s) except (ValueError, TypeError): return s # tolerate size as stringified int size = as_int(size) if not isinstance(size, int) or size <= 0: msg = (_("Volume size '%s' must be an integer and greater than 0") % size) raise exception.InvalidInput(reason=msg) if (image_id and not (source_volume or snapshot)): # check image existence image_meta = self.image_service.show(context, image_id) image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB #check image size is not larger than volume size. if image_size_in_gb > size: msg = _('Size of specified image is larger than volume size.') raise exception.InvalidInput(reason=msg) try: reservations = QUOTAS.reserve(context, volumes=1, gigabytes=size) except exception.OverQuota as e: overs = e.kwargs['overs'] usages = e.kwargs['usages'] quotas = e.kwargs['quotas'] def _consumed(name): return (usages[name]['reserved'] + usages[name]['in_use']) if 'gigabytes' in overs: msg = _("Quota exceeded for %(s_pid)s, tried to create " "%(s_size)sG volume (%(d_consumed)dG of %(d_quota)dG " "already consumed)") LOG.warn(msg % {'s_pid': context.project_id, 's_size': size, 'd_consumed': _consumed('gigabytes'), 'd_quota': quotas['gigabytes']}) raise exception.VolumeSizeExceedsAvailableQuota() elif 'volumes' in overs: msg = _("Quota exceeded for %(s_pid)s, tried to create " "volume (%(d_consumed)d volumes " "already consumed)") LOG.warn(msg % {'s_pid': context.project_id, 'd_consumed': _consumed('volumes')}) raise exception.VolumeLimitExceeded(allowed=quotas['volumes']) if availability_zone is None: availability_zone = FLAGS.storage_availability_zone if not volume_type and not source_volume: volume_type = volume_types.get_default_volume_type() if not volume_type and source_volume: volume_type_id = source_volume['volume_type_id'] else: volume_type_id = volume_type.get('id') self._check_metadata_properties(context, metadata) options = {'size': size, 'user_id': context.user_id, 'project_id': context.project_id, 'snapshot_id': snapshot_id, 'availability_zone': availability_zone, 'status': "creating", 'attach_status': "detached", 'display_name': name, 'display_description': description, 'volume_type_id': volume_type_id, 'metadata': metadata, 'source_volid': source_volid} try: volume = self.db.volume_create(context, options) QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): try: self.db.volume_destroy(context, volume['id']) finally: QUOTAS.rollback(context, reservations) request_spec = {'volume_properties': options, 'volume_type': volume_type, 'volume_id': volume['id'], 'snapshot_id': volume['snapshot_id'], 'image_id': image_id, 'source_volid': volume['source_volid']} filter_properties = {} self._cast_create_volume(context, request_spec, filter_properties) return volume
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None): if ((snapshot is not None) and (source_volume is not None)): msg = (_("May specify either snapshot, " "or src volume but not both!")) raise exception.InvalidInput(reason=msg) check_policy(context, 'create') if snapshot is not None: if snapshot['status'] != "available": msg = _("status must be available") raise exception.InvalidSnapshot(reason=msg) if not size: size = snapshot['volume_size'] snapshot_id = snapshot['id'] else: snapshot_id = None if source_volume is not None: if source_volume['status'] == "error": msg = _("Unable to clone volumes that are in an error state") raise exception.InvalidSourceVolume(reason=msg) if not size: size = source_volume['size'] else: if size < source_volume['size']: msg = _("Clones currently must be " ">= original volume size.") raise exception.InvalidInput(reason=msg) source_volid = source_volume['id'] else: source_volid = None def as_int(s): try: return int(s) except (ValueError, TypeError): return s # tolerate size as stringified int size = as_int(size) if not isinstance(size, int) or size <= 0: msg = (_("Volume size '%s' must be an integer and greater than 0") % size) raise exception.InvalidInput(reason=msg) if (image_id and not (source_volume or snapshot)): # check image existence image_meta = self.image_service.show(context, image_id) image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB #check image size is not larger than volume size. if image_size_in_gb > size: msg = _('Size of specified image is larger than volume size.') raise exception.InvalidInput(reason=msg) try: reservations = QUOTAS.reserve(context, volumes=1, gigabytes=size) except exception.OverQuota as e: overs = e.kwargs['overs'] usages = e.kwargs['usages'] quotas = e.kwargs['quotas'] def _consumed(name): return (usages[name]['reserved'] + usages[name]['in_use']) if 'gigabytes' in overs: msg = _("Quota exceeded for %(s_pid)s, tried to create " "%(s_size)sG volume (%(d_consumed)dG of %(d_quota)dG " "already consumed)") LOG.warn(msg % {'s_pid': context.project_id, 's_size': size, 'd_consumed': _consumed('gigabytes'), 'd_quota': quotas['gigabytes']}) raise exception.VolumeSizeExceedsAvailableQuota() elif 'volumes' in overs: msg = _("Quota exceeded for %(s_pid)s, tried to create " "volume (%(d_consumed)d volumes" "already consumed)") LOG.warn(msg % {'s_pid': context.project_id, 'd_consumed': _consumed('volumes')}) raise exception.VolumeLimitExceeded(allowed=quotas['volumes']) if availability_zone is None: availability_zone = FLAGS.storage_availability_zone if not volume_type and not source_volume: volume_type = volume_types.get_default_volume_type() if not volume_type and source_volume: volume_type_id = source_volume['volume_type_id'] else: volume_type_id = volume_type.get('id') self._check_metadata_properties(context, metadata) options = {'size': size, 'user_id': context.user_id, 'project_id': context.project_id, 'snapshot_id': snapshot_id, 'availability_zone': availability_zone, 'status': "creating", 'attach_status': "detached", 'display_name': name, 'display_description': description, 'volume_type_id': volume_type_id, 'metadata': metadata, 'source_volid': source_volid} try: volume = self.db.volume_create(context, options) QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): try: self.db.volume_destroy(context, volume['id']) finally: QUOTAS.rollback(context, reservations) request_spec = {'volume_properties': options, 'volume_type': volume_type, 'volume_id': volume['id'], 'snapshot_id': volume['snapshot_id'], 'image_id': image_id, 'source_volid': volume['source_volid']} filter_properties = {} self._cast_create_volume(context, request_spec, filter_properties) return volume
def test_get_default_volume_type(self): """Ensures default volume type can be retrieved.""" type_ref = volume_types.create(self.ctxt, fake_flags.def_vol_type, {}) default_vol_type = volume_types.get_default_volume_type() self.assertEqual(default_vol_type.get('name'), fake_flags.def_vol_type)
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None, scheduler_hints=None, source_replica=None, consistencygroup=None, cgsnapshot=None, multiattach=False, source_cg=None): if not volume_type: volume_type = volume_types.get_default_volume_type() if self._is_lunr_volume_type(context, volume_type): # Lunr has size limits by volume type. Fail here instead of # getting an 'error' volume. self._validate_lunr_volume_type(volume_type, size) if not CONF.lunr_copy_image_enabled: image_id = None if not CONF.lunr_volume_clone_enabled: source_volume = None if snapshot: if self._is_lunr_volume_type(context, snapshot['volume_type_id']): snapshot['volume_type_id'] = volume_type['id'] if source_volume: if self._is_lunr_volume_type(context, source_volume['volume_type_id']): source_volume['volume_type_id'] = volume_type['id'] if metadata: if all (k in metadata for k in ("different_node", "different_rack")): msg = _("Cannot specify both different_node " "and different_rack metadata keys") raise exception.InvalidInput(reason=msg) kwargs = {} if snapshot is not None: kwargs['snapshot'] = snapshot if image_id is not None: kwargs['image_id'] = image_id if volume_type is not None: kwargs['volume_type'] = volume_type if metadata is not None: kwargs['metadata'] = metadata if availability_zone is not None: kwargs['availability_zone'] = availability_zone if source_volume is not None: kwargs['source_volume'] = source_volume if scheduler_hints is not None: kwargs['scheduler_hints'] = scheduler_hints if multiattach is not None: kwargs['multiattach'] = multiattach if source_replica is not None: kwargs['source_replica'] = source_replica if consistencygroup is not None: kwargs['consistencygroup'] = consistencygroup if cgsnapshot is not None: kwargs['cgsnapshot'] = cgsnapshot if source_cg is not None: kwargs['source_cg'] = source_cg return super(API, self).create(context, size, name, description, **kwargs)
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None, scheduler_hints=None): exclusive_options = (snapshot, image_id, source_volume) exclusive_options_set = sum(1 for option in exclusive_options if option is not None) if exclusive_options_set > 1: msg = (_("May specify only one of snapshot, imageRef " "or source volume")) raise exception.InvalidInput(reason=msg) check_policy(context, 'create') if snapshot is not None: if snapshot['status'] != "available": msg = _("status must be available") raise exception.InvalidSnapshot(reason=msg) if not size: size = snapshot['volume_size'] elif size < snapshot['volume_size']: msg = _("Volume size cannot be lesser than" " the Snapshot size") raise exception.InvalidInput(reason=msg) snapshot_id = snapshot['id'] else: snapshot_id = None if source_volume is not None: if source_volume['status'] == "error": msg = _("Unable to clone volumes that are in an error state") raise exception.InvalidSourceVolume(reason=msg) if not size: size = source_volume['size'] else: if size < source_volume['size']: msg = _("Clones currently must be " ">= original volume size.") raise exception.InvalidInput(reason=msg) source_volid = source_volume['id'] else: source_volid = None def as_int(s): try: return int(s) except (ValueError, TypeError): return s # tolerate size as stringified int size = as_int(size) if not isinstance(size, int) or size <= 0: msg = (_("Volume size '%s' must be an integer and greater than 0") % size) raise exception.InvalidInput(reason=msg) if (image_id and not (source_volume or snapshot)): # check image existence image_meta = self.image_service.show(context, image_id) image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB #check image size is not larger than volume size. if image_size_in_gb > size: msg = _('Size of specified image is larger than volume size.') raise exception.InvalidInput(reason=msg) # Check image minDisk requirement is met for the particular volume if size < image_meta.get('min_disk', 0): msg = _('Image minDisk size is larger than the volume size.') raise exception.InvalidInput(reason=msg) if availability_zone is None: if snapshot is not None: availability_zone = snapshot['volume']['availability_zone'] elif source_volume is not None: availability_zone = source_volume['availability_zone'] else: availability_zone = CONF.storage_availability_zone else: self._check_availabilty_zone(availability_zone) if CONF.cloned_volume_same_az: if (snapshot and snapshot['volume']['availability_zone'] != availability_zone): msg = _("Volume must be in the same " "availability zone as the snapshot") raise exception.InvalidInput(reason=msg) elif source_volume and \ source_volume['availability_zone'] != availability_zone: msg = _("Volume must be in the same " "availability zone as the source volume") raise exception.InvalidInput(reason=msg) if not volume_type and not source_volume: volume_type = volume_types.get_default_volume_type() if not volume_type and source_volume: volume_type_id = source_volume['volume_type_id'] else: volume_type_id = volume_type.get('id') try: reserve_opts = {'volumes': 1, 'gigabytes': size} QUOTAS.add_volume_type_opts(context, reserve_opts, volume_type_id) reservations = QUOTAS.reserve(context, **reserve_opts) except exception.OverQuota as e: overs = e.kwargs['overs'] usages = e.kwargs['usages'] quotas = e.kwargs['quotas'] def _consumed(name): return (usages[name]['reserved'] + usages[name]['in_use']) for over in overs: if 'gigabytes' in over: msg = _("Quota exceeded for %(s_pid)s, tried to create " "%(s_size)sG volume (%(d_consumed)dG of " "%(d_quota)dG already consumed)") LOG.warn(msg % {'s_pid': context.project_id, 's_size': size, 'd_consumed': _consumed(over), 'd_quota': quotas[over]}) raise exception.VolumeSizeExceedsAvailableQuota() elif 'volumes' in over: msg = _("Quota exceeded for %(s_pid)s, tried to create " "volume (%(d_consumed)d volumes" "already consumed)") LOG.warn(msg % {'s_pid': context.project_id, 'd_consumed': _consumed(over)}) raise exception.VolumeLimitExceeded(allowed=quotas[over]) self._check_metadata_properties(context, metadata) options = {'size': size, 'user_id': context.user_id, 'project_id': context.project_id, 'snapshot_id': snapshot_id, 'availability_zone': availability_zone, 'status': "creating", 'attach_status': "detached", 'display_name': name, 'display_description': description, 'volume_type_id': volume_type_id, 'metadata': metadata, 'source_volid': source_volid} try: volume = self.db.volume_create(context, options) QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): try: self.db.volume_destroy(context, volume['id']) finally: QUOTAS.rollback(context, reservations) request_spec = {'volume_properties': options, 'volume_type': volume_type, 'volume_id': volume['id'], 'snapshot_id': volume['snapshot_id'], 'image_id': image_id, 'source_volid': volume['source_volid']} if scheduler_hints: filter_properties = {'scheduler_hints': scheduler_hints} else: filter_properties = {} self._cast_create_volume(context, request_spec, filter_properties) return volume
def execute(self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, source_replica, consistencygroup, cgsnapshot, group): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume) policy.enforce_action(context, ACTION) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) source_replicaid = self._extract_source_replica(source_replica) size = self._extract_size(size, source_volume, snapshot) consistencygroup_id = self._extract_consistencygroup(consistencygroup) cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot) group_id = self._extract_group(group) image_meta = self._get_image_metadata(context, image_id, size) availability_zone, refresh_az = self._extract_availability_zone( availability_zone, snapshot, source_volume, group) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoids any dependency upon the encrypted volume type. def_vol_type = volume_types.get_default_volume_type() if not volume_type and not source_volume and not snapshot: image_volume_type = self._get_image_volume_type(context, image_id) volume_type = (image_volume_type if image_volume_type else def_vol_type) # When creating a clone of a replica (replication test), we can't # use the volume type of the replica, therefore, we use the default. # NOTE(ronenkat): this assumes the default type is not replicated. if source_replicaid: volume_type = def_vol_type volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot) encryption_key_id = self._get_encryption_key_id( key_manager, context, volume_type_id, snapshot, source_volume, image_meta) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) if qos_specs['qos_specs']: specs = qos_specs['qos_specs'].get('specs', {}) # Determine default replication status extra_specs = volume_types.get_volume_type_extra_specs( volume_type_id) if not specs: # to make sure we don't pass empty dict specs = None extra_specs = None if vol_utils.is_replicated_spec(extra_specs): replication_status = fields.ReplicationStatus.ENABLED else: replication_status = fields.ReplicationStatus.DISABLED return { 'size': size, 'snapshot_id': snapshot_id, 'source_volid': source_volid, 'availability_zone': availability_zone, 'volume_type': volume_type, 'volume_type_id': volume_type_id, 'encryption_key_id': encryption_key_id, 'qos_specs': specs, 'source_replicaid': source_replicaid, 'consistencygroup_id': consistencygroup_id, 'cgsnapshot_id': cgsnapshot_id, 'group_id': group_id, 'replication_status': replication_status, 'refresh_az': refresh_az }
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None): check_policy(context, 'create') if snapshot is not None: if snapshot['status'] != "available": msg = _("status must be available") raise exception.InvalidSnapshot(reason=msg) if not size: size = snapshot['volume_size'] snapshot_id = snapshot['id'] else: snapshot_id = None def as_int(s): try: return int(s) except ValueError: return s # tolerate size as stringified int size = as_int(size) if not isinstance(size, int) or size <= 0: msg = (_("Volume size '%s' must be an integer and greater than 0") % size) raise exception.InvalidInput(reason=msg) if image_id: # check image existence image_meta = self.image_service.show(context, image_id) image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB #check image size is not larger than volume size. if image_size_in_gb > size: msg = _('Size of specified image is larger than volume size.') raise exception.InvalidInput(reason=msg) try: reservations = QUOTAS.reserve(context, volumes=1, gigabytes=size) except exception.OverQuota as e: overs = e.kwargs['overs'] usages = e.kwargs['usages'] quotas = e.kwargs['quotas'] def _consumed(name): return (usages[name]['reserved'] + usages[name]['in_use']) pid = context.project_id if 'gigabytes' in overs: consumed = _consumed('gigabytes') quota = quotas['gigabytes'] LOG.warn(_("Quota exceeded for %(pid)s, tried to create " "%(size)sG volume (%(consumed)dG of %(quota)dG " "already consumed)") % locals()) raise exception.VolumeSizeExceedsAvailableQuota() elif 'volumes' in overs: consumed = _consumed('volumes') LOG.warn(_("Quota exceeded for %(pid)s, tried to create " "volume (%(consumed)d volumes already consumed)") % locals()) raise exception.VolumeLimitExceeded(allowed=quotas['volumes']) if availability_zone is None: availability_zone = FLAGS.storage_availability_zone if not volume_type: volume_type = volume_types.get_default_volume_type() volume_type_id = volume_type.get('id') options = { 'size': size, 'user_id': context.user_id, 'project_id': context.project_id, 'snapshot_id': snapshot_id, 'availability_zone': availability_zone, 'status': "creating", 'attach_status': "detached", 'display_name': name, 'display_description': description, 'volume_type_id': volume_type_id, 'metadata': metadata, } try: volume = self.db.volume_create(context, options) QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): try: self.db.volume_destroy(context, volume['id']) finally: QUOTAS.rollback(context, reservations) request_spec = { 'volume_properties': options, 'volume_type': volume_type, 'volume_id': volume['id'], 'snapshot_id': volume['snapshot_id'], 'image_id': image_id } filter_properties = {} self._cast_create_volume(context, request_spec, filter_properties) return volume
def test_default_volume_type_missing_in_db(self): """Ensures proper exception raised if default volume type is not in database. """ default_vol_type = volume_types.get_default_volume_type() self.assertEqual(default_vol_type, {})
def create(self, req, body): """Creates a new volume.""" LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] # NOTE (pooja_jadhav) To fix bug 1774155, scheduler hints is not # loaded as a standard extension. If user passes # OS-SCH-HNT:scheduler_hints in the request body, then it will be # validated in the create method and this method will add # scheduler_hints in body['volume']. body = scheduler_hints.create(req, body) volume = body['volume'] kwargs = {} self.validate_name_and_description(volume, check_length=False) # NOTE(thingee): v2 API allows name instead of display_name if 'name' in volume: volume['display_name'] = volume.pop('name') # NOTE(thingee): v2 API allows description instead of # display_description if 'description' in volume: volume['display_description'] = volume.pop('description') if 'image_id' in volume: volume['imageRef'] = volume.pop('image_id') req_volume_type = volume.get('volume_type', None) if req_volume_type: # Not found exception will be handled at the wsgi level kwargs['volume_type'] = (objects.VolumeType.get_by_name_or_id( context, req_volume_type)) else: kwargs['volume_type'] = (objects.VolumeType.get_by_name_or_id( context, volume_types.get_default_volume_type()['id'])) kwargs['metadata'] = volume.get('metadata', None) snapshot_id = volume.get('snapshot_id') if snapshot_id is not None: # Not found exception will be handled at the wsgi level kwargs['snapshot'] = self.volume_api.get_snapshot( context, snapshot_id) else: kwargs['snapshot'] = None source_volid = volume.get('source_volid') if source_volid is not None: # Not found exception will be handled at the wsgi level kwargs['source_volume'] = \ self.volume_api.get_volume(context, source_volid) else: kwargs['source_volume'] = None kwargs['group'] = None kwargs['consistencygroup'] = None consistencygroup_id = volume.get('consistencygroup_id') if consistencygroup_id is not None: # Not found exception will be handled at the wsgi level kwargs['group'] = self.group_api.get(context, consistencygroup_id) size = volume.get('size', None) if size is None and kwargs['snapshot'] is not None: size = kwargs['snapshot']['volume_size'] elif size is None and kwargs['source_volume'] is not None: size = kwargs['source_volume']['size'] LOG.info("Create volume of %s GB", size) image_ref = volume.get('imageRef') if image_ref is not None: image_uuid = self._image_uuid_from_ref(image_ref, context) kwargs['image_id'] = image_uuid kwargs['availability_zone'] = volume.get('availability_zone', None) kwargs['scheduler_hints'] = volume.get('scheduler_hints', None) kwargs['multiattach'] = utils.get_bool_param('multiattach', volume) if kwargs.get('multiattach', False): msg = ("The option 'multiattach' " "is deprecated and will be removed in a future " "release. The default behavior going forward will " "be to specify multiattach enabled volume types.") versionutils.report_deprecated_feature(LOG, msg) new_volume = self.volume_api.create(context, size, volume.get('display_name'), volume.get('display_description'), **kwargs) retval = self._view_builder.detail(req, new_volume) return retval
def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None, scheduler_hints=None, source_replica=None, consistencygroup=None, cgsnapshot=None, multiattach=False, source_cg=None): if not volume_type: volume_type = volume_types.get_default_volume_type() if self._is_lunr_volume_type(context, volume_type): # Lunr has size limits by volume type. Fail here instead of # getting an 'error' volume. self._validate_lunr_volume_type(volume_type, size) if not CONF.lunr_copy_image_enabled: image_id = None if not CONF.lunr_volume_clone_enabled: source_volume = None if snapshot: if self._is_lunr_volume_type(context, snapshot['volume_type_id']): snapshot['volume_type_id'] = volume_type['id'] if source_volume: # validate if source is in use if self._is_lunr_volume_type(context, source_volume['volume_type_id']): source_volume['volume_type_id'] = volume_type['id'] if metadata: if all(k in metadata for k in ("different_node", "different_rack")): msg = _("Cannot specify both different_node " "and different_rack metadata keys") raise exception.InvalidInput(reason=msg) kwargs = {} if snapshot is not None: kwargs['snapshot'] = snapshot if image_id is not None: kwargs['image_id'] = image_id if volume_type is not None: kwargs['volume_type'] = volume_type if metadata is not None: kwargs['metadata'] = metadata if availability_zone is not None: kwargs['availability_zone'] = availability_zone if source_volume is not None: kwargs['source_volume'] = source_volume if scheduler_hints is not None: kwargs['scheduler_hints'] = scheduler_hints if multiattach is not None: kwargs['multiattach'] = multiattach if source_replica is not None: kwargs['source_replica'] = source_replica if consistencygroup is not None: kwargs['consistencygroup'] = consistencygroup if cgsnapshot is not None: kwargs['cgsnapshot'] = cgsnapshot if source_cg is not None: kwargs['source_cg'] = source_cg if source_volume is not None: LOG.info("Finding on going operations on source volume %s." % source_volume) siblings = self.db.snapshot_get_all_for_volume( context, source_volume["id"]) in_progess_snapshots = [ snapshot for snapshot in siblings if snapshot['status'] == 'creating' ] if in_progess_snapshots: raise SnapshotConflict(reason="Snapshot conflict", volume_id=source_volume["id"]) self._check_clone_conflict(context, volume_id=source_volume["id"]) return super(API, self).create(context, size, name, description, **kwargs)
def execute(self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, consistencygroup, cgsnapshot, group): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume) policy.enforce_action(context, ACTION) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) size = self._extract_size(size, source_volume, snapshot) consistencygroup_id = self._extract_consistencygroup(consistencygroup) cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot) group_id = self._extract_group(group) image_meta = self._get_image_metadata(context, image_id, size) availability_zone, refresh_az = self._extract_availability_zone( availability_zone, snapshot, source_volume, group) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoids any dependency upon the encrypted volume type. def_vol_type = volume_types.get_default_volume_type() if not volume_type and not source_volume and not snapshot: image_volume_type = self._get_image_volume_type(context, image_id) volume_type = (image_volume_type if image_volume_type else def_vol_type) volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot) encryption_key_id = self._get_encryption_key_id( key_manager, context, volume_type_id, snapshot, source_volume, image_meta) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) if qos_specs['qos_specs']: specs = qos_specs['qos_specs'].get('specs', {}) # Determine default replication status extra_specs = volume_types.get_volume_type_extra_specs( volume_type_id) if not specs: # to make sure we don't pass empty dict specs = None extra_specs = None if vol_utils.is_replicated_spec(extra_specs): replication_status = fields.ReplicationStatus.ENABLED else: replication_status = fields.ReplicationStatus.DISABLED return { 'size': size, 'snapshot_id': snapshot_id, 'source_volid': source_volid, 'availability_zone': availability_zone, 'volume_type': volume_type, 'volume_type_id': volume_type_id, 'encryption_key_id': encryption_key_id, 'qos_specs': specs, 'consistencygroup_id': consistencygroup_id, 'cgsnapshot_id': cgsnapshot_id, 'group_id': group_id, 'replication_status': replication_status, 'refresh_az': refresh_az }
def setUp(self): """Run before each test method to initialize test environment.""" super(TestCase, self).setUp() # Create default notifier self.notifier = fake_notifier.get_fake_notifier() # Mock rpc get notifier with fake notifier method that joins all # notifications with the default notifier self.patch('cinder.rpc.get_notifier', side_effect=self._get_joined_notifier) # Protect against any case where someone doesn't directly patch a retry # decorated call. self.patch('tenacity.nap.sleep') if self.MOCK_WORKER: # Mock worker creation for all tests that don't care about it clean_path = 'cinder.objects.cleanable.CinderCleanableObject.%s' for method in ('create_worker', 'set_worker', 'unset_worker'): self.patch(clean_path % method, return_value=None) if self.MOCK_TOOZ: self.patch('cinder.coordination.Coordinator.start') self.patch('cinder.coordination.Coordinator.stop') self.patch('cinder.coordination.Coordinator.get_lock') # Unit tests do not need to use lazy gettext i18n.enable_lazy(False) test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) except ValueError: # If timeout value is invalid do not set a timeout. test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) self.useFixture(fixtures.NestedTempfile()) self.useFixture(fixtures.TempHomeDir()) environ_enabled = (lambda var_name: strutils.bool_from_string( os.environ.get(var_name))) if environ_enabled('OS_STDOUT_CAPTURE'): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if environ_enabled('OS_STDERR_CAPTURE'): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) self.useFixture(log_fixture.get_logging_handle_error_fixture()) self.useFixture(cinder_fixtures.StandardLogging()) rpc.add_extra_exmods("cinder.tests.unit") self.addCleanup(rpc.clear_extra_exmods) self.addCleanup(rpc.cleanup) self.messaging_conf = messaging_conffixture.ConfFixture(CONF) self.messaging_conf.transport_url = 'fake:/' self.messaging_conf.response_timeout = 15 self.useFixture(self.messaging_conf) # Load oslo_messaging_notifications config group so we can set an # override to prevent notifications from being ignored due to the # short-circuit mechanism. oslo_messaging.get_notification_transport(CONF) # We need to use a valid driver for the notifications, so we use test. self.override_config('driver', ['test'], group='oslo_messaging_notifications') rpc.init(CONF) # NOTE(geguileo): This is required because _determine_obj_version_cap # and _determine_rpc_version_cap functions in cinder.rpc.RPCAPI cache # versions in LAST_RPC_VERSIONS and LAST_OBJ_VERSIONS so we may have # weird interactions between tests if we don't clear them before each # test. rpc.LAST_OBJ_VERSIONS = {} rpc.LAST_RPC_VERSIONS = {} # Init AuthProtocol to register some base options first, such as # auth_url. auth_token.AuthProtocol('fake_app', { 'auth_type': 'password', 'auth_url': 'fake_url' }) conf_fixture.set_defaults(CONF) CONF([], default_config_files=[]) # NOTE(vish): We need a better method for creating fixtures for tests # now that we have some required db setup for the system # to work properly. self.start = timeutils.utcnow() CONF.set_default('connection', 'sqlite://', 'database') CONF.set_default('sqlite_synchronous', False, 'database') global _DB_CACHE if not _DB_CACHE: _DB_CACHE = Database(sqla_api, migration, sql_connection=CONF.database.connection) self.useFixture(_DB_CACHE) # NOTE(blk-u): WarningsFixture must be after the Database fixture # because sqlalchemy-migrate messes with the warnings filters. self.useFixture(cinder_fixtures.WarningsFixture()) # NOTE(danms): Make sure to reset us back to non-remote objects # for each test to avoid interactions. Also, backup the object # registry. objects_base.CinderObject.indirection_api = None self._base_test_obj_backup = copy.copy( objects_base.CinderObjectRegistry._registry._obj_classes) self.addCleanup(self._restore_obj_registry) self.addCleanup(CONF.reset) self.addCleanup(self._common_cleanup) self.injected = [] self._services = [] fake_notifier.mock_notifier(self) # This will be cleaned up by the NestedTempfile fixture lock_path = self.useFixture(fixtures.TempDir()).path self.fixture = self.useFixture(config_fixture.Config(lockutils.CONF)) self.fixture.config(lock_path=lock_path, group='oslo_concurrency') lockutils.set_defaults(lock_path) self.override_config('policy_file', os.path.join( os.path.abspath(os.path.dirname(__file__)), self.POLICY_PATH), group='oslo_policy') self.override_config('resource_query_filters_file', self.RESOURCE_FILTER_PATH) self._disable_osprofiler() # NOTE(geguileo): This is required because common get_by_id method in # cinder.db.sqlalchemy.api caches get methods and if we use a mocked # get method in one test it would carry on to the next test. So we # clear out the cache. sqla_api._GET_METHODS = {} self.override_config('backend_url', 'file://' + lock_path, group='coordination') coordination.COORDINATOR.start() self.addCleanup(coordination.COORDINATOR.stop) if six.PY3: # TODO(smcginnis) Python 3 deprecates assertRaisesRegexp to # assertRaisesRegex, but Python 2 does not have the new name. This # can be removed once we stop supporting py2 or the new name is # added. self.assertRaisesRegexp = self.assertRaisesRegex # Ensure we have the default tpool size value and we don't carry # threads from other test runs. tpool.killall() tpool._nthreads = 20 # NOTE(mikal): make sure we don't load a privsep helper accidentally self.useFixture(cinder_fixtures.PrivsepNoHelperFixture()) # NOTE: This volume type is created to avoid failure at database since # volume_type_id is non-nullable for volumes and snapshots self.vt = volume_types.get_default_volume_type()
def test_get_default_volume_type(self): """Ensures default volume type can be retrieved.""" volume_types.create(self.ctxt, conf_fixture.def_vol_type, {}) default_vol_type = volume_types.get_default_volume_type() self.assertEqual(conf_fixture.def_vol_type, default_vol_type.get('name'))
def execute(self, context, size, snapshot, image_id, source_volume, availability_zone, volume_type, metadata, key_manager, source_replica, consistencygroup, cgsnapshot): utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume) policy.enforce_action(context, ACTION) # TODO(harlowja): what guarantee is there that the snapshot or source # volume will remain available after we do this initial verification?? snapshot_id = self._extract_snapshot(snapshot) source_volid = self._extract_source_volume(source_volume) source_replicaid = self._extract_source_replica(source_replica) size = self._extract_size(size, source_volume, snapshot) consistencygroup_id = self._extract_consistencygroup(consistencygroup) cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot) self._check_image_metadata(context, image_id, size) availability_zone = self._extract_availability_zone(availability_zone, snapshot, source_volume) # TODO(joel-coffman): This special handling of snapshots to ensure that # their volume type matches the source volume is too convoluted. We # should copy encryption metadata from the encrypted volume type to the # volume upon creation and propagate that information to each snapshot. # This strategy avoids any dependency upon the encrypted volume type. def_vol_type = volume_types.get_default_volume_type() if not volume_type and not source_volume and not snapshot: image_volume_type = self._get_image_volume_type(context, image_id) volume_type = (image_volume_type if image_volume_type else def_vol_type) # When creating a clone of a replica (replication test), we can't # use the volume type of the replica, therefore, we use the default. # NOTE(ronenkat): this assumes the default type is not replicated. if source_replicaid: volume_type = def_vol_type volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot) if image_id and volume_types.is_encrypted(context, volume_type_id): msg = _('Create encrypted volumes with type %(type)s ' 'from image %(image)s is not supported.') msg = msg % {'type': volume_type_id, 'image': image_id, } raise exception.InvalidInput(reason=msg) encryption_key_id = self._get_encryption_key_id(key_manager, context, volume_type_id, snapshot, source_volume) specs = {} if volume_type_id: qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) if qos_specs['qos_specs']: specs = qos_specs['qos_specs'].get('specs', {}) if not specs: # to make sure we don't pass empty dict specs = None utils.check_metadata_properties(metadata) return { 'size': size, 'snapshot_id': snapshot_id, 'source_volid': source_volid, 'availability_zone': availability_zone, 'volume_type': volume_type, 'volume_type_id': volume_type_id, 'encryption_key_id': encryption_key_id, 'qos_specs': specs, 'source_replicaid': source_replicaid, 'consistencygroup_id': consistencygroup_id, 'cgsnapshot_id': cgsnapshot_id, }
def create(self, context, name, description, cg_volume_types=None, availability_zone=None): check_policy(context, 'create') volume_type_list = None if cg_volume_types: volume_type_list = cg_volume_types.split(',') req_volume_types = [] if volume_type_list: req_volume_types = (self.db.volume_types_get_by_name_or_id( context, volume_type_list)) if not req_volume_types: volume_type = volume_types.get_default_volume_type() req_volume_types.append(volume_type) req_volume_type_ids = "" for voltype in req_volume_types: if voltype: req_volume_type_ids = (req_volume_type_ids + voltype.get('id') + ",") if len(req_volume_type_ids) == 0: req_volume_type_ids = None availability_zone = self._extract_availability_zone(availability_zone) options = { 'user_id': context.user_id, 'project_id': context.project_id, 'availability_zone': availability_zone, 'status': "creating", 'name': name, 'description': description, 'volume_type_id': req_volume_type_ids } group = None try: group = self.db.consistencygroup_create(context, options) except Exception: with excutils.save_and_reraise_exception(): LOG.error( _("Error occurred when creating consistency group" " %s."), name) request_spec_list = [] filter_properties_list = [] for req_volume_type in req_volume_types: request_spec = { 'volume_type': req_volume_type.copy(), 'consistencygroup_id': group['id'] } filter_properties = {} request_spec_list.append(request_spec) filter_properties_list.append(filter_properties) # Update quota for consistencygroups self.update_quota(context, group['id']) self._cast_create_consistencygroup(context, group['id'], request_spec_list, filter_properties_list) return group