Example #1
0
    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']
        req_version = req.api_version_request

        cascade = utils.get_bool_param('cascade', req.params)
        force = False

        params = ""
        if req_version.matches(mv.VOLUME_LIST_BOOTABLE):
            force = utils.get_bool_param('force', req.params)
            if cascade or force:
                params = "(cascade: %(c)s, force: %(f)s)" % {'c': cascade,
                                                             'f': force}

        LOG.info("Delete volume with id: %(id)s %(params)s",
                 {'id': id, 'params': params}, context=context)

        volume = self.volume_api.get(context, id)

        if force:
            context.authorize(policy.FORCE_DELETE_POLICY, target_obj=volume)

        self.volume_api.delete(context, volume,
                               cascade=cascade,
                               force=force)

        return webob.Response(status_int=202)
Example #2
0
    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']
        req_version = req.api_version_request

        cascade = utils.get_bool_param('cascade', req.params)
        force = False

        params = ""
        if req_version.matches('3.23'):
            force = utils.get_bool_param('force', req.params)
            if cascade or force:
                params = "(cascade: %(c)s, force: %(f)s)" % {'c': cascade,
                                                             'f': force}

        LOG.info("Delete volume with id: %(id)s %(params)s",
                 {'id': id, 'params': params}, context=context)

        if force:
            check_policy(context, 'force_delete')

        volume = self.volume_api.get(context, id)

        self.volume_api.delete(context, volume,
                               cascade=cascade,
                               force=force)

        return webob.Response(status_int=202)
Example #3
0
    def _migrate_volume(self, req, id, body):
        """Migrate a volume to the specified host."""
        context = req.environ['cinder.context']
        self.authorize(context, 'migrate_volume')
        # Not found exception will be handled at the wsgi level
        volume = self._get(context, id)
        params = body['os-migrate_volume']

        cluster_name, host = common.get_cluster_host(req, params, '3.16')
        force_host_copy = utils.get_bool_param('force_host_copy', params)
        lock_volume = utils.get_bool_param('lock_volume', params)
        self.volume_api.migrate_volume(context, volume, host, cluster_name,
                                       force_host_copy, lock_volume)
        return webob.Response(status_int=202)
Example #4
0
    def _migrate_volume(self, req, id, body):
        """Migrate a volume to the specified host."""
        context = req.environ['cinder.context']
        # Not found exception will be handled at the wsgi level
        volume = self._get(context, id)
        self.authorize(context, 'migrate_volume', target_obj=volume)
        params = body['os-migrate_volume']

        cluster_name, host = common.get_cluster_host(req, params,
                                                     mv.VOLUME_MIGRATE_CLUSTER)
        force_host_copy = utils.get_bool_param('force_host_copy', params)
        lock_volume = utils.get_bool_param('lock_volume', params)
        self.volume_api.migrate_volume(context, volume, host, cluster_name,
                                       force_host_copy, lock_volume)
Example #5
0
    def _get_clusters(self, req, detail):
        # Let the wsgi middleware convert NotAuthorized exceptions
        context = self.policy_checker(req, 'get_all')
        replication_data = req.api_version_request.matches(
            REPLICATION_DATA_MICRO_VERSION)
        filters = dict(req.GET)
        allowed = self.allowed_list_keys
        if not replication_data:
            allowed = allowed.difference(self.replication_fields)

        # Check filters are valid
        if not allowed.issuperset(filters):
            invalid_keys = set(filters).difference(allowed)
            msg = _('Invalid filter keys: %s') % ', '.join(invalid_keys)
            raise exception.InvalidInput(reason=msg)

        # Check boolean values
        for bool_key in ('disabled', 'is_up'):
            if bool_key in filters:
                filters[bool_key] = utils.get_bool_param(bool_key, req.GET)

        # For detailed view we need the services summary information
        filters['services_summary'] = detail

        clusters = objects.ClusterList.get_all(context, **filters)
        return clusters_view.ViewBuilder.list(clusters, detail,
                                              replication_data)
Example #6
0
    def _get_clusters(self, req, detail):
        # Let the wsgi middleware convert NotAuthorized exceptions
        context = self.policy_checker(req, 'get_all')
        replication_data = req.api_version_request.matches(
            mv.REPLICATION_CLUSTER)
        filters = dict(req.GET)
        allowed = self.allowed_list_keys
        if not replication_data:
            allowed = allowed.difference(self.replication_fields)

        # Check filters are valid
        if not allowed.issuperset(filters):
            invalid_keys = set(filters).difference(allowed)
            msg = _('Invalid filter keys: %s') % ', '.join(invalid_keys)
            raise exception.InvalidInput(reason=msg)

        # Check boolean values
        for bool_key in ('disabled', 'is_up'):
            if bool_key in filters:
                filters[bool_key] = utils.get_bool_param(bool_key, req.GET)

        # For detailed view we need the services summary information
        filters['services_summary'] = detail

        clusters = objects.ClusterList.get_all(context, **filters)
        return clusters_view.ViewBuilder.list(clusters, detail,
                                              replication_data)
Example #7
0
    def detail(self, request, group):
        """Detailed view of a single group."""
        group_ref = {
            'group': {
                'id': group.id,
                'status': group.status,
                'availability_zone': group.availability_zone,
                'created_at': group.created_at,
                'name': group.name,
                'description': group.description,
                'group_type': group.group_type_id,
                'volume_types': [v_type.id for v_type in group.volume_types],
            }
        }

        req_version = request.api_version_request
        # Add group_snapshot_id and source_group_id if min version is greater
        # than or equal to 3.14.
        if req_version.matches("3.14", None):
            group_ref['group']['group_snapshot_id'] = group.group_snapshot_id
            group_ref['group']['source_group_id'] = group.source_group_id

        # Add volumes if min version is greater than or equal to 3.25.
        if req_version.matches("3.25", None):
            if utils.get_bool_param('list_volume', request.params):
                group_ref['group']['volumes'] = [volume.id
                                                 for volume in group.volumes]

        # Add replication_status if min version is greater than or equal
        # to 3.38.
        if req_version.matches("3.38", None):
            group_ref['group']['replication_status'] = group.replication_status

        return group_ref
Example #8
0
 def _migrate_volume(self, req, id, body):
     """Migrate a volume to the specified host."""
     context = req.environ['cinder.context']
     self.authorize(context, 'migrate_volume')
     # Not found exception will be handled at the wsgi level
     volume = self._get(context, id)
     params = body['os-migrate_volume']
     try:
         host = params['host']
     except KeyError:
         raise exc.HTTPBadRequest(explanation=_("Must specify 'host'."))
     force_host_copy = utils.get_bool_param('force_host_copy', params)
     lock_volume = utils.get_bool_param('lock_volume', params)
     self.volume_api.migrate_volume(context, volume, host, force_host_copy,
                                    lock_volume)
     return webob.Response(status_int=202)
Example #9
0
def fake_volume_create(self,
                       context,
                       size,
                       name,
                       description,
                       snapshot=None,
                       group_id=None,
                       **param):
    vol = create_volume(DEFAULT_VOL_ID)
    vol['size'] = size
    vol['display_name'] = name
    vol['display_description'] = description
    source_volume = param.get('source_volume') or {}
    vol['source_volid'] = source_volume.get('id')
    vol['bootable'] = False
    vol['volume_attachment'] = []
    vol['multiattach'] = utils.get_bool_param('multiattach', param)
    try:
        vol['snapshot_id'] = snapshot['id']
    except (KeyError, TypeError):
        vol['snapshot_id'] = None
    vol['availability_zone'] = param.get('availability_zone', 'fakeaz')
    if group_id:
        vol['group_id'] = group_id
    return vol
