def test_provision_filter_on_size(self): volume_types.create(self.ctxt, "type1", { "key1": "val1", "key2": "val2" }) volume_types.create(self.ctxt, "type2", { volume_types.MIN_SIZE_KEY: "12", "key3": "val3" }) volume_types.create(self.ctxt, "type3", { volume_types.MAX_SIZE_KEY: "99", "key4": "val4" }) volume_types.create( self.ctxt, "type4", { volume_types.MIN_SIZE_KEY: "24", volume_types.MAX_SIZE_KEY: "99", "key4": "val4" }) # Make sure we don't raise if there are no min/max set type1 = volume_types.get_by_name_or_id(self.ctxt, 'type1') volume_types.provision_filter_on_size(self.ctxt, type1, "11") # verify minimum size requirements type2 = volume_types.get_by_name_or_id(self.ctxt, 'type2') self.assertRaises(exception.InvalidInput, volume_types.provision_filter_on_size, self.ctxt, type2, "11") volume_types.provision_filter_on_size(self.ctxt, type2, "12") volume_types.provision_filter_on_size(self.ctxt, type2, "100") # verify max size requirements type3 = volume_types.get_by_name_or_id(self.ctxt, 'type3') self.assertRaises(exception.InvalidInput, volume_types.provision_filter_on_size, self.ctxt, type3, "100") volume_types.provision_filter_on_size(self.ctxt, type3, "99") volume_types.provision_filter_on_size(self.ctxt, type3, "1") # verify min and max type4 = volume_types.get_by_name_or_id(self.ctxt, 'type4') self.assertRaises(exception.InvalidInput, volume_types.provision_filter_on_size, self.ctxt, type4, "20") self.assertRaises(exception.InvalidInput, volume_types.provision_filter_on_size, self.ctxt, type4, "130") volume_types.provision_filter_on_size(self.ctxt, type4, "24") volume_types.provision_filter_on_size(self.ctxt, type4, "99") volume_types.provision_filter_on_size(self.ctxt, type4, "30")
def get_by_name_or_id(cls, context, identity): orm_obj = volume_types.get_by_name_or_id(context, identity) expected_attrs = cls._get_expected_attrs(context) return cls._from_db_object(context, cls(context), orm_obj, expected_attrs=expected_attrs)
def test_get_volume_type_by_name_with_uuid_name(self): """Ensure volume types can be created and found.""" uuid_format_name = uuidutils.generate_uuid() volume_types.create(self.ctxt, uuid_format_name, self.vol_type1_specs, description=self.vol_type1_description) type_ref = volume_types.get_by_name_or_id(self.ctxt, uuid_format_name) self.assertEqual(uuid_format_name, type_ref['name'])
def get_pools(self, context, filters=None): """Returns a dict of all pools on all hosts HostManager knows about.""" self._update_backend_state_map(context) all_pools = {} name = volume_type = None if filters: name = filters.pop('name', None) volume_type = filters.pop('volume_type', None) for backend_key, state in self.backend_state_map.items(): for key in state.pools: filtered = False pool = state.pools[key] # use backend_key.pool_name to make sure key is unique pool_key = volume_utils.append_host(backend_key, pool.pool_name) new_pool = dict(name=pool_key) new_pool.update(dict(capabilities=pool.capabilities)) if name and new_pool.get('name') != name: continue if filters: # filter all other items in capabilities for (attr, value) in filters.items(): cap = new_pool.get('capabilities').get(attr) if not self._equal_after_convert(cap, value): filtered = True break if not filtered: all_pools[pool_key] = pool # filter pools by volume type if volume_type: volume_type = volume_types.get_by_name_or_id(context, volume_type) all_pools = (self._filter_pools_by_volume_type( context, volume_type, all_pools)) # encapsulate pools in format:{name: XXX, capabilities: XXX} return [ dict(name=key, capabilities=value.capabilities) for key, value in all_pools.items() ]
def get_pools(self, context, filters=None): """Returns a dict of all pools on all hosts HostManager knows about.""" self._update_backend_state_map(context) all_pools = {} name = volume_type = None if filters: name = filters.pop('name', None) volume_type = filters.pop('volume_type', None) for backend_key, state in self.backend_state_map.items(): for key in state.pools: filtered = False pool = state.pools[key] # use backend_key.pool_name to make sure key is unique pool_key = vol_utils.append_host(backend_key, pool.pool_name) new_pool = dict(name=pool_key) new_pool.update(dict(capabilities=pool.capabilities)) if name and new_pool.get('name') != name: continue if filters: # filter all other items in capabilities for (attr, value) in filters.items(): cap = new_pool.get('capabilities').get(attr) if not self._equal_after_convert(cap, value): filtered = True break if not filtered: all_pools[pool_key] = pool # filter pools by volume type if volume_type: volume_type = volume_types.get_by_name_or_id( context, volume_type) all_pools = ( self._filter_pools_by_volume_type(context, volume_type, all_pools)) # encapsulate pools in format:{name: XXX, capabilities: XXX} return [dict(name=key, capabilities=value.capabilities) for key, value in all_pools.items()]
def setUp(self): super(VolumeTypesApiTest, self).setUp() self.controller = types.VolumeTypesController() self.ctxt = context.RequestContext(user_id=fake.USER_ID, project_id=fake.PROJECT_ID, is_admin=True) self.mock_authorize = self.patch( 'cinder.context.RequestContext.authorize') # since __DEFAULT__ type always exists, total number of volume types # is total_types_created + 1. In this case it's 4 self.type_id1 = self._create_volume_type('volume_type1', {'key1': 'value1'}) self.type_id2 = self._create_volume_type('volume_type2', {'key2': 'value2'}) self.type_id3 = self._create_volume_type('volume_type3', {'key3': 'value3'}, False, [fake.PROJECT_ID]) self.default_type = volume_types.get_default_volume_type()['id'] self.vol_type = volume_types.get_by_name_or_id( context.get_admin_context(), '__DEFAULT__')['id']
def create(self, req, body): """Instruct Cinder to manage a storage object. Manages an existing backend storage object (e.g. a Linux logical volume or a SAN disk) by creating the Cinder objects required to manage it, and possibly renaming the backend storage object (driver dependent) From an API perspective, this operation behaves very much like a volume creation operation, except that properties such as image, snapshot and volume references don't make sense, because we are taking an existing storage object into Cinder management. Required HTTP Body: .. code-block:: json { "volume": { "host": "<Cinder host on which the existing storage resides>", "cluster": "<Cinder cluster on which the storage resides>", "ref": "<Driver-specific reference to existing storage object>" } } See the appropriate Cinder drivers' implementations of the manage_volume method to find out the accepted format of 'ref'. This API call will return with an error if any of the above elements are missing from the request, or if the 'host' element refers to a cinder host that is not registered. The volume will later enter the error state if it is discovered that 'ref' is bad. Optional elements to 'volume' are:: name A name for the new volume. description A description for the new volume. volume_type ID or name of a volume type to associate with the new Cinder volume. Does not necessarily guarantee that the managed volume will have the properties described in the volume_type. The driver may choose to fail if it identifies that the specified volume_type is not compatible with the backend storage object. metadata Key/value pairs to be associated with the new volume. availability_zone The availability zone to associate with the new volume. bootable If set to True, marks the volume as bootable. """ context = req.environ['cinder.context'] context.authorize(policy.MANAGE_POLICY) self.assert_valid_body(body, 'volume') volume = body['volume'] self.validate_name_and_description(volume) # Check that the required keys are present, return an error if they # are not. if 'ref' not in volume: raise exception.MissingRequired(element='ref') cluster_name, host = common.get_cluster_host(req, volume, mv.VOLUME_MIGRATE_CLUSTER) LOG.debug('Manage volume request body: %s', body) kwargs = {} req_volume_type = volume.get('volume_type', None) if req_volume_type: try: kwargs['volume_type'] = volume_types.get_by_name_or_id( context, req_volume_type) except exception.VolumeTypeNotFound: msg = _("Cannot find requested '%s' " "volume type") % req_volume_type raise exception.InvalidVolumeType(reason=msg) else: kwargs['volume_type'] = {} kwargs['name'] = volume.get('name', None) kwargs['description'] = volume.get('description', None) kwargs['metadata'] = volume.get('metadata', None) kwargs['availability_zone'] = volume.get('availability_zone', None) kwargs['bootable'] = utils.get_bool_param('bootable', volume) utils.check_metadata_properties(kwargs['metadata']) try: new_volume = self.volume_api.manage_existing( context, host, cluster_name, volume['ref'], **kwargs) except exception.ServiceNotFound: msg = _("%(name)s '%(value)s' not found") % { 'name': 'Host' if host else 'Cluster', 'value': host or cluster_name } raise exception.ServiceUnavailable(message=msg) utils.add_visible_admin_metadata(new_volume) return self._view_builder.detail(req, new_volume)
def create(self, req, body): """Creates a new volume. :param req: the request :param body: the request body :returns: dict -- the new volume dictionary :raises: HTTPNotFound, HTTPBadRequest """ self.assert_valid_body(body, 'volume') LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] req_version = req.api_version_request # Remove group_id from body if max version is less than 3.13. if req_version.matches(None, "3.12"): # NOTE(xyang): The group_id is from a group created with a # group_type. So with this group_id, we've got a group_type # for this volume. Also if group_id is passed in, that means # we already know which backend is hosting the group and the # volume will be created on the same backend as well. So it # won't go through the scheduler again if a group_id is # passed in. try: body.get('volume', {}).pop('group_id', None) except AttributeError: msg = (_("Invalid body provided for creating volume. " "Request API version: %s.") % req_version) raise exc.HTTPBadRequest(explanation=msg) volume = body['volume'] kwargs = {} self.validate_name_and_description(volume) # 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'] = (volume_types.get_by_name_or_id( context, req_volume_type)) 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 source_replica = volume.get('source_replica') if source_replica is not None: # Not found exception will be handled at the wsgi level src_vol = self.volume_api.get_volume(context, source_replica) if src_vol['replication_status'] == 'disabled': explanation = _('source volume id:%s is not' ' replicated') % source_replica raise exc.HTTPBadRequest(explanation=explanation) kwargs['source_replica'] = src_vol else: kwargs['source_replica'] = None consistencygroup_id = volume.get('consistencygroup_id') if consistencygroup_id is not None: # Not found exception will be handled at the wsgi level kwargs['consistencygroup'] = (self.consistencygroup_api.get( context, consistencygroup_id)) else: kwargs['consistencygroup'] = None # Get group_id if volume is in a group. group_id = volume.get('group_id') if group_id is not None: try: kwargs['group'] = self.group_api.get(context, group_id) except exception.GroupNotFound as error: raise exc.HTTPNotFound(explanation=error.msg) 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'] elif size is None and kwargs['source_replica'] is not None: size = kwargs['source_replica']['size'] LOG.info(_LI("Create volume of %s GB"), size) if self.ext_mgr.is_loaded('os-image-create'): 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) multiattach = volume.get('multiattach', False) kwargs['multiattach'] = multiattach 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, req, body): """Creates a new volume. :param req: the request :param body: the request body :returns: dict -- the new volume dictionary :raises: HTTPNotFound, HTTPBadRequest """ self.assert_valid_body(body, 'volume') LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] req_version = req.api_version_request # Remove group_id from body if max version is less than 3.13. if req_version.matches(None, "3.12"): # NOTE(xyang): The group_id is from a group created with a # group_type. So with this group_id, we've got a group_type # for this volume. Also if group_id is passed in, that means # we already know which backend is hosting the group and the # volume will be created on the same backend as well. So it # won't go through the scheduler again if a group_id is # passed in. try: body.get('volume', {}).pop('group_id', None) except AttributeError: msg = (_("Invalid body provided for creating volume. " "Request API version: %s.") % req_version) raise exc.HTTPBadRequest(explanation=msg) volume = body['volume'] kwargs = {} self.validate_name_and_description(volume) # 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'] = ( volume_types.get_by_name_or_id(context, req_volume_type)) kwargs['metadata'] = volume.get('metadata', None) snapshot_id = volume.get('snapshot_id') if snapshot_id is not None: if not uuidutils.is_uuid_like(snapshot_id): msg = _("Snapshot ID must be in UUID form.") raise exc.HTTPBadRequest(explanation=msg) # 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 source_replica = volume.get('source_replica') if source_replica is not None: # Not found exception will be handled at the wsgi level src_vol = self.volume_api.get_volume(context, source_replica) if src_vol['replication_status'] == 'disabled': explanation = _('source volume id:%s is not' ' replicated') % source_replica raise exc.HTTPBadRequest(explanation=explanation) kwargs['source_replica'] = src_vol else: kwargs['source_replica'] = None consistencygroup_id = volume.get('consistencygroup_id') if consistencygroup_id is not None: # Not found exception will be handled at the wsgi level kwargs['consistencygroup'] = ( self.consistencygroup_api.get(context, consistencygroup_id)) else: kwargs['consistencygroup'] = None # Get group_id if volume is in a group. group_id = volume.get('group_id') if group_id is not None: try: kwargs['group'] = self.group_api.get(context, group_id) except exception.GroupNotFound as error: raise exc.HTTPNotFound(explanation=error.msg) 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'] elif size is None and kwargs['source_replica'] is not None: size = kwargs['source_replica']['size'] LOG.info(_LI("Create volume of %s GB"), size) if self.ext_mgr.is_loaded('os-image-create'): 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) multiattach = volume.get('multiattach', False) kwargs['multiattach'] = multiattach 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, req, body): """Instruct Cinder to manage a storage object. Manages an existing backend storage object (e.g. a Linux logical volume or a SAN disk) by creating the Cinder objects required to manage it, and possibly renaming the backend storage object (driver dependent) From an API perspective, this operation behaves very much like a volume creation operation, except that properties such as image, snapshot and volume references don't make sense, because we are taking an existing storage object into Cinder management. Required HTTP Body: .. code-block:: json { 'volume': { 'host': <Cinder host on which the existing storage resides>, 'ref': <Driver-specific reference to existing storage object>, } } See the appropriate Cinder drivers' implementations of the manage_volume method to find out the accepted format of 'ref'. This API call will return with an error if any of the above elements are missing from the request, or if the 'host' element refers to a cinder host that is not registered. The volume will later enter the error state if it is discovered that 'ref' is bad. Optional elements to 'volume' are:: name A name for the new volume. description A description for the new volume. volume_type ID or name of a volume type to associate with the new Cinder volume. Does not necessarily guarantee that the managed volume will have the properties described in the volume_type. The driver may choose to fail if it identifies that the specified volume_type is not compatible with the backend storage object. metadata Key/value pairs to be associated with the new volume. availability_zone The availability zone to associate with the new volume. bootable If set to True, marks the volume as bootable. """ context = req.environ['cinder.context'] authorize_manage(context) self.assert_valid_body(body, 'volume') volume = body['volume'] self.validate_name_and_description(volume) # Check that the required keys are present, return an error if they # are not. required_keys = set(['ref', 'host']) missing_keys = list(required_keys - set(volume.keys())) if missing_keys: msg = _("The following elements are required: %s") % \ ', '.join(missing_keys) raise exc.HTTPBadRequest(explanation=msg) LOG.debug('Manage volume request body: %s', body) kwargs = {} 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'] = ( volume_types.get_by_name_or_id(context, req_volume_type)) else: kwargs['volume_type'] = {} kwargs['name'] = volume.get('name', None) kwargs['description'] = volume.get('description', None) kwargs['metadata'] = volume.get('metadata', None) kwargs['availability_zone'] = volume.get('availability_zone', None) kwargs['bootable'] = utils.get_bool_param('bootable', volume) utils.check_metadata_properties(kwargs['metadata']) # Not found exception will be handled at wsgi level new_volume = self.volume_api.manage_existing(context, volume['host'], volume['ref'], **kwargs) utils.add_visible_admin_metadata(new_volume) return self._view_builder.detail(req, new_volume)
def create(self, req, body): """Creates a new volume.""" self.assert_valid_body(body, 'volume') LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] volume = body['volume'] kwargs = {} self.validate_name_and_description(volume) # 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'] = ( volume_types.get_by_name_or_id(context, req_volume_type)) 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 source_replica = volume.get('source_replica') if source_replica is not None: # Not found exception will be handled at the wsgi level src_vol = self.volume_api.get_volume(context, source_replica) if src_vol['replication_status'] == 'disabled': explanation = _('source volume id:%s is not' ' replicated') % source_replica raise exc.HTTPBadRequest(explanation=explanation) kwargs['source_replica'] = src_vol else: kwargs['source_replica'] = None consistencygroup_id = volume.get('consistencygroup_id') if consistencygroup_id is not None: # Not found exception will be handled at the wsgi level kwargs['consistencygroup'] = \ self.consistencygroup_api.get(context, consistencygroup_id) else: kwargs['consistencygroup'] = None 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'] elif size is None and kwargs['source_replica'] is not None: size = kwargs['source_replica']['size'] LOG.info(_LI("Create volume of %s GB"), size) if self.ext_mgr.is_loaded('os-image-create'): 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) 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, req, body): """Creates a new volume.""" if not self.is_valid_body(body, 'volume'): raise exc.HTTPUnprocessableEntity() LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] volume = body['volume'] kwargs = {} 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'] = (volume_types.get_by_name_or_id( context, req_volume_type)) 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 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(_LI("Create volume of %s GB"), size) multiattach = volume.get('multiattach', False) kwargs['multiattach'] = multiattach image_href = None image_uuid = None if self.ext_mgr.is_loaded('os-image-create'): # NOTE(jdg): misleading name "imageRef" as it's an image-id image_href = volume.get('imageRef') if image_href is not None: image_uuid = self._image_uuid_from_href(image_href) kwargs['image_id'] = image_uuid kwargs['availability_zone'] = volume.get('availability_zone', None) new_volume = self.volume_api.create(context, size, volume.get('display_name'), volume.get('display_description'), **kwargs) retval = _translate_volume_detail_view(context, new_volume, image_uuid) return {'volume': retval}
def create(self, req, body): """Instruct Cinder to manage a storage object. Manages an existing backend storage object (e.g. a Linux logical volume or a SAN disk) by creating the Cinder objects required to manage it, and possibly renaming the backend storage object (driver dependent) From an API perspective, this operation behaves very much like a volume creation operation, except that properties such as image, snapshot and volume references don't make sense, because we are taking an existing storage object into Cinder management. Required HTTP Body: .. code-block:: json { "volume": { "host": "<Cinder host on which the existing storage resides>", "cluster": "<Cinder cluster on which the storage resides>", "ref": "<Driver-specific reference to existing storage object>" } } See the appropriate Cinder drivers' implementations of the manage_volume method to find out the accepted format of 'ref'. This API call will return with an error if any of the above elements are missing from the request, or if the 'host' element refers to a cinder host that is not registered. The volume will later enter the error state if it is discovered that 'ref' is bad. Optional elements to 'volume' are:: name A name for the new volume. description A description for the new volume. volume_type ID or name of a volume type to associate with the new Cinder volume. Does not necessarily guarantee that the managed volume will have the properties described in the volume_type. The driver may choose to fail if it identifies that the specified volume_type is not compatible with the backend storage object. metadata Key/value pairs to be associated with the new volume. availability_zone The availability zone to associate with the new volume. bootable If set to True, marks the volume as bootable. """ context = req.environ['cinder.context'] context.authorize(policy.MANAGE_POLICY) volume = body['volume'] cluster_name, host = common.get_cluster_host( req, volume, mv.VOLUME_MIGRATE_CLUSTER) LOG.debug('Manage volume request body: %s', body) kwargs = {} req_volume_type = volume.get('volume_type', None) if req_volume_type: try: kwargs['volume_type'] = volume_types.get_by_name_or_id( context, req_volume_type) except exception.VolumeTypeNotFound: msg = _("Cannot find requested '%s' " "volume type") % req_volume_type raise exception.InvalidVolumeType(reason=msg) else: kwargs['volume_type'] = {} if volume.get('name'): kwargs['name'] = volume.get('name').strip() if volume.get('description'): kwargs['description'] = volume.get('description').strip() kwargs['metadata'] = volume.get('metadata', None) kwargs['availability_zone'] = volume.get('availability_zone', None) bootable = volume.get('bootable', False) kwargs['bootable'] = strutils.bool_from_string(bootable, strict=True) try: new_volume = self.volume_api.manage_existing(context, host, cluster_name, volume['ref'], **kwargs) except exception.ServiceNotFound: msg = _("%(name)s '%(value)s' not found") % { 'name': 'Host' if host else 'Cluster', 'value': host or cluster_name} raise exception.ServiceUnavailable(message=msg) utils.add_visible_admin_metadata(new_volume) return self._view_builder.detail(req, new_volume)
def create(self, req, body): """Instruct Cinder to manage a storage object. Manages an existing backend storage object (e.g. a Linux logical volume or a SAN disk) by creating the Cinder objects required to manage it, and possibly renaming the backend storage object (driver dependent) From an API perspective, this operation behaves very much like a volume creation operation, except that properties such as image, snapshot and volume references don't make sense, because we are taking an existing storage object into Cinder management. Required HTTP Body: .. code-block:: json { 'volume': { 'host': <Cinder host on which the existing storage resides>, 'ref': <Driver-specific reference to existing storage object>, } } See the appropriate Cinder drivers' implementations of the manage_volume method to find out the accepted format of 'ref'. This API call will return with an error if any of the above elements are missing from the request, or if the 'host' element refers to a cinder host that is not registered. The volume will later enter the error state if it is discovered that 'ref' is bad. Optional elements to 'volume' are:: name A name for the new volume. description A description for the new volume. volume_type ID or name of a volume type to associate with the new Cinder volume. Does not necessarily guarantee that the managed volume will have the properties described in the volume_type. The driver may choose to fail if it identifies that the specified volume_type is not compatible with the backend storage object. metadata Key/value pairs to be associated with the new volume. availability_zone The availability zone to associate with the new volume. bootable If set to True, marks the volume as bootable. """ context = req.environ['cinder.context'] authorize_manage(context) self.assert_valid_body(body, 'volume') volume = body['volume'] self.validate_name_and_description(volume) # Check that the required keys are present, return an error if they # are not. required_keys = set(['ref', 'host']) missing_keys = list(required_keys - set(volume.keys())) if missing_keys: msg = _("The following elements are required: %s") % \ ', '.join(missing_keys) raise exc.HTTPBadRequest(explanation=msg) LOG.debug('Manage volume request body: %s', body) kwargs = {} 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'] = (volume_types.get_by_name_or_id( context, req_volume_type)) else: kwargs['volume_type'] = {} kwargs['name'] = volume.get('name', None) kwargs['description'] = volume.get('description', None) kwargs['metadata'] = volume.get('metadata', None) kwargs['availability_zone'] = volume.get('availability_zone', None) kwargs['bootable'] = utils.get_bool_param('bootable', volume) utils.check_metadata_properties(kwargs['metadata']) # Not found exception will be handled at wsgi level new_volume = self.volume_api.manage_existing(context, volume['host'], volume['ref'], **kwargs) utils.add_visible_admin_metadata(new_volume) return self._view_builder.detail(req, new_volume)
def create(self, req, body): """Creates a new volume.""" self.assert_valid_body(body, 'volume') LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] volume = body['volume'] kwargs = {} self.validate_name_and_description(volume) # 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'] = (volume_types.get_by_name_or_id( context, req_volume_type)) 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 source_replica = volume.get('source_replica') if source_replica is not None: # Not found exception will be handled at the wsgi level src_vol = self.volume_api.get_volume(context, source_replica) if src_vol['replication_status'] == 'disabled': explanation = _('source volume id:%s is not' ' replicated') % source_replica raise exc.HTTPBadRequest(explanation=explanation) kwargs['source_replica'] = src_vol else: kwargs['source_replica'] = None consistencygroup_id = volume.get('consistencygroup_id') if consistencygroup_id is not None: # Not found exception will be handled at the wsgi level kwargs['consistencygroup'] = \ self.consistencygroup_api.get(context, consistencygroup_id) else: kwargs['consistencygroup'] = None 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'] elif size is None and kwargs['source_replica'] is not None: size = kwargs['source_replica']['size'] LOG.info(_LI("Create volume of %s GB"), size) if self.ext_mgr.is_loaded('os-image-create'): 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) 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, req, body): """Creates a new volume.""" if not self.is_valid_body(body, 'volume'): raise exc.HTTPUnprocessableEntity() LOG.debug('Create volume request body: %s', body) context = req.environ['cinder.context'] volume = body['volume'] kwargs = {} 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'] = ( volume_types.get_by_name_or_id(context, req_volume_type)) kwargs['metadata'] = volume.get('metadata', None) snapshot_id = volume.get('snapshot_id') if snapshot_id is not None: if not uuidutils.is_uuid_like(snapshot_id): msg = _("Snapshot ID must be in UUID form.") raise exc.HTTPBadRequest(explanation=msg) # 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 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(_LI("Create volume of %s GB"), size) multiattach = volume.get('multiattach', False) kwargs['multiattach'] = multiattach image_href = None image_uuid = None if self.ext_mgr.is_loaded('os-image-create'): # NOTE(jdg): misleading name "imageRef" as it's an image-id image_href = volume.get('imageRef') if image_href is not None: image_uuid = self._image_uuid_from_href(image_href) kwargs['image_id'] = image_uuid kwargs['availability_zone'] = volume.get('availability_zone', None) new_volume = self.volume_api.create(context, size, volume.get('display_name'), volume.get('display_description'), **kwargs) retval = _translate_volume_detail_view(context, new_volume, image_uuid) return {'volume': retval}