Exemplo n.º 1
0
def remove_volume_type_access(context, volume_type_id, project_id):
    """Remove access to volume type for project_id."""
    if volume_type_id is None:
        msg = _("volume_type_id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    if is_public_volume_type(context, volume_type_id):
        msg = _("Type access modification is not applicable to public volume "
                "type.")
        raise exception.InvalidVolumeType(reason=msg)
    return db.volume_type_access_remove(context, volume_type_id, project_id)
Exemplo n.º 2
0
def add_volume_type_access(context, volume_type_id, project_id):
    """Add access to volume type for project_id."""
    if volume_type_id is None:
        msg = _("volume_type_id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    elevated = context if context.is_admin else context.elevated()
    if is_public_volume_type(elevated, volume_type_id):
        msg = _("Type access modification is not applicable to public volume "
                "type.")
        raise exception.InvalidVolumeType(reason=msg)
    return db.volume_type_access_add(elevated, volume_type_id, project_id)
Exemplo n.º 3
0
def remove_volume_type_access(context, volume_type_id, project_id):
    """Remove access to volume type for project_id."""
    if volume_type_id is None:
        msg = _("volume_type_id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    elevated = context if context.is_admin else context.elevated()
    if is_public_volume_type(elevated, volume_type_id):
        msg = _("Type access modification is not applicable to public volume "
                "type.")
        raise exception.InvalidVolumeType(reason=msg)

    db.volume_type_access_remove(elevated, volume_type_id, project_id)

    notify_about_volume_type_access_usage(context, volume_type_id, project_id,
                                          'access.remove')
Exemplo n.º 4
0
def update(context, id, name, description, is_public=None):
    """Update volume type by id."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    elevated = context if context.is_admin else context.elevated()
    old_volume_type = get_volume_type(elevated, id)
    try:
        db.volume_type_update(elevated, id,
                              dict(name=name, description=description,
                                   is_public=is_public))
        # Rename resource in quota if volume type name is changed.
        if name:
            old_type_name = old_volume_type.get('name')
            if old_type_name != name:
                old_description = old_volume_type.get('description')
                old_public = old_volume_type.get('is_public')
                try:
                    QUOTAS.update_quota_resource(elevated,
                                                 old_type_name,
                                                 name)
                # Rollback the updated information to the original
                except db_exc.DBError:
                    db.volume_type_update(elevated, id,
                                          dict(name=old_type_name,
                                               description=old_description,
                                               is_public=old_public))
                    raise
    except db_exc.DBError:
        LOG.exception('DB error:')
        raise exception.VolumeTypeUpdateFailed(id=id)
Exemplo n.º 5
0
def destroy(context, id):
    """Marks volume types as deleted."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    elevated = context if context.is_admin else context.elevated()
    return db.volume_type_destroy(elevated, id)
Exemplo n.º 6
0
def associate_qos_with_type(context, specs_id, type_id):
    """Associate qos_specs with volume type.

    Associate target qos specs with specific volume type. Would raise
    following exceptions:
        VolumeTypeNotFound  - if volume type doesn't exist;
        QoSSpecsNotFound  - if qos specs doesn't exist;
        InvalidVolumeType  - if volume type is already associated with
                             qos specs other than given one.
        QoSSpecsAssociateFailed -  if there was general DB error
    :param specs_id: qos specs ID to associate with
    :param type_id: volume type ID to associate with
    """
    try:
        get_qos_specs(context, specs_id)
        res = volume_types.get_volume_type_qos_specs(type_id)
        if res.get('qos_specs', None):
            if res['qos_specs'].get('id') != specs_id:
                msg = (_("Type %(type_id)s is already associated with another "
                         "qos specs: %(qos_specs_id)s") %
                       {'type_id': type_id,
                        'qos_specs_id': res['qos_specs']['id']})
                raise exception.InvalidVolumeType(reason=msg)
        else:
            db.qos_specs_associate(context, specs_id, type_id)
    except db_exc.DBError as e:
        LOG.exception(_('DB error: %s') % e)
        LOG.warn(_('Failed to associate qos specs '
                   '%(id)s with type: %(vol_type_id)s') %
                 dict(id=specs_id, vol_type_id=type_id))
        raise exception.QoSSpecsAssociateFailed(specs_id=specs_id,
                                                type_id=type_id)
Exemplo n.º 7
0
    def create_cloned_volume(self, volume, src_vref):
        """Creates a clone of the specified volume.

        :param volume: reference to the volume being created
        :param src_vref: reference to the source volume
        :returns: the provider_location of the cloned volume
        """

        # HNAS always creates cloned volumes in the same pool as the source
        # volumes. So, it is not allowed to use different volume types for
        # clone operations.
        if volume.volume_type_id != src_vref.volume_type_id:
            msg = _("Source and cloned volumes should have the same "
                    "volume type.")
            LOG.error(msg)
            raise exception.InvalidVolumeType(msg)

        vol_size = volume.size
        src_vol_size = src_vref.size

        self._clone_volume(src_vref, volume.name, src_vref.name)

        share = src_vref.provider_location

        if vol_size > src_vol_size:
            volume.provider_location = share
            self.extend_volume(volume, vol_size)

        return {'provider_location': share}
Exemplo n.º 8
0
def get_volume_type_by_name(context, name):
    """Retrieves single volume type by name."""
    if name is None:
        msg = _("name cannot be None")
        raise exception.InvalidVolumeType(reason=msg)

    return db.volume_type_get_by_name(context, name)
Exemplo n.º 9
0
def destroy(context, id):
    """Marks volume types as deleted."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    else:
        db.volume_type_destroy(context, id)
Exemplo n.º 10
0
def destroy(context, id):
    """Marks volume types as deleted.

    There must exist at least one volume type (i.e. the default type) in
    the deployment.
    This method achieves that by ensuring:
    1) the default_volume_type is set and is a valid one
    2) the type requested to delete isn't the default type

    :raises VolumeTypeDefaultDeletionError: when the type requested to
                                            delete is the default type
    """
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    elevated = context if context.is_admin else context.elevated()

    # Default type *must* be set in order to delete any volume type.
    # If the default isn't set, the following call will raise
    # VolumeTypeDefaultMisconfiguredError exception which will error out the
    # delete operation.
    default_type = get_default_volume_type()
    # don't allow delete if the type requested is the default type
    if id == default_type.get('id'):
        raise exception.VolumeTypeDefaultDeletionError(volume_type_id=id)

    return db.volume_type_destroy(elevated, id)
Exemplo n.º 11
0
def get_volume_type_by_name(context: context.RequestContext,
                            name: Optional[str]) -> dict[str, Any]:
    """Retrieves single volume type by name."""
    if name is None:
        msg = _("name cannot be None")
        raise exception.InvalidVolumeType(reason=msg)

    return db.volume_type_get_by_name(context, name)
Exemplo n.º 12
0
    def validate(self, enabler_status):
        """Checks whether the extra specs are valid.

        :param enabler_status: Instance of VNXEnablerStatus
        """
        if "storagetype:pool" in self.specs:
            LOG.warning(
                _LW("Extra spec key 'storagetype:pool' is obsoleted "
                    "since driver version 5.1.0. This key will be "
                    "ignored."))

        if (self._provision == storops.VNXProvisionEnum.DEDUPED
                and self._tier is not None):
            msg = _("Can not set tiering policy for a deduplicated volume. "
                    "Set the tiering policy on the pool where the "
                    "deduplicated volume locates.")
            raise exception.InvalidVolumeType(reason=msg)

        if (self._provision == storops.VNXProvisionEnum.COMPRESSED
                and not enabler_status.compression_enabled):
            msg = _("Compression Enabler is not installed. "
                    "Can not create compressed volume.")
            raise exception.InvalidVolumeType(reason=msg)

        if (self._provision == storops.VNXProvisionEnum.DEDUPED
                and not enabler_status.dedup_enabled):
            msg = _("Deduplication Enabler is not installed. "
                    "Can not create deduplicated volume.")
            raise exception.InvalidVolumeType(reason=msg)

        if (self._provision in [
                storops.VNXProvisionEnum.THIN,
                storops.VNXProvisionEnum.COMPRESSED,
                storops.VNXProvisionEnum.DEDUPED
        ] and not enabler_status.thin_enabled):
            msg = _("ThinProvisioning Enabler is not installed. "
                    "Can not create thin volume.")
            raise exception.InvalidVolumeType(reason=msg)

        if (self._tier is not None and not enabler_status.fast_enabled):
            msg = _("FAST VP Enabler is not installed. "
                    "Can not set tiering policy for the volume.")
            raise exception.InvalidVolumeType(reason=msg)
        return True
Exemplo n.º 13
0
def add_volume_type_access(context: context.RequestContext,
                           volume_type_id: Optional[str],
                           project_id: str) -> None:
    """Add access to volume type for project_id."""
    if volume_type_id is None:
        msg = _("volume_type_id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    elevated = context if context.is_admin else context.elevated()
    if is_public_volume_type(elevated, volume_type_id):
        msg = _("Type access modification is not applicable to public volume "
                "type.")
        raise exception.InvalidVolumeType(reason=msg)

    db.volume_type_access_add(elevated, volume_type_id, project_id)

    notify_about_volume_type_access_usage(context,
                                          volume_type_id,
                                          project_id,
                                          'access.add')
Exemplo n.º 14
0
def destroy(context, id):
    """Marks volume types as deleted."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    vol_type = get_volume_type(context, id)
    if vol_type['name'] == DEFAULT_VOLUME_TYPE:
        raise exception.VolumeTypeDefault(vol_type['name'])
    elevated = context if context.is_admin else context.elevated()
    return db.volume_type_destroy(elevated, id)
Exemplo n.º 15
0
def get_volume_type(ctxt, id, expected_fields=None):
    """Retrieves single volume type by id."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)

    if ctxt is None:
        ctxt = context.get_admin_context()

    return db.volume_type_get(ctxt, id, expected_fields=expected_fields)
Exemplo n.º 16
0
def update(context, id, name, description):
    """Update volume type by id."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    try:
        type_updated = db.volume_type_update(
            context, id, dict(name=name, description=description))
    except db_exc.DBError as e:
        LOG.exception(_LE('DB error: %s') % six.text_type(e))
        raise exception.VolumeTypeUpdateFailed(id=id)
    return type_updated
Exemplo n.º 17
0
 def _parse_to_enum(self, key, enum_class):
     value = (self.specs[key]
              if key in self.specs else None)
     if value is not None:
         try:
             value = enum_class.parse(value)
         except ValueError:
             reason = (_("The value %(value)s for key %(key)s in extra "
                         "specs is invalid.") %
                       {'key': key, 'value': value})
             raise exception.InvalidVolumeType(reason=reason)
     return value
Exemplo n.º 18
0
    def _check_type(self, group):
        if not vol_utils.is_group_a_replication_group_type(group):
            msg = _("Group %s is not a replication group type.") % group.id
            LOG.error(msg)
            raise exception.InvalidGroupType(reason=msg)

        for vol_type in group.volume_types:
            if not vol_utils.is_replicated_spec(vol_type.extra_specs):
                msg = _("Volume type %s does not have 'replication_enabled' "
                        "spec key set to '<is> True'.") % vol_type.id
                LOG.error(msg)
                raise exception.InvalidVolumeType(reason=msg)
Exemplo n.º 19
0
def get_volume_type(
        ctxt: Optional[context.RequestContext],
        id: Optional[str],
        expected_fields: Optional[Iterable[str]] = None) -> dict[str, Any]:
    """Retrieves single volume type by id."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)

    if ctxt is None:
        ctxt = context.get_admin_context()

    return db.volume_type_get(ctxt, id, expected_fields=expected_fields)
Exemplo n.º 20
0
    def schedule_create_volume(self, context, request_spec, filter_properties):
        """Use volume type extra_specs to store tenant info for tenant isolation"""

        volume_id = request_spec.get('volume_id')
        snapshot_id = request_spec.get('snapshot_id')
        image_id = request_spec.get('image_id')
        volume_properties = request_spec.get('volume_properties')
        availability_zone = volume_properties.get('availability_zone')

        context_dict = context.to_dict()
        tenant_name = context_dict['project_name']

        # check if request has volume type and volume type matching tenant
        # if no volume type in request, search db for tenant's bind volume type
        # if no bind volume type, add default volume type to create volume
        volume_type = request_spec.get('volume_type')
        if volume_type:
            specs = volume_type.get('extra_specs')
            if 'tenant_name' in specs:
                if specs['tenant_name'] != tenant_name:
                    msg = _("Tenant cannot use volume type %s." %
                            volume_type['name'])
                    raise exception.InvalidVolumeType(reason=msg)
        else:
            #check db if user's tenant has been bond to a volume type
            bindType = False
            volume_types = db.volume_type_get_all(context)
            for key in volume_types:
                specs = volume_types[key].get('extra_specs')
                if 'tenant_name' in specs:
                    if specs['tenant_name'] == tenant_name:
                        bindType = True
                        request_spec['volume_type'] = volume_types[key]
                        break
            if not bindType:
                request_spec['volume_type'] = db.volume_type_get_by_name(
                    context, 'DEFAULT')

        LOG.debug(str(request_spec))

        host = 'MyHost'
        updated_volume = driver.volume_update_db(context, volume_id, host)
        self.volume_rpcapi.create_volume(context,
                                         updated_volume,
                                         host,
                                         request_spec,
                                         filter_properties,
                                         snapshot_id=snapshot_id,
                                         image_id=image_id)
        return None
Exemplo n.º 21
0
def update(context, id, name, description, is_public=None):
    """Update volume type by id."""
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    old_volume_type = get_volume_type(context, id)
    try:
        type_updated = db.volume_type_update(
            context, id,
            dict(name=name, description=description, is_public=is_public))
        # Rename resource in quota if volume type name is changed.
        if name:
            old_type_name = old_volume_type.get('name')
            if old_type_name != name:
                QUOTAS.update_quota_resource(context, old_type_name, name)
    except db_exc.DBError:
        LOG.exception(_LE('DB error:'))
        raise exception.VolumeTypeUpdateFailed(id=id)
    return type_updated
Exemplo n.º 22
0
def destroy(context, id):
    """Marks volume types as deleted.

    There must exist at least one volume type (i.e. the default type) in
    the deployment.
    This method achieves that by ensuring:
    1) the default_volume_type is set and is a valid one
    2) the type requested to delete isn't the default type

    :raises VolumeTypeDefaultDeletionError: when the type requested to
                                            delete is the default type
    """
    if id is None:
        msg = _("id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)

    projects_with_default_type = db.get_all_projects_with_default_type(
        context.elevated(), id)
    if len(projects_with_default_type) > 0:
        # don't allow delete if the type requested is a project default
        project_list = [p.project_id for p in projects_with_default_type]
        LOG.exception(
            'Default type with %(volume_type_id)s is associated '
            'with projects %(projects)s', {
                'volume_type_id': id,
                'projects': project_list
            })
        raise exception.VolumeTypeDefaultDeletionError(volume_type_id=id)

    # Default type *must* be set in order to delete any volume type.
    # If the default isn't set, the following call will raise
    # VolumeTypeDefaultMisconfiguredError exception which will error out the
    # delete operation.
    default_type = get_default_volume_type()
    # don't allow delete if the type requested is the conf default type
    if id == default_type.get('id'):
        raise exception.VolumeTypeDefaultDeletionError(volume_type_id=id)

    elevated = context if context.is_admin else context.elevated()
    return db.volume_type_destroy(elevated, id)
Exemplo n.º 23
0
def decode_cipher(cipher_spec: str, key_size: int) -> dict[str, str]:
    """Decode a dm-crypt style cipher specification string

       The assumed format being cipher-chainmode-ivmode, similar to that
       documented under
       linux/Documentation/admin-guide/device-mapper/dm-crypt.txt in the
       kernel source tree.  Cinder does not support the [:keycount] or
       [:ivopts] options.
    """
    try:
        cipher_alg, cipher_mode, ivgen_alg = cipher_spec.split('-')
    except ValueError:
        raise exception.InvalidVolumeType(
            reason="Invalid cipher field in encryption type")

    cipher_alg = cipher_alg + '-' + str(key_size)

    return {
        'cipher_alg': cipher_alg,
        'cipher_mode': cipher_mode,
        'ivgen_alg': ivgen_alg
    }
Exemplo n.º 24
0
    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)
Exemplo n.º 25
0
def remove_volume_type_access(context, volume_type_id, project_id):
    """Remove access to volume type for project_id."""
    if volume_type_id is None:
        msg = _("volume_type_id cannot be None")
        raise exception.InvalidVolumeType(reason=msg)
    return db.volume_type_access_remove(context, volume_type_id, project_id)
Exemplo n.º 26
0
    def _get_weighted_candidates(self,
                                 context,
                                 request_spec,
                                 filter_properties=None):
        """Return a list of hosts that meet required specs.

        Returned list is ordered by their fitness.
        """
        elevated = context.elevated()

        volume_properties = request_spec['volume_properties']
        # Since Cinder is using mixed filters from Oslo and it's own, which
        # takes 'resource_XX' and 'volume_XX' as input respectively, copying
        # 'volume_XX' to 'resource_XX' will make both filters happy.
        resource_properties = volume_properties.copy()
        volume_type = request_spec.get("volume_type", None)
        resource_type = request_spec.get("volume_type", None)
        request_spec.update({'resource_properties': resource_properties})

        config_options = self._get_configuration_options()

        if filter_properties is None:
            filter_properties = {}
        self._populate_retry(filter_properties, resource_properties)

        if resource_type is None:
            msg = _("volume_type cannot be None")
            raise exception.InvalidVolumeType(reason=msg)

        filter_properties.update({
            'context': context,
            'request_spec': request_spec,
            'config_options': config_options,
            'volume_type': volume_type,
            'resource_type': resource_type
        })

        self.populate_filter_properties(request_spec, filter_properties)

        # If multiattach is enabled on a volume, we need to add
        # multiattach to extra specs, so that the capability
        # filtering is enabled.
        multiattach = volume_properties.get('multiattach', False)
        if multiattach and 'multiattach' not in resource_type.get(
                'extra_specs', {}):
            if 'extra_specs' not in resource_type:
                resource_type['extra_specs'] = {}

            resource_type['extra_specs'].update(multiattach='<is> True')

        # Find our local list of acceptable hosts by filtering and
        # weighing our options. we virtually consume resources on
        # it so subsequent selections can adjust accordingly.

        # Note: remember, we are using an iterator here. So only
        # traverse this list once.
        hosts = self.host_manager.get_all_host_states(elevated)

        # Filter local hosts based on requirements ...
        hosts = self.host_manager.get_filtered_hosts(hosts, filter_properties)
        if not hosts:
            return []

        LOG.debug("Filtered %s", hosts)
        # weighted_host = WeightedHost() ... the best
        # host for the job.
        weighed_hosts = self.host_manager.get_weighed_hosts(
            hosts, filter_properties)
        return weighed_hosts