Example #10
0
 def _migrate_volume(self, req, id, body):
     """Migrate a volume to the specified host."""
     context = req.environ['cinder.context']
     self.authorize(context, 'migrate_volume')
     # Not found exception will be handled at the wsgi level
     volume = self._get(context, id)
     params = body['os-migrate_volume']
     try:
         host = params['host']
     except KeyError:
         raise exc.HTTPBadRequest(explanation=_("Must specify 'host'."))
     force_host_copy = utils.get_bool_param('force_host_copy', params)
     lock_volume = utils.get_bool_param('lock_volume', params)
     self.volume_api.migrate_volume(context, volume, host, force_host_copy,
                                    lock_volume)
     return webob.Response(status_int=202)
Example #11
0
    def delete(self, req, id):
        """Deletes an existing qos specs."""
        context = req.environ['cinder.context']
        authorize(context)

        # Convert string to bool type in strict manner
        force = utils.get_bool_param('force', req.params)
        LOG.debug("Delete qos_spec: %(id)s, force: %(force)s",
                  {'id': id, 'force': force})

        try:
            qos_specs.delete(context, id, force)
            notifier_info = dict(id=id)
            rpc.get_notifier('QoSSpecs').info(context,
                                              'qos_specs.delete',
                                              notifier_info)
        except exception.QoSSpecsNotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.delete',
                                         notifier_err)
            # Not found exception will be handled at the wsgi level
            raise
        except exception.QoSSpecsInUse as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context,
                                         'qos_specs.delete',
                                         notifier_err)
            if force:
                msg = _('Failed to disassociate qos specs.')
                raise webob.exc.HTTPInternalServerError(explanation=msg)
            msg = _('Qos specs still in use.')
            raise webob.exc.HTTPBadRequest(explanation=msg)

        return webob.Response(status_int=202)
Example #12
0
    def delete(self, req, id):
        """Deletes an existing qos specs."""
        context = req.environ['cinder.context']
        context.authorize(policy.DELETE_POLICY)

        # Convert string to bool type in strict manner
        force = utils.get_bool_param('force', req.params)
        LOG.debug("Delete qos_spec: %(id)s, force: %(force)s", {
            'id': id,
            'force': force
        })

        try:
            qos_specs.delete(context, id, force)
            notifier_info = dict(id=id)
            rpc.get_notifier('QoSSpecs').info(context, 'qos_specs.delete',
                                              notifier_info)
        except exception.QoSSpecsNotFound as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context, 'qos_specs.delete',
                                         notifier_err)
            # Not found exception will be handled at the wsgi level
            raise
        except exception.QoSSpecsInUse as err:
            notifier_err = dict(id=id, error_message=err)
            self._notify_qos_specs_error(context, 'qos_specs.delete',
                                         notifier_err)
            if force:
                msg = _('Failed to disassociate qos specs.')
                raise webob.exc.HTTPInternalServerError(explanation=msg)
            msg = _('Qos specs still in use.')
            raise webob.exc.HTTPBadRequest(explanation=msg)

        return webob.Response(status_int=http_client.ACCEPTED)
Example #13
0
    def _prepare_params(self, ctxt, params, allowed):
        if not allowed.issuperset(params):
            invalid_keys = set(params).difference(allowed)
            msg = _('Invalid filter keys: %s') % ', '.join(invalid_keys)
            raise exception.InvalidInput(reason=msg)

        if params.get('binary') not in (None, 'cinder-volume',
                                        'cinder-scheduler'):
            msg = _('binary must be empty or set to cinder-volume or '
                    'cinder-scheduler')
            raise exception.InvalidInput(reason=msg)

        for boolean in ('disabled', 'is_up'):
            if params.get(boolean) is not None:
                params[boolean] = utils.get_bool_param(boolean, params)

        resource_type = params.get('resource_type')

        if resource_type:
            resource_type = resource_type.title()
            types = cleanable.CinderCleanableObject.cleanable_resource_types
            if resource_type not in types:
                msg = (_('Resource type %s not valid, must be ') %
                       resource_type)
                msg = utils.build_or_str(types, msg + '%s.')
                raise exception.InvalidInput(reason=msg)
            params['resource_type'] = resource_type

        resource_id = params.get('resource_id')
        if resource_id:
            if not uuidutils.is_uuid_like(resource_id):
                msg = (_('Resource ID must be a UUID, and %s is not.') %
                       resource_id)
                raise exception.InvalidInput(reason=msg)

            # If we have the resource type but we don't have where it is
            # located, we get it from the DB to limit the distribution of the
            # request by the scheduler, otherwise it will be distributed to all
            # the services.
            location_keys = {'service_id', 'cluster_name', 'host'}
            if not location_keys.intersection(params):
                workers = db.worker_get_all(ctxt, resource_id=resource_id,
                                            binary=params.get('binary'),
                                            resource_type=resource_type)

                if len(workers) == 0:
                    msg = (_('There is no resource with UUID %s pending '
                             'cleanup.'), resource_id)
                    raise exception.InvalidInput(reason=msg)
                if len(workers) > 1:
                    msg = (_('There are multiple resources with UUID %s '
                             'pending cleanup.  Please be more specific.'),
                           resource_id)
                    raise exception.InvalidInput(reason=msg)

                worker = workers[0]
                params.update(service_id=worker.service_id,
                              resource_type=worker.resource_type)

        return params
Example #14
0
    def _update(self, req, id, body):
        # Update description for a given volume type.
        context = req.environ['cinder.context']
        authorize(context)

        self.assert_valid_body(body, 'volume_type')

        vol_type = body['volume_type']
        description = vol_type.get('description')
        name = vol_type.get('name')
        is_public = vol_type.get('is_public')

        # Name and description can not be both None.
        # If name specified, name can not be empty.
        if name and len(name.strip()) == 0:
            msg = _("Volume type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if name is None and description is None and is_public is None:
            msg = _("Specify volume type name, description, is_public or "
                    "a combination thereof.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if is_public is not None:
            is_public = utils.get_bool_param('is_public', vol_type)

        if name:
            utils.check_string_length(name, 'Type name',
                                      min_length=1, max_length=255)

        if description is not None:
            utils.check_string_length(description, 'Type description',
                                      min_length=0, max_length=255)

        try:
            volume_types.update(context, id, name, description,
                                is_public=is_public)
            # Get the updated
            vol_type = volume_types.get_volume_type(context, id)
            req.cache_resource(vol_type, name='types')
            self._notify_volume_type_info(
                context, 'volume_type.update', vol_type)

        except exception.VolumeTypeNotFound as err:
            self._notify_volume_type_error(
                context, 'volume_type.update', err, id=id)
            # Not found exception will be handled at the wsgi level
            raise
        except exception.VolumeTypeExists as err:
            self._notify_volume_type_error(
                context, 'volume_type.update', err, volume_type=vol_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.VolumeTypeUpdateFailed as err:
            self._notify_volume_type_error(
                context, 'volume_type.update', err, volume_type=vol_type)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return self._view_builder.show(req, vol_type)
Example #15
0
    def update(self, req, id, body):
        # Update description for a given group type.
        context = req.environ['cinder.context']
        self._check_policy(context)

        self.assert_valid_body(body, 'group_type')

        grp_type = body['group_type']
        description = grp_type.get('description')
        name = grp_type.get('name')
        is_public = grp_type.get('is_public')

        # Name and description can not be both None.
        # If name specified, name can not be empty.
        if name and len(name.strip()) == 0:
            msg = _("Group type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if name is None and description is None and is_public is None:
            msg = _("Specify group type name, description or "
                    "a combination thereof.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if is_public is not None:
            is_public = utils.get_bool_param('is_public', grp_type)

        if name:
            utils.check_string_length(name, 'Type name',
                                      min_length=1, max_length=255)

        if description is not None:
            utils.check_string_length(description, 'Type description',
                                      min_length=0, max_length=255)

        try:
            group_types.update(context, id, name, description,
                               is_public=is_public)
            # Get the updated
            grp_type = group_types.get_group_type(context, id)
            req.cache_resource(grp_type, name='group_types')
            self._notify_group_type_info(
                context, 'group_type.update', grp_type)

        except exception.GroupTypeNotFound as err:
            self._notify_group_type_error(
                context, 'group_type.update', err, id=id)
            raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
        except exception.GroupTypeExists as err:
            self._notify_group_type_error(
                context, 'group_type.update', err, group_type=grp_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.GroupTypeUpdateFailed as err:
            self._notify_group_type_error(
                context, 'group_type.update', err, group_type=grp_type)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return self._view_builder.show(req, grp_type)
Example #16
0
    def _get_volumes(self, req, is_detail):
        """Returns a list of volumes, transformed through view builder."""

        context = req.environ['cinder.context']
        req_version = req.api_version_request

        params = req.params.copy()
        marker, limit, offset = common.get_pagination_params(params)
        sort_keys, sort_dirs = common.get_sort_params(params)
        filters = params

        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in filters:
            show_count = utils.get_bool_param('with_count', filters)
            filters.pop('with_count')

        self._process_volume_filtering(context=context,
                                       filters=filters,
                                       req_version=req_version)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in sort_keys:
            sort_keys[sort_keys.index('name')] = 'display_name'

        if 'name' in filters:
            filters['display_name'] = filters.pop('name')

        self._handle_time_comparison_filters(filters)

        strict = req.api_version_request.matches(mv.VOLUME_LIST_BOOTABLE, None)
        self.volume_api.check_volume_filters(filters, strict)

        volumes = self.volume_api.get_all(context,
                                          marker,
                                          limit,
                                          sort_keys=sort_keys,
                                          sort_dirs=sort_dirs,
                                          filters=filters.copy(),
                                          viewable_admin_meta=True,
                                          offset=offset)
        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'volume', filters)

        for volume in volumes:
            api_utils.add_visible_admin_metadata(volume)

        req.cache_db_volumes(volumes.objects)

        if is_detail:
            volumes = self._view_builder.detail_list(req, volumes, total_count)
        else:
            volumes = self._view_builder.summary_list(req, volumes,
                                                      total_count)
        return volumes
Example #17
0
    def _create(self, req, body):
        """Creates a new volume type."""
        context = req.environ['cinder.context']
        context.authorize(policy.MANAGE_POLICY)

        self.assert_valid_body(body, 'volume_type')

        vol_type = body['volume_type']
        name = vol_type.get('name', None)
        description = vol_type.get('description')
        specs = vol_type.get('extra_specs', {})
        utils.validate_dictionary_string_length(specs)
        is_public = utils.get_bool_param('os-volume-type-access:is_public',
                                         vol_type, True)

        if name is None or len(name.strip()) == 0:
            msg = _("Volume type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        utils.check_string_length(name,
                                  'Type name',
                                  min_length=1,
                                  max_length=255)

        if description is not None:
            utils.check_string_length(description,
                                      'Type description',
                                      min_length=0,
                                      max_length=255)

        try:
            volume_types.create(context,
                                name,
                                specs,
                                is_public,
                                description=description)
            vol_type = volume_types.get_volume_type_by_name(context, name)
            req.cache_resource(vol_type, name='types')
            self._notify_volume_type_info(context, 'volume_type.create',
                                          vol_type)

        except exception.VolumeTypeExists as err:
            self._notify_volume_type_error(context,
                                           'volume_type.create',
                                           err,
                                           volume_type=vol_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.VolumeTypeNotFoundByName as err:
            self._notify_volume_type_error(context,
                                           'volume_type.create',
                                           err,
                                           name=name)
            # Not found exception will be handled at the wsgi level
            raise

        return self._view_builder.show(req, vol_type)
    def get_pools(self, req):
        """List all active pools in scheduler."""
        context = req.environ['cinder.context']
        authorize(context, 'get_pools')

        # TODO(zhiteng) Add filters support
        detail = utils.get_bool_param('detail', req.params)
        pools = self.scheduler_api.get_pools(context, filters=None)

        return self._view_builder.pools(req, pools, detail)
Example #19
0
    def _get_volumes(self, req, is_detail):
        """Returns a list of volumes, transformed through view builder."""

        context = req.environ['cinder.context']
        req_version = req.api_version_request

        params = req.params.copy()
        marker, limit, offset = common.get_pagination_params(params)
        sort_keys, sort_dirs = common.get_sort_params(params)
        filters = params

        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in filters:
            show_count = utils.get_bool_param('with_count', filters)
            filters.pop('with_count')

        self._process_volume_filtering(context=context, filters=filters,
                                       req_version=req_version)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in sort_keys:
            sort_keys[sort_keys.index('name')] = 'display_name'

        if 'name' in filters:
            filters['display_name'] = filters.pop('name')

        strict = req.api_version_request.matches(
            mv.VOLUME_LIST_BOOTABLE, None)
        self.volume_api.check_volume_filters(filters, strict)

        volumes = self.volume_api.get_all(context, marker, limit,
                                          sort_keys=sort_keys,
                                          sort_dirs=sort_dirs,
                                          filters=filters.copy(),
                                          viewable_admin_meta=True,
                                          offset=offset)
        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'volume', filters)

        for volume in volumes:
            utils.add_visible_admin_metadata(volume)

        req.cache_db_volumes(volumes.objects)

        if is_detail:
            volumes = self._view_builder.detail_list(
                req, volumes, total_count)
        else:
            volumes = self._view_builder.summary_list(
                req, volumes, total_count)
        return volumes
Example #20
0
    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']

        cascade = utils.get_bool_param('cascade', req.params)

        LOG.info("Delete volume with id: %s", id)

        # Not found exception will be handled at the wsgi level
        volume = self.volume_api.get(context, id)
        self.volume_api.delete(context, volume, cascade=cascade)
        return webob.Response(status_int=http_client.ACCEPTED)
Example #21
0
    def _items(self, req, is_detail=True):
        """Returns a list of snapshots, transformed through view builder."""
        context = req.environ['cinder.context']
        req_version = req.api_version_request
        # Pop out non search_opts and create local variables
        search_opts = req.GET.copy()
        sort_keys, sort_dirs = common.get_sort_params(search_opts)
        marker, limit, offset = common.get_pagination_params(search_opts)

        req_version = req.api_version_request
        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in search_opts:
            show_count = utils.get_bool_param('with_count', search_opts)
            search_opts.pop('with_count')

        # process filters
        self._process_snapshot_filtering(context=context,
                                         filters=search_opts,
                                         req_version=req_version)
        # process snapshot filters to appropriate formats if required
        self._format_snapshot_filter_options(search_opts)

        req_version = req.api_version_request
        if req_version.matches(mv.SNAPSHOT_SORT, None) and 'name' in sort_keys:
            sort_keys[sort_keys.index('name')] = 'display_name'

        # NOTE(thingee): v3 API allows name instead of display_name
        if 'name' in search_opts:
            search_opts['display_name'] = search_opts.pop('name')

        snapshots = self.volume_api.get_all_snapshots(
            context,
            search_opts=search_opts.copy(),
            marker=marker,
            limit=limit,
            sort_keys=sort_keys,
            sort_dirs=sort_dirs,
            offset=offset)
        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'snapshot', search_opts)

        req.cache_db_snapshots(snapshots.objects)

        if is_detail:
            snapshots = self._view_builder.detail_list(req, snapshots.objects,
                                                       total_count)
        else:
            snapshots = self._view_builder.summary_list(req, snapshots.objects,
                                                        total_count)
        return snapshots
Example #22
0
    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']

        cascade = utils.get_bool_param('cascade', req.params)

        LOG.info(_LI("Delete volume with id: %s"), id)

        # Not found exception will be handled at the wsgi level
        volume = self.volume_api.get(context, id)
        self.volume_api.delete(context, volume, cascade=cascade)
        return webob.Response(status_int=202)
Example #23
0
    def create(self, req, body):
        """Creates a new group type."""
        context = req.environ['cinder.context']
        self._check_policy(context)

        self.assert_valid_body(body, 'group_type')

        grp_type = body['group_type']
        name = grp_type.get('name', None)
        description = grp_type.get('description')
        specs = grp_type.get('group_specs', {})
        is_public = utils.get_bool_param('is_public', grp_type, True)

        if name is None or len(name.strip()) == 0:
            msg = _("Group type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        utils.check_string_length(name,
                                  'Type name',
                                  min_length=1,
                                  max_length=255)

        if description is not None:
            utils.check_string_length(description,
                                      'Type description',
                                      min_length=0,
                                      max_length=255)

        try:
            group_types.create(context,
                               name,
                               specs,
                               is_public,
                               description=description)
            grp_type = group_types.get_group_type_by_name(context, name)
            req.cache_resource(grp_type, name='group_types')
            self._notify_group_type_info(context, 'group_type.create',
                                         grp_type)

        except exception.GroupTypeExists as err:
            self._notify_group_type_error(context,
                                          'group_type.create',
                                          err,
                                          group_type=grp_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.GroupTypeNotFoundByName as err:
            self._notify_group_type_error(context,
                                          'group_type.create',
                                          err,
                                          name=name)
            raise webob.exc.HTTPNotFound(explanation=err.msg)

        return self._view_builder.show(req, grp_type)
Example #24
0
    def _items(self, req, is_detail=True):
        """Returns a list of snapshots, transformed through view builder."""
        context = req.environ['cinder.context']
        req_version = req.api_version_request
        # Pop out non search_opts and create local variables
        search_opts = req.GET.copy()
        sort_keys, sort_dirs = common.get_sort_params(search_opts)
        marker, limit, offset = common.get_pagination_params(search_opts)

        req_version = req.api_version_request
        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in search_opts:
            show_count = utils.get_bool_param('with_count', search_opts)
            search_opts.pop('with_count')

        # process filters
        self._process_snapshot_filtering(context=context,
                                         filters=search_opts,
                                         req_version=req_version)
        # process snapshot filters to appropriate formats if required
        self._format_snapshot_filter_options(search_opts)

        req_version = req.api_version_request
        if req_version.matches(mv.SNAPSHOT_SORT, None) and 'name' in sort_keys:
            sort_keys[sort_keys.index('name')] = 'display_name'

        # NOTE(thingee): v3 API allows name instead of display_name
        if 'name' in search_opts:
            search_opts['display_name'] = search_opts.pop('name')

        snapshots = self.volume_api.get_all_snapshots(
            context,
            search_opts=search_opts.copy(),
            marker=marker,
            limit=limit,
            sort_keys=sort_keys,
            sort_dirs=sort_dirs,
            offset=offset)
        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'snapshot', search_opts)

        req.cache_db_snapshots(snapshots.objects)

        if is_detail:
            snapshots = self._view_builder.detail_list(req, snapshots.objects,
                                                       total_count)
        else:
            snapshots = self._view_builder.summary_list(
                req, snapshots.objects, total_count)
        return snapshots
Example #25
0
    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']

        cascade = utils.get_bool_param('cascade', req.params)

        LOG.info(_LI("Delete volume with id: %s"), id, context=context)

        try:
            volume = self.volume_api.get(context, id)
            self.volume_api.delete(context, volume, cascade=cascade)
        except exception.VolumeNotFound as error:
            raise exc.HTTPNotFound(explanation=error.msg)
        return webob.Response(status_int=202)
Example #26
0
    def delete(self, req, id):
        """Delete a volume."""
        context = req.environ['cinder.context']

        cascade = utils.get_bool_param('cascade', req.params)

        LOG.info(_LI("Delete volume with id: %s"), id, context=context)

        try:
            volume = self.volume_api.get(context, id)
            self.volume_api.delete(context, volume, cascade=cascade)
        except exception.VolumeNotFound as error:
            raise exc.HTTPNotFound(explanation=error.msg)
        return webob.Response(status_int=202)
Example #27
0
    def _create(self, req, body):
        """Creates a new volume type."""
        context = req.environ['cinder.context']
        authorize(context)

        self.assert_valid_body(body, 'volume_type')

        vol_type = body['volume_type']
        name = vol_type.get('name', None)
        description = vol_type.get('description')
        specs = vol_type.get('extra_specs', {})
        utils.validate_dictionary_string_length(specs)
        is_public = utils.get_bool_param('os-volume-type-access:is_public',
                                         vol_type, True)

        if name is None or len(name.strip()) == 0:
            msg = _("Volume type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        utils.check_string_length(name, 'Type name',
                                  min_length=1, max_length=255)

        if description is not None:
            utils.check_string_length(description, 'Type description',
                                      min_length=0, max_length=255)

        try:
            volume_types.create(context,
                                name,
                                specs,
                                is_public,
                                description=description)
            vol_type = volume_types.get_volume_type_by_name(context, name)
            req.cache_resource(vol_type, name='types')
            self._notify_volume_type_info(
                context, 'volume_type.create', vol_type)

        except exception.VolumeTypeExists as err:
            self._notify_volume_type_error(
                context, 'volume_type.create', err, volume_type=vol_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.VolumeTypeNotFoundByName as err:
            self._notify_volume_type_error(
                context, 'volume_type.create', err, name=name)
            # Not found exception will be handled at the wsgi level
            raise

        return self._view_builder.show(req, vol_type)
Example #28
0
def stub_volume_create(self, context, size, name, description, snapshot=None, **param):
    vol = stub_volume(DEFAULT_VOL_ID)
    vol["size"] = size
    vol["display_name"] = name
    vol["display_description"] = description
    source_volume = param.get("source_volume") or {}
    vol["source_volid"] = source_volume.get("id")
    vol["bootable"] = False
    vol["volume_attachment"] = []
    vol["multiattach"] = utils.get_bool_param("multiattach", param)
    try:
        vol["snapshot_id"] = snapshot["id"]
    except (KeyError, TypeError):
        vol["snapshot_id"] = None
    vol["availability_zone"] = param.get("availability_zone", "fakeaz")
    return vol
Example #29
0
    def _format_snapshot_filter_options(self, search_opts):
        """Convert valid filter options to correct expected format"""

        # Get the dict object out of queried metadata
        # convert metadata query value from string to dict
        if 'metadata' in search_opts.keys():
            try:
                search_opts['metadata'] = ast.literal_eval(
                    search_opts['metadata'])
            except (ValueError, SyntaxError):
                LOG.debug('Could not evaluate value %s, assuming string',
                          search_opts['metadata'])

        if 'use_quota' in search_opts:
            search_opts['use_quota'] = utils.get_bool_param(
                'use_quota', search_opts)
Example #30
0
def fake_volume_create(self, context, size, name, description, snapshot=None,
                       **param):
    vol = create_fake_volume(DEFAULT_VOL_ID)
    vol['size'] = size
    vol['display_name'] = name
    vol['display_description'] = description
    source_volume = param.get('source_volume') or {}
    vol['source_volid'] = source_volume.get('id')
    vol['bootable'] = False
    vol['volume_attachment'] = []
    vol['multiattach'] = utils.get_bool_param('multiattach', param)
    try:
        vol['snapshot_id'] = snapshot['id']
    except (KeyError, TypeError):
        vol['snapshot_id'] = None
    vol['availability_zone'] = param.get('availability_zone', 'fakeaz')
    return vol
Example #31
0
    def get_pools(self, req):
        """List all active pools in scheduler."""
        context = req.environ['cinder.context']
        authorize(context, 'get_pools')

        detail = utils.get_bool_param('detail', req.params)

        req_version = req.api_version_request

        if req_version.matches(GET_POOL_NAME_FILTER_MICRO_VERSION):
            filters = req.params.copy()
            filters.pop('detail', None)
            pools = self.scheduler_api.get_pools(context, filters=filters)
        else:
            pools = self.scheduler_api.get_pools(context, filters=None)

        return self._view_builder.pools(req, pools, detail)
Example #32
0
    def create(self, req, body):
        """Creates a new group type."""
        context = req.environ['cinder.context']
        self._check_policy(context)

        self.assert_valid_body(body, 'group_type')

        grp_type = body['group_type']
        name = grp_type.get('name', None)
        description = grp_type.get('description')
        specs = grp_type.get('group_specs', {})
        is_public = utils.get_bool_param('is_public', grp_type, True)

        if name is None or len(name.strip()) == 0:
            msg = _("Group type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        utils.check_string_length(name, 'Type name',
                                  min_length=1, max_length=255)

        if description is not None:
            utils.check_string_length(description, 'Type description',
                                      min_length=0, max_length=255)

        try:
            group_types.create(context,
                               name,
                               specs,
                               is_public,
                               description=description)
            grp_type = group_types.get_group_type_by_name(context, name)
            req.cache_resource(grp_type, name='group_types')
            self._notify_group_type_info(
                context, 'group_type.create', grp_type)

        except exception.GroupTypeExists as err:
            self._notify_group_type_error(
                context, 'group_type.create', err, group_type=grp_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.GroupTypeNotFoundByName as err:
            self._notify_group_type_error(
                context, 'group_type.create', err, name=name)
            raise webob.exc.HTTPNotFound(explanation=err.msg)

        return self._view_builder.show(req, grp_type)
Example #33
0
    def get_pools(self, req):
        """List all active pools in scheduler."""
        context = req.environ['cinder.context']
        authorize(context, 'get_pools')

        detail = utils.get_bool_param('detail', req.params)

        req_version = req.api_version_request
        filters = req.params.copy()
        filters.pop('detail', None)

        self._process_pool_filtering(context=context,
                                     filters=filters,
                                     req_version=req_version)

        pools = self.scheduler_api.get_pools(context, filters=filters)

        return self._view_builder.pools(req, pools, detail)
Example #34
0
    def _get_backups(self, req, is_detail):
        """Returns a list of backups, transformed through view builder."""
        context = req.environ['cinder.context']
        filters = req.params.copy()
        req_version = req.api_version_request
        marker, limit, offset = common.get_pagination_params(filters)
        sort_keys, sort_dirs = common.get_sort_params(filters)

        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in filters:
            show_count = utils.get_bool_param('with_count', filters)
            filters.pop('with_count')
        self._convert_sort_name(req_version, sort_keys)
        self._process_backup_filtering(context=context,
                                       filters=filters,
                                       req_version=req_version)

        if 'name' in filters:
            filters['display_name'] = filters.pop('name')

        backups = self.backup_api.get_all(
            context,
            search_opts=filters.copy(),
            marker=marker,
            limit=limit,
            offset=offset,
            sort_keys=sort_keys,
            sort_dirs=sort_dirs,
        )

        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'backup', filters)
        req.cache_db_backups(backups.objects)

        if is_detail:
            backups = self._view_builder.detail_list(req, backups.objects,
                                                     total_count)
        else:
            backups = self._view_builder.summary_list(req, backups.objects,
                                                      total_count)
        return backups
Example #35
0
    def show(self, req, id):
        """Show quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects admin of current project, immediate
        parent of the project or the CLOUD admin are able to perform
        a show.

        :param req: request
        :param id: target project id that needs to be shown
        """
        context = req.environ['cinder.context']
        authorize_show(context)
        params = req.params
        target_project_id = id

        if not hasattr(params, '__call__') and 'usage' in params:
            usage = utils.get_bool_param('usage', params)
        else:
            usage = False

        if QUOTAS.using_nested_quotas():
            # With hierarchical projects, only the admin of the current project
            # or the root project has privilege to perform quota show
            # operations.
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id)
            context_project = quota_utils.get_project_hierarchy(
                context,
                context.project_id,
                subtree_as_ids=True,
                is_admin_project=context.is_admin)

            self._authorize_show(context_project, target_project)

        try:
            sqlalchemy_api.authorize_project_context(context,
                                                     target_project_id)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        quotas = self._get_quotas(context, target_project_id, usage)
        return self._format_quota_set(target_project_id, quotas)
Example #36
0
    def show(self, req, id):
        """Show quota for a particular tenant

        :param req: request
        :param id: target project id that needs to be shown
        """
        context = req.environ['cinder.context']
        params = req.params
        target_project_id = id
        context.authorize(policy.SHOW_POLICY,
                          target={'project_id': target_project_id})

        if not hasattr(params, '__call__') and 'usage' in params:
            usage = utils.get_bool_param('usage', params)
        else:
            usage = False

        quotas = self._get_quotas(context, target_project_id, usage)
        return self._format_quota_set(target_project_id, quotas)
Example #37
0
    def detail(self, request, group):
        """Detailed view of a single group."""
        context = request.environ['cinder.context']
        group_ref = {
            'group': {
                'id': group.id,
                'status': group.status,
                'availability_zone': group.availability_zone,
                'created_at': group.created_at,
                'name': group.name,
                'description': group.description,
                'group_type': group.group_type_id,
                'volume_types': [v_type.id for v_type in group.volume_types],
            }
        }

        req_version = request.api_version_request
        # Add group_snapshot_id and source_group_id if min version is greater
        # than or equal to GROUP_SNAPSHOTS.
        if req_version.matches(mv.GROUP_SNAPSHOTS, None):
            group_ref['group']['group_snapshot_id'] = group.group_snapshot_id
            group_ref['group']['source_group_id'] = group.source_group_id

        # Add volumes if min version is greater than or equal to
        # GROUP_VOLUME_LIST.
        if req_version.matches(mv.GROUP_VOLUME_LIST, None):
            if utils.get_bool_param('list_volume', request.params):
                group_ref['group']['volumes'] = [
                    volume.id for volume in group.volumes
                ]

        # Add replication_status if min version is greater than or equal
        # to GROUP_REPLICATION.
        if req_version.matches(mv.GROUP_REPLICATION, None):
            group_ref['group']['replication_status'] = group.replication_status

        if req_version.matches(mv.GROUP_PROJECT_ID, None):
            if context.authorize(policy.GROUP_ATTRIBUTES_POLICY, fatal=False):
                group_ref['group']['project_id'] = group.project_id

        return group_ref
Example #38
0
    def _get_backups(self, req, is_detail):
        """Returns a list of backups, transformed through view builder."""
        context = req.environ['cinder.context']
        filters = req.params.copy()
        req_version = req.api_version_request
        marker, limit, offset = common.get_pagination_params(filters)
        sort_keys, sort_dirs = common.get_sort_params(filters)

        show_count = False
        if req_version.matches(
                mv.SUPPORT_COUNT_INFO) and 'with_count' in filters:
            show_count = utils.get_bool_param('with_count', filters)
            filters.pop('with_count')
        self._convert_sort_name(req_version, sort_keys)
        self._process_backup_filtering(context=context, filters=filters,
                                       req_version=req_version)

        if 'name' in filters:
            filters['display_name'] = filters.pop('name')

        backups = self.backup_api.get_all(context, search_opts=filters.copy(),
                                          marker=marker,
                                          limit=limit,
                                          offset=offset,
                                          sort_keys=sort_keys,
                                          sort_dirs=sort_dirs,
                                          )

        total_count = None
        if show_count:
            total_count = self.volume_api.calculate_resource_count(
                context, 'backup', filters)
        req.cache_db_backups(backups.objects)

        if is_detail:
            backups = self._view_builder.detail_list(req, backups.objects,
                                                     total_count)
        else:
            backups = self._view_builder.summary_list(req, backups.objects,
                                                      total_count)
        return backups
Example #39
0
    def show(self, req, id):
        """Show quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects admin of current project, immediate
        parent of the project or the CLOUD admin are able to perform
        a show.

        :param req: request
        :param id: target project id that needs to be shown
        """
        context = req.environ['cinder.context']
        authorize_show(context)
        params = req.params
        target_project_id = id

        if not hasattr(params, '__call__') and 'usage' in params:
            usage = utils.get_bool_param('usage', params)
        else:
            usage = False

        if QUOTAS.using_nested_quotas():
            # With hierarchical projects, only the admin of the current project
            # or the root project has privilege to perform quota show
            # operations.
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id)
            context_project = quota_utils.get_project_hierarchy(
                context, context.project_id, subtree_as_ids=True,
                is_admin_project=context.is_admin)

            self._authorize_show(context_project, target_project)

        try:
            sqlalchemy_api.authorize_project_context(context,
                                                     target_project_id)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        quotas = self._get_quotas(context, target_project_id, usage)
        return self._format_quota_set(target_project_id, quotas)
    def get_pools(self, req):
        """List all active pools in scheduler."""
        context = req.environ['cinder.context']
        authorize(context, 'get_pools')

        detail = utils.get_bool_param('detail', req.params)

        req_version = req.api_version_request
        filters = req.params.copy()
        filters.pop('detail', None)

        self._process_pool_filtering(context=context,
                                     filters=filters,
                                     req_version=req_version)

        if not req_version.matches(GET_POOL_VOLUME_TYPE_FILTER_MICRO_VERSION):
            filters.pop('volume_type', None)

        pools = self.scheduler_api.get_pools(context, filters=filters)

        return self._view_builder.pools(req, pools, detail)
    def get_pools(self, req):
        """List all active pools in scheduler."""
        context = req.environ['cinder.context']
        authorize(context, 'get_pools')

        detail = utils.get_bool_param('detail', req.params)

        req_version = req.api_version_request
        filters = req.params.copy()
        filters.pop('detail', None)

        self._process_pool_filtering(context=context,
                                     filters=filters,
                                     req_version=req_version)

        if not req_version.matches(GET_POOL_VOLUME_TYPE_FILTER_MICRO_VERSION):
            filters.pop('volume_type', None)

        pools = self.scheduler_api.get_pools(context, filters=filters)

        return self._view_builder.pools(req, pools, detail)
Example #42
0
    def detail(self, request, group):
        """Detailed view of a single group."""
        context = request.environ['cinder.context']
        group_ref = {
            'group': {
                'id': group.id,
                'status': group.status,
                'availability_zone': group.availability_zone,
                'created_at': group.created_at,
                'name': group.name,
                'description': group.description,
                'group_type': group.group_type_id,
                'volume_types': [v_type.id for v_type in group.volume_types],
            }
        }

        req_version = request.api_version_request
        # Add group_snapshot_id and source_group_id if min version is greater
        # than or equal to GROUP_SNAPSHOTS.
        if req_version.matches(mv.GROUP_SNAPSHOTS, None):
            group_ref['group']['group_snapshot_id'] = group.group_snapshot_id
            group_ref['group']['source_group_id'] = group.source_group_id

        # Add volumes if min version is greater than or equal to
        # GROUP_VOLUME_LIST.
        if req_version.matches(mv.GROUP_VOLUME_LIST, None):
            if utils.get_bool_param('list_volume', request.params):
                group_ref['group']['volumes'] = [volume.id
                                                 for volume in group.volumes]

        # Add replication_status if min version is greater than or equal
        # to GROUP_REPLICATION.
        if req_version.matches(mv.GROUP_REPLICATION, None):
            group_ref['group']['replication_status'] = group.replication_status

        if req_version.matches(mv.GROUP_GROUPSNAPSHOT_PROJECT_ID, None):
            if context.authorize(policy.GROUP_ATTRIBUTES_POLICY, fatal=False):
                group_ref['group']['project_id'] = group.project_id

        return group_ref
Example #43
0
    def _volume_upload_image(self, req, id, body):
        """Uploads the specified volume to image service."""
        context = req.environ['cinder.context']
        params = body['os-volume_upload_image']
        req_version = req.api_version_request
        if not params.get("image_name"):
            msg = _("No image_name was specified in request.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        force = params.get('force', 'False')
        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            err_msg = encodeutils.exception_to_unicode(error)
            msg = _("Invalid value for 'force': '%s'") % err_msg
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            volume = self.volume_api.get(context, id)
        except exception.VolumeNotFound as error:
            raise webob.exc.HTTPNotFound(explanation=error.msg)

        authorize(context, "upload_image")
        # check for valid disk-format
        disk_format = params.get("disk_format", "raw")
        if not image_utils.validate_disk_format(disk_format):
            msg = _("Invalid disk-format '%(disk_format)s' is specified. "
                    "Allowed disk-formats are %(allowed_disk_formats)s.") % {
                        "disk_format": disk_format,
                        "allowed_disk_formats": ", ".join(
                            image_utils.VALID_DISK_FORMATS)
                    }
            raise webob.exc.HTTPBadRequest(explanation=msg)

        image_metadata = {
            "container_format": params.get("container_format", "bare"),
            "disk_format": disk_format,
            "name": params["image_name"]
        }

        if req_version >= api_version_request.APIVersionRequest('3.1'):

            image_metadata['visibility'] = params.get('visibility', 'private')
            image_metadata['protected'] = params.get('protected', 'False')

            if image_metadata['visibility'] == 'public':
                authorize(context, 'upload_public')

            if CONF.glance_api_version != 2:
                # Replace visibility with is_public for Glance V1
                image_metadata['is_public'] = (
                    image_metadata['visibility'] == 'public')
                image_metadata.pop('visibility', None)

            image_metadata['protected'] = (utils.get_bool_param(
                'protected', image_metadata))

        try:
            response = self.volume_api.copy_volume_to_image(
                context, volume, image_metadata, force)
        except exception.InvalidVolume as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except ValueError as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        except messaging.RemoteError as error:
            msg = "%(err_type)s: %(err_msg)s" % {
                'err_type': error.exc_type,
                'err_msg': error.value
            }
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        return {'os-volume_upload_image': response}
Example #44
0
    def _volume_upload_image(self, req, id, body):
        """Uploads the specified volume to image service."""
        context = req.environ["cinder.context"]
        params = body["os-volume_upload_image"]
        req_version = req.api_version_request
        if not params.get("image_name"):
            msg = _("No image_name was specified in request.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        force = params.get("force", "False")
        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            err_msg = encodeutils.exception_to_unicode(error)
            msg = _("Invalid value for 'force': '%s'") % err_msg
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            volume = self.volume_api.get(context, id)
        except exception.VolumeNotFound as error:
            raise webob.exc.HTTPNotFound(explanation=error.msg)

        authorize(context, "upload_image")
        # check for valid disk-format
        disk_format = params.get("disk_format", "raw")
        if not image_utils.validate_disk_format(disk_format):
            msg = _(
                "Invalid disk-format '%(disk_format)s' is specified. "
                "Allowed disk-formats are %(allowed_disk_formats)s."
            ) % {"disk_format": disk_format, "allowed_disk_formats": ", ".join(image_utils.VALID_DISK_FORMATS)}
            raise webob.exc.HTTPBadRequest(explanation=msg)

        image_metadata = {
            "container_format": params.get("container_format", "bare"),
            "disk_format": disk_format,
            "name": params["image_name"],
        }

        if req_version >= api_version_request.APIVersionRequest("3.1"):

            image_metadata["visibility"] = params.get("visibility", "private")
            image_metadata["protected"] = params.get("protected", "False")

            if image_metadata["visibility"] == "public":
                authorize(context, "upload_public")

            if CONF.glance_api_version != 2:
                # Replace visibility with is_public for Glance V1
                image_metadata["is_public"] = image_metadata["visibility"] == "public"
                image_metadata.pop("visibility", None)

            image_metadata["protected"] = utils.get_bool_param("protected", image_metadata)

        try:
            response = self.volume_api.copy_volume_to_image(context, volume, image_metadata, force)
        except exception.InvalidVolume as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except ValueError as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        except messaging.RemoteError as error:
            msg = "%(err_type)s: %(err_msg)s" % {"err_type": error.exc_type, "err_msg": error.value}
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        return {"os-volume_upload_image": response}
Example #45
0
    def update(self, req, id, body):
        # Update description for a given group type.
        context = req.environ['cinder.context']
        self._check_policy(context)

        self.assert_valid_body(body, 'group_type')

        grp_type = body['group_type']
        description = grp_type.get('description')
        name = grp_type.get('name')
        is_public = grp_type.get('is_public')

        # Name and description can not be both None.
        # If name specified, name can not be empty.
        if name and len(name.strip()) == 0:
            msg = _("Group type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if name is None and description is None and is_public is None:
            msg = _("Specify group type name, description or "
                    "a combination thereof.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if is_public is not None:
            is_public = utils.get_bool_param('is_public', grp_type)

        if name:
            utils.check_string_length(name,
                                      'Type name',
                                      min_length=1,
                                      max_length=255)

        if description is not None:
            utils.check_string_length(description,
                                      'Type description',
                                      min_length=0,
                                      max_length=255)

        try:
            group_types.update(context,
                               id,
                               name,
                               description,
                               is_public=is_public)
            # Get the updated
            grp_type = group_types.get_group_type(context, id)
            req.cache_resource(grp_type, name='group_types')
            self._notify_group_type_info(context, 'group_type.update',
                                         grp_type)

        except exception.GroupTypeNotFound as err:
            self._notify_group_type_error(context,
                                          'group_type.update',
                                          err,
                                          id=id)
            raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
        except exception.GroupTypeExists as err:
            self._notify_group_type_error(context,
                                          'group_type.update',
                                          err,
                                          group_type=grp_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.GroupTypeUpdateFailed as err:
            self._notify_group_type_error(context,
                                          'group_type.update',
                                          err,
                                          group_type=grp_type)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return self._view_builder.show(req, grp_type)
Example #46
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>,
             '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):
        """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)
Example #48
0
    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']

        # Check up front for legacy replication parameters to quick fail
        source_replica = volume.get('source_replica')
        if source_replica:
            msg = _("Creating a volume from a replica source was part of the "
                    "replication v1 implementation which is no longer "
                    "available.")
            raise exception.InvalidInput(reason=msg)

        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'] = (
                objects.VolumeType.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:
            if not uuidutils.is_uuid_like(source_volid):
                msg = _("Source volume ID '%s' must be a "
                        "valid UUID.") % source_volid
                raise exc.HTTPBadRequest(explanation=msg)
            # 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:
            if not uuidutils.is_uuid_like(consistencygroup_id):
                msg = _("Consistency group ID '%s' must be a "
                        "valid UUID.") % consistencygroup_id
                raise exc.HTTPBadRequest(explanation=msg)
            # 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)

        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
Example #49
0
    def _update(self, req, id, body):
        # Update description for a given volume type.
        context = req.environ['cinder.context']
        context.authorize(policy.MANAGE_POLICY)

        self.assert_valid_body(body, 'volume_type')

        vol_type = body['volume_type']
        description = vol_type.get('description')
        name = vol_type.get('name')
        is_public = vol_type.get('is_public')

        # Name and description can not be both None.
        # If name specified, name can not be empty.
        if name and len(name.strip()) == 0:
            msg = _("Volume type name can not be empty.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if name is None and description is None and is_public is None:
            msg = _("Specify volume type name, description, is_public or "
                    "a combination thereof.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        if is_public is not None:
            is_public = utils.get_bool_param('is_public', vol_type)

        if name:
            utils.check_string_length(name,
                                      'Type name',
                                      min_length=1,
                                      max_length=255)

        if description is not None:
            utils.check_string_length(description,
                                      'Type description',
                                      min_length=0,
                                      max_length=255)

        try:
            volume_types.update(context,
                                id,
                                name,
                                description,
                                is_public=is_public)
            # Get the updated
            vol_type = volume_types.get_volume_type(context, id)
            req.cache_resource(vol_type, name='types')
            self._notify_volume_type_info(context, 'volume_type.update',
                                          vol_type)

        except exception.VolumeTypeNotFound as err:
            self._notify_volume_type_error(context,
                                           'volume_type.update',
                                           err,
                                           id=id)
            # Not found exception will be handled at the wsgi level
            raise
        except exception.VolumeTypeExists as err:
            self._notify_volume_type_error(context,
                                           'volume_type.update',
                                           err,
                                           volume_type=vol_type)
            raise webob.exc.HTTPConflict(explanation=six.text_type(err))
        except exception.VolumeTypeUpdateFailed as err:
            self._notify_volume_type_error(context,
                                           'volume_type.update',
                                           err,
                                           volume_type=vol_type)
            raise webob.exc.HTTPInternalServerError(
                explanation=six.text_type(err))

        return self._view_builder.show(req, vol_type)
Example #50
0
    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))

        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
Example #51
0
    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
Example #52
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>,
             '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)
Example #53
0
    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))

        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 _volume_upload_image(self, req, id, body):
        """Uploads the specified volume to image service."""
        context = req.environ['cinder.context']
        params = body['os-volume_upload_image']
        req_version = req.api_version_request
        if not params.get("image_name"):
            msg = _("No image_name was specified in request.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        force = params.get('force', 'False')
        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            err_msg = encodeutils.exception_to_unicode(error)
            msg = _("Invalid value for 'force': '%s'") % err_msg
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # Not found exception will be handled at the wsgi level
        volume = self.volume_api.get(context, id)

        authorize(context, "upload_image")
        # check for valid disk-format
        disk_format = params.get("disk_format", "raw")
        if not image_utils.validate_disk_format(disk_format):
            msg = _("Invalid disk-format '%(disk_format)s' is specified. "
                    "Allowed disk-formats are %(allowed_disk_formats)s.") % {
                "disk_format": disk_format,
                "allowed_disk_formats": ", ".join(
                    image_utils.VALID_DISK_FORMATS)
            }
            raise webob.exc.HTTPBadRequest(explanation=msg)
        if disk_format == "parallels":
            disk_format = "ploop"

        image_metadata = {"container_format": params.get(
            "container_format", "bare"),
            "disk_format": disk_format,
            "name": params["image_name"]}

        if volume.encryption_key_id:
            # Clone volume encryption key: the current key cannot
            # be reused because it will be deleted when the volume is
            # deleted.
            # TODO(eharney): Currently, there is no mechanism to remove
            # these keys, because Glance will not delete the key from
            # Barbican when the image is deleted.
            encryption_key_id = self._key_manager.store(
                context,
                self._key_manager.get(context, volume.encryption_key_id))

            image_metadata['cinder_encryption_key_id'] = encryption_key_id

        if req_version >= api_version_request.APIVersionRequest('3.1'):

            image_metadata['visibility'] = params.get('visibility', 'private')
            image_metadata['protected'] = params.get('protected', 'False')

            if image_metadata['visibility'] == 'public':
                authorize(context, 'upload_public')

            if CONF.glance_api_version != 2:
                # Replace visibility with is_public for Glance V1
                image_metadata['is_public'] = (
                    image_metadata['visibility'] == 'public')
                image_metadata.pop('visibility', None)

            image_metadata['protected'] = (
                utils.get_bool_param('protected', image_metadata))

        try:
            response = self.volume_api.copy_volume_to_image(context,
                                                            volume,
                                                            image_metadata,
                                                            force)
        except exception.InvalidVolume as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except ValueError as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        except messaging.RemoteError as error:
            msg = "%(err_type)s: %(err_msg)s" % {'err_type': error.exc_type,
                                                 'err_msg': error.value}
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        return {'os-volume_upload_image': response}
Example #55
0
    def _volume_upload_image(self, req, id, body):
        """Uploads the specified volume to image service."""
        context = req.environ['cinder.context']
        params = body['os-volume_upload_image']
        req_version = req.api_version_request
        if not params.get("image_name"):
            msg = _("No image_name was specified in request.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        force = params.get('force', 'False')
        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            err_msg = encodeutils.exception_to_unicode(error)
            msg = _("Invalid value for 'force': '%s'") % err_msg
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # Not found exception will be handled at the wsgi level
        volume = self.volume_api.get(context, id)

        authorize(context, "upload_image")
        # check for valid disk-format
        disk_format = params.get("disk_format", "raw")
        if not image_utils.validate_disk_format(disk_format):
            msg = _("Invalid disk-format '%(disk_format)s' is specified. "
                    "Allowed disk-formats are %(allowed_disk_formats)s.") % {
                        "disk_format": disk_format,
                        "allowed_disk_formats": ", ".join(
                            image_utils.VALID_DISK_FORMATS)
                    }
            raise webob.exc.HTTPBadRequest(explanation=msg)

        image_metadata = {
            "container_format": params.get("container_format", "bare"),
            "disk_format": disk_format,
            "name": params["image_name"]
        }

        if volume.encryption_key_id:
            # Clone volume encryption key: the current key cannot
            # be reused because it will be deleted when the volume is
            # deleted.
            # TODO(eharney): Currently, there is no mechanism to remove
            # these keys, because Glance will not delete the key from
            # Barbican when the image is deleted.
            encryption_key_id = self._key_manager.store(
                context,
                self._key_manager.get(context, volume.encryption_key_id))

            image_metadata['cinder_encryption_key_id'] = encryption_key_id

        if req_version >= api_version_request.APIVersionRequest('3.1'):

            image_metadata['visibility'] = params.get('visibility', 'private')
            image_metadata['protected'] = params.get('protected', 'False')

            if image_metadata['visibility'] == 'public':
                authorize(context, 'upload_public')

            if CONF.glance_api_version != 2:
                # Replace visibility with is_public for Glance V1
                image_metadata['is_public'] = (
                    image_metadata['visibility'] == 'public')
                image_metadata.pop('visibility', None)

            image_metadata['protected'] = (utils.get_bool_param(
                'protected', image_metadata))

        try:
            response = self.volume_api.copy_volume_to_image(
                context, volume, image_metadata, force)
        except exception.InvalidVolume as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except ValueError as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        except messaging.RemoteError as error:
            msg = "%(err_type)s: %(err_msg)s" % {
                'err_type': error.exc_type,
                'err_msg': error.value
            }
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        return {'os-volume_upload_image': response}
Example #56
0
    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']

        # Check up front for legacy replication parameters to quick fail
        source_replica = volume.get('source_replica')
        if source_replica:
            msg = _("Creating a volume from a replica source was part of the "
                    "replication v1 implementation which is no longer "
                    "available.")
            raise exception.InvalidInput(reason=msg)

        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'] = (objects.VolumeType.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:
            if not uuidutils.is_uuid_like(source_volid):
                msg = _("Source volume ID '%s' must be a "
                        "valid UUID.") % source_volid
                raise exc.HTTPBadRequest(explanation=msg)
            # 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:
            if not uuidutils.is_uuid_like(consistencygroup_id):
                msg = _("Consistency group ID '%s' must be a "
                        "valid UUID.") % consistencygroup_id
                raise exc.HTTPBadRequest(explanation=msg)
            # 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)

        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
Example #57
0
    def _prepare_params(self, ctxt, params, allowed):
        if not allowed.issuperset(params):
            invalid_keys = set(params).difference(allowed)
            msg = _('Invalid filter keys: %s') % ', '.join(invalid_keys)
            raise exception.InvalidInput(reason=msg)

        if params.get('binary') not in (None, constants.VOLUME_BINARY,
                                        'cinder-scheduler'):
            msg = _('binary must be empty or set to cinder-volume or '
                    'cinder-scheduler')
            raise exception.InvalidInput(reason=msg)

        for boolean in ('disabled', 'is_up'):
            if params.get(boolean) is not None:
                params[boolean] = utils.get_bool_param(boolean, params)

        resource_type = params.get('resource_type')

        if resource_type:
            resource_type = resource_type.title()
            types = cleanable.CinderCleanableObject.cleanable_resource_types
            if resource_type not in types:
                valid_types = utils.build_or_str(types)
                msg = _('Resource type %(resource_type)s not valid,'
                        ' must be %(valid_types)s')
                msg = msg % {
                    "resource_type": resource_type,
                    "valid_types": valid_types
                }
                raise exception.InvalidInput(reason=msg)
            params['resource_type'] = resource_type

        resource_id = params.get('resource_id')
        if resource_id:
            if not uuidutils.is_uuid_like(resource_id):
                msg = (_('Resource ID must be a UUID, and %s is not.') %
                       resource_id)
                raise exception.InvalidInput(reason=msg)

            # If we have the resource type but we don't have where it is
            # located, we get it from the DB to limit the distribution of the
            # request by the scheduler, otherwise it will be distributed to all
            # the services.
            location_keys = {'service_id', 'cluster_name', 'host'}
            if not location_keys.intersection(params):
                workers = db.worker_get_all(ctxt,
                                            resource_id=resource_id,
                                            binary=params.get('binary'),
                                            resource_type=resource_type)

                if len(workers) == 0:
                    msg = (_('There is no resource with UUID %s pending '
                             'cleanup.'), resource_id)
                    raise exception.InvalidInput(reason=msg)
                if len(workers) > 1:
                    msg = (_('There are multiple resources with UUID %s '
                             'pending cleanup.  Please be more specific.'),
                           resource_id)
                    raise exception.InvalidInput(reason=msg)

                worker = workers[0]
                params.update(service_id=worker.service_id,
                              resource_type=worker.resource_type)

        return params
Example #58
0
    def _volume_upload_image(self, req, id, body):
        """Uploads the specified volume to image service."""
        context = req.environ['cinder.context']
        params = body['os-volume_upload_image']
        req_version = req.api_version_request
        if not params.get("image_name"):
            msg = _("No image_name was specified in request.")
            raise webob.exc.HTTPBadRequest(explanation=msg)

        force = params.get('force', 'False')
        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            err_msg = encodeutils.exception_to_unicode(error)
            msg = _("Invalid value for 'force': '%s'") % err_msg
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            volume = self.volume_api.get(context, id)
        except exception.VolumeNotFound as error:
            raise webob.exc.HTTPNotFound(explanation=error.msg)

        authorize(context, "upload_image")
        image_metadata = {"container_format": params.get("container_format",
                                                         "bare"),
                          "disk_format": params.get("disk_format", "raw"),
                          "name": params["image_name"]}

        if req_version >= api_version_request.APIVersionRequest('3.1'):

            image_metadata['visibility'] = params.get('visibility', 'private')
            image_metadata['protected'] = params.get('protected', 'False')

            if image_metadata['visibility'] == 'public':
                authorize(context, 'upload_public')

            if CONF.glance_api_version != 2:
                # Replace visibility with is_public for Glance V1
                image_metadata['is_public'] = (
                    image_metadata['visibility'] == 'public')
                image_metadata.pop('visibility', None)

            image_metadata['protected'] = (
                utils.get_bool_param('protected', image_metadata))

        try:
            response = self.volume_api.copy_volume_to_image(context,
                                                            volume,
                                                            image_metadata,
                                                            force)
        except exception.InvalidVolume as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except ValueError as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        except messaging.RemoteError as error:
            msg = "%(err_type)s: %(err_msg)s" % {'err_type': error.exc_type,
                                                 'err_msg': error.value}
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            raise webob.exc.HTTPBadRequest(explanation=six.text_type(error))
        return {'os-volume_upload_image': response}