Example #1
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                aggrs = self.aggregate_api.get_aggregates_by_host(context,
                                                                  service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(context,
                                                                  ag.id,
                                                                  service.host)
            self.host_api.service_delete(context, id)

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Example #2
0
    def _validate_id(req, hypervisor_id):
        """Validates that the id is a uuid for microversions that require it.

        :param req: The HTTP request object which contains the requested
            microversion information.
        :param hypervisor_id: The provided hypervisor id.
        :raises: webob.exc.HTTPBadRequest if the requested microversion is
            greater than or equal to 2.53 and the id is not a uuid.
        :raises: webob.exc.HTTPNotFound if the requested microversion is
            less than 2.53 and the id is not an integer.
        """
        expect_uuid = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)
        if expect_uuid:
            if not uuidutils.is_uuid_like(hypervisor_id):
                msg = _('Invalid uuid %s') % hypervisor_id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            # This API is supported for cells v1 and as such the id can be
            # a cell v1 delimited string, so we have to parse it first.
            if cells_utils.CELL_ITEM_SEP in str(hypervisor_id):
                hypervisor_id = cells_utils.split_cell_and_item(
                    hypervisor_id)[1]
            try:
                utils.validate_integer(hypervisor_id, 'id')
            except exception.InvalidInput:
                msg = (_("Hypervisor with ID '%s' could not be found.") %
                       hypervisor_id)
                raise webob.exc.HTTPNotFound(explanation=msg)
Example #3
0
    def server_create(self, server_dict, create_kwargs):
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = server_dict.get(MIN_ATTRIBUTE_NAME, 1)
        max_count = server_dict.get(MAX_ATTRIBUTE_NAME, min_count)
        return_id = server_dict.get(RRID_ATTRIBUTE_NAME, False)

        try:
            min_count = utils.validate_integer(min_count,
                                               "min_count", min_value=1)
            max_count = utils.validate_integer(max_count,
                                               "max_count", min_value=1)
            return_id = strutils.bool_from_string(return_id, strict=True)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        create_kwargs['min_count'] = min_count
        create_kwargs['max_count'] = max_count
        create_kwargs['return_reservation_id'] = return_id
Example #4
0
    def update(self, req, id, body):
        context = req.environ["nova.context"]
        authorize(context)
        quota_class = id
        bad_keys = []

        if not self.is_valid_body(body, "quota_class_set"):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body["quota_class_set"]
        for key in quota_class_set.keys():
            if key not in self.supported_quotas:
                bad_keys.append(key)
                continue
            try:
                value = utils.validate_integer(body["quota_class_set"][key], key)
            except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        for key in quota_class_set.keys():
            value = utils.validate_integer(body["quota_class_set"][key], key)
            try:
                db.quota_class_update(context, quota_class, key, value)
            except exception.QuotaClassNotFound:
                db.quota_class_create(context, quota_class, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

        values = QUOTAS.get_class_quotas(context, quota_class)
        return self._format_quota_set(None, values)
Example #5
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            if not uuidutils.is_uuid_like(id):
                msg = _('Invalid uuid %s') % id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(id, 'id')
            except exception.InvalidInput as exc:
                raise webob.exc.HTTPBadRequest(
                    explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                # Check to see if there are any instances on this compute host
                # because if there are, we need to block the service (and
                # related compute_nodes record) delete since it will impact
                # resource accounting in Placement and orphan the compute node
                # resource provider.
                # TODO(mriedem): Use a COUNT SQL query-based function instead
                # of InstanceList.get_uuids_by_host for performance.
                instance_uuids = objects.InstanceList.get_uuids_by_host(
                    context, service['host'])
                if instance_uuids:
                    raise webob.exc.HTTPConflict(
                        explanation=_('Unable to delete compute service that '
                                      'is hosting instances. Migrate or '
                                      'delete the instances first.'))

                aggrs = self.aggregate_api.get_aggregates_by_host(context,
                                                                  service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(context,
                                                                  ag.id,
                                                                  service.host)
                # remove the corresponding resource provider record from
                # placement for this compute node
                self.placementclient.delete_resource_provider(
                    context, service.compute_node, cascade=True)
                # remove the host_mapping of this host.
                hm = objects.HostMapping.get_by_host(context, service.host)
                hm.destroy()
            self.host_api.service_delete(context, id)

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
        except exception.ServiceNotUnique:
            explanation = _("Service id %s refers to multiple services.") % id
            raise webob.exc.HTTPBadRequest(explanation=explanation)
Example #6
0
    def delete(self, req, id):
        """Deletes an existing agent build."""
        context = req.environ['nova.context']
        authorize(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.destroy()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())
Example #7
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ["nova.context"]
        authorize(context)

        try:
            utils.validate_integer(id, "id")
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            self.host_api.service_delete(context, id)
        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Example #8
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            self.host_api.service_delete(context, id)
        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Example #9
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize(context)
        quota_class = id

        if not self.is_valid_body(body, 'quota_class_set'):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body['quota_class_set']
        for key in quota_class_set.keys():
            if key in QUOTAS:
                try:
                    value = utils.validate_integer(
                        body['quota_class_set'][key], key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())
                try:
                    db.quota_class_update(context, quota_class, key, value)
                except exception.QuotaClassNotFound:
                    db.quota_class_create(context, quota_class, key, value)
                except exception.AdminRequired:
                    raise webob.exc.HTTPForbidden()
        return {'quota_class_set': QUOTAS.get_class_quotas(context,
                                                           quota_class)}
Example #10
0
    def delete(self, req, id):
        """Deletes an existing agent build."""
        context = req.environ['nova.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks.
        nova_context.require_admin_context(context)
        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.destroy()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())
Example #11
0
def validate_and_default_volume_size(bdm):
    if bdm.get('volume_size'):
        try:
            bdm['volume_size'] = utils.validate_integer(
                bdm['volume_size'], 'volume_size', min_value=0)
        except exception.InvalidInput:
            raise exception.InvalidBDMFormat(
                details=_("Invalid volume_size."))
Example #12
0
def _get_int_param(request, param):
    """Extract integer param from request or fail."""
    try:
        int_param = utils.validate_integer(request.GET[param], param,
                                           min_value=0)
    except exception.InvalidInput as e:
        raise webob.exc.HTTPBadRequest(explanation=e.format_message())
    return int_param
def validate_and_default_volume_size(bdm):
    if bdm.get("volume_size"):
        try:
            bdm["volume_size"] = utils.validate_integer(bdm["volume_size"], "volume_size", min_value=0)
        except exception.InvalidInput:
            # NOTE: We can remove this validation code after removing
            # Nova v2.0 API code because v2.1 API validates this case
            # already at its REST API layer.
            raise exception.InvalidBDMFormat(details=_("Invalid volume_size."))
Example #14
0
def validate_and_default_volume_size(bdm):
    if bdm.get('volume_size'):
        try:
            bdm['volume_size'] = utils.validate_integer(bdm['volume_size'],
                                                        'volume_size',
                                                        min_value=0)
        except exception.InvalidInput:
            # NOTE: We can remove this validation code after removing
            # Nova v2.0 API code because v2.1 API validates this case
            # already at its REST API layer.
            raise exception.InvalidBDMFormat(details=_("Invalid volume_size."))
Example #15
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['nova.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks.
        nova_context.require_admin_context(context)
        try:
            para = body['para']
            url = para['url']
            md5hash = para['md5hash']
            version = para['version']
        except (TypeError, KeyError) as ex:
            msg = _("Invalid request body: %s") % ex
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            utils.validate_integer(id, 'id')
            utils.check_string_length(url, 'url', max_length=255)
            utils.check_string_length(md5hash, 'md5hash', max_length=255)
            utils.check_string_length(version, 'version', max_length=255)
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.obj_reset_changes()
            agent.version = version
            agent.url = url
            agent.md5hash = md5hash
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # NOTE(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {"agent": {'agent_id': id, 'version': version,
                'url': url, 'md5hash': md5hash}}
Example #16
0
    def _add_host(self, req, id, body):
        """Adds a host to the specified aggregate."""
        host = body['add_host']['host']

        context = _get_context(req)
        context.can(aggr_policies.POLICY_ROOT % 'add_host', target={})

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        try:
            aggregate = self.api.add_host_to_aggregate(context, id, host)
        except (exception.AggregateNotFound, exception.HostMappingNotFound,
                exception.ComputeHostNotFound) as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.AggregateHostExists,
                exception.InvalidAggregateAction) as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        return self._marshall_aggregate(req, aggregate)
Example #17
0
    def delete(self, req, id):
        """Deletes the specified service."""
        if not self.ext_mgr.is_loaded('os-extended-services-delete'):
            raise webob.exc.HTTPMethodNotAllowed()

        context = req.environ['nova.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks
        nova_context.require_admin_context(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            self.host_api.service_delete(context, id)
        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Example #18
0
 def test_valid_inputs(self):
     self.assertEqual(utils.validate_integer(42, "answer"), 42)
     self.assertEqual(utils.validate_integer("42", "answer"), 42)
     self.assertEqual(utils.validate_integer("7", "lucky", min_value=7, max_value=8), 7)
     self.assertEqual(utils.validate_integer(7, "lucky", min_value=6, max_value=7), 7)
     self.assertEqual(utils.validate_integer(300, "Spartaaa!!!", min_value=300), 300)
     self.assertEqual(utils.validate_integer("300", "Spartaaa!!!", max_value=300), 300)
Example #19
0
    def server_create(self, server_dict, create_kwargs):
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = server_dict.get(MIN_ATTRIBUTE_NAME, 1)
        max_count = server_dict.get(MAX_ATTRIBUTE_NAME, min_count)

        try:
            utils.validate_integer(min_count, "min_count", min_value=1)
            utils.validate_integer(max_count, "max_count", min_value=1)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        create_kwargs['min_count'] = min_count
        create_kwargs['max_count'] = max_count
        create_kwargs['return_reservation_id'] = server_dict.get(
            RRID_ATTRIBUTE_NAME, False)
Example #20
0
    def update(self, req, id, body):
        """Updates the name and/or availability_zone of given aggregate."""
        context = _get_context(req)
        context.can(aggr_policies.POLICY_ROOT % 'update', target={})
        updates = body["aggregate"]
        if 'name' in updates:
            updates['name'] = common.normalize_name(updates['name'])

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        try:
            aggregate = self.api.update_aggregate(context, id, updates)
        except exception.AggregateNameExists as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.AggregateNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.InvalidAggregateAction as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        return self._marshall_aggregate(req, aggregate)
Example #21
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['nova.context']
        authorize(context)

        # TODO(oomichi): This parameter name "para" is different from the ones
        # of the other APIs. Most other names are resource names like "server"
        # etc. This name should be changed to "agent" for consistent naming
        # with v2.1+microversions.
        para = body['para']

        url = para['url']
        md5hash = para['md5hash']
        version = para['version']

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        agent = objects.Agent(context=context, id=id)
        agent.obj_reset_changes()
        agent.version = version
        agent.url = url
        agent.md5hash = md5hash
        try:
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # TODO(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {"agent": {'agent_id': id, 'version': version,
                'url': url, 'md5hash': md5hash}}
Example #22
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize(context)
        quota_class = id
        bad_keys = []

        if not self.is_valid_body(body, 'quota_class_set'):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body['quota_class_set']
        for key in quota_class_set.keys():
            if key not in self.supported_quotas:
                bad_keys.append(key)
                continue
            try:
                value = utils.validate_integer(
                        body['quota_class_set'][key], key)
            except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(
                    explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        for key in quota_class_set.keys():
            value = utils.validate_integer(
                        body['quota_class_set'][key], key)
            try:
                db.quota_class_update(context, quota_class, key, value)
            except exception.QuotaClassNotFound:
                db.quota_class_create(context, quota_class, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

        values = QUOTAS.get_class_quotas(context, quota_class)
        return self._format_quota_set(None, values)
Example #23
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['nova.context']
        context.can(agents_policies.BASE_POLICY_NAME % 'update', target={})

        # TODO(oomichi): This parameter name "para" is different from the ones
        # of the other APIs. Most other names are resource names like "server"
        # etc. This name should be changed to "agent" for consistent naming
        # with v2.1+microversions.
        para = body['para']

        url = para['url']
        md5hash = para['md5hash']
        version = para['version']

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        agent = objects.Agent(context=context, id=id)
        agent.obj_reset_changes()
        agent.version = version
        agent.url = url
        agent.md5hash = md5hash
        try:
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # TODO(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {"agent": {'agent_id': id, 'version': version,
                'url': url, 'md5hash': md5hash}}
Example #24
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize(context)
        try:
            utils.check_string_length(id,
                                      'quota_class_name',
                                      min_length=1,
                                      max_length=255)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        quota_class = id
        bad_keys = []

        if not self.is_valid_body(body, 'quota_class_set'):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body['quota_class_set']
        for key in quota_class_set.keys():
            if key not in self.supported_quotas:
                bad_keys.append(key)
                continue
            try:
                body['quota_class_set'][key] = utils.validate_integer(
                    body['quota_class_set'][key], key, max_value=db.MAX_INT)
            except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            # NOTE(alex_xu): back-compatible with db layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            nova.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        for key, value in quota_class_set.items():
            try:
                db.quota_class_update(context, quota_class, key, value)
            except exception.QuotaClassNotFound:
                db.quota_class_create(context, quota_class, key, value)

        values = QUOTAS.get_class_quotas(context, quota_class)
        return self._format_quota_set(None, values)
    def _validate_id(req, hypervisor_id):
        """Validates that the id is a uuid for microversions that require it.

        :param req: The HTTP request object which contains the requested
            microversion information.
        :param hypervisor_id: The provided hypervisor id.
        :raises: webob.exc.HTTPBadRequest if the requested microversion is
            greater than or equal to 2.53 and the id is not a uuid.
        :raises: webob.exc.HTTPNotFound if the requested microversion is
            less than 2.53 and the id is not an integer.
        """
        expect_uuid = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)
        if expect_uuid:
            if not uuidutils.is_uuid_like(hypervisor_id):
                msg = _('Invalid uuid %s') % hypervisor_id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(hypervisor_id, 'id')
            except exception.InvalidInput:
                msg = (_("Hypervisor with ID '%s' could not be found.") %
                       hypervisor_id)
                raise webob.exc.HTTPNotFound(explanation=msg)
Example #26
0
def limited(items, request, max_limit=CONF.osapi_max_limit):
    """Return a slice of items according to requested offset and limit.

    :param items: A sliceable entity
    :param request: ``wsgi.Request`` possibly containing 'offset' and 'limit'
                    GET variables. 'offset' is where to start in the list,
                    and 'limit' is the maximum number of items to return. If
                    'limit' is not specified, 0, or > max_limit, we default
                    to max_limit. Negative values for either offset or limit
                    will cause exc.HTTPBadRequest() exceptions to be raised.
    :kwarg max_limit: The maximum number of items to return from 'items'
    """
    offset = request.GET.get("offset", 0)
    limit = request.GET.get('limit', max_limit)

    try:
        offset = utils.validate_integer(offset, "offset", min_value=0)
        limit = utils.validate_integer(limit, "limit", min_value=0)
    except exception.InvalidInput as e:
        raise webob.exc.HTTPBadRequest(explanation=e.format_message())

    limit = min(max_limit, limit or max_limit)
    range_end = offset + limit
    return items[offset:range_end]
Example #27
0
    def _validate_id(req, hypervisor_id):
        """Validates that the id is a uuid for microversions that require it.

        :param req: The HTTP request object which contains the requested
            microversion information.
        :param hypervisor_id: The provided hypervisor id.
        :raises: webob.exc.HTTPBadRequest if the requested microversion is
            greater than or equal to 2.53 and the id is not a uuid.
        :raises: webob.exc.HTTPNotFound if the requested microversion is
            less than 2.53 and the id is not an integer.
        """
        expect_uuid = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)
        if expect_uuid:
            if not uuidutils.is_uuid_like(hypervisor_id):
                msg = _('Invalid uuid %s') % hypervisor_id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(hypervisor_id, 'id')
            except exception.InvalidInput:
                msg = (_("Hypervisor with ID '%s' could not be found.") %
                       hypervisor_id)
                raise webob.exc.HTTPNotFound(explanation=msg)
Example #28
0
def limited(items, request, max_limit=CONF.osapi_max_limit):
    """Return a slice of items according to requested offset and limit.

    :param items: A sliceable entity
    :param request: ``wsgi.Request`` possibly containing 'offset' and 'limit'
                    GET variables. 'offset' is where to start in the list,
                    and 'limit' is the maximum number of items to return. If
                    'limit' is not specified, 0, or > max_limit, we default
                    to max_limit. Negative values for either offset or limit
                    will cause exc.HTTPBadRequest() exceptions to be raised.
    :kwarg max_limit: The maximum number of items to return from 'items'
    """
    offset = request.GET.get("offset", 0)
    limit = request.GET.get('limit', max_limit)

    try:
        offset = utils.validate_integer(offset, "offset", min_value=0)
        limit = utils.validate_integer(limit, "limit", min_value=0)
    except exception.InvalidInput as e:
        raise webob.exc.HTTPBadRequest(explanation=e.format_message())

    limit = min(max_limit, limit or max_limit)
    range_end = offset + limit
    return items[offset:range_end]
Example #29
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                aggrs = self.aggregate_api.get_aggregates_by_host(
                    context, service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(
                        context, ag.id, service.host)
            self.host_api.service_delete(context, id)

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Example #30
0
    def update(self, req, id, body):
        context = req.environ["nova.context"]
        authorize(context)
        try:
            utils.check_string_length(id, "quota_class_name", min_length=1, max_length=255)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        quota_class = id
        bad_keys = []

        if not self.is_valid_body(body, "quota_class_set"):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body["quota_class_set"]
        for key in quota_class_set.keys():
            if key not in self.supported_quotas:
                bad_keys.append(key)
                continue
            try:
                body["quota_class_set"][key] = utils.validate_integer(
                    body["quota_class_set"][key], key, max_value=db.MAX_INT
                )
            except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            # NOTE(alex_xu): back-compatible with db layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            nova.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        for key, value in quota_class_set.items():
            try:
                db.quota_class_update(context, quota_class, key, value)
            except exception.QuotaClassNotFound:
                db.quota_class_create(context, quota_class, key, value)

        values = QUOTAS.get_class_quotas(context, quota_class)
        return self._format_quota_set(None, values)
Example #31
0
 def test_valid_inputs(self):
     self.assertEqual(utils.validate_integer(42, "answer"), 42)
     self.assertEqual(utils.validate_integer("42", "answer"), 42)
     self.assertEqual(
         utils.validate_integer("7", "lucky", min_value=7, max_value=8), 7)
     self.assertEqual(
         utils.validate_integer(7, "lucky", min_value=6, max_value=7), 7)
     self.assertEqual(
         utils.validate_integer(300, "Spartaaa!!!", min_value=300), 300)
     self.assertEqual(
         utils.validate_integer("300", "Spartaaa!!!", max_value=300), 300)
 def update(self, req, id, body):
     context = req.environ['nova.context']
     authorize(context)
     quota_class = id
     if not self.is_valid_body(body, 'quota_class_set'):
         raise webob.exc.HTTPBadRequest("The request body invalid")
     quota_class_set = body['quota_class_set']
     for key in quota_class_set.keys():
         if key in QUOTAS and key not in FILTERED_QUOTAS:
             try:
                 value = utils.validate_integer(
                     body['quota_class_set'][key], key)
             except exception.InvalidInput as e:
                 raise webob.exc.HTTPBadRequest(
                     explanation=e.format_message())
             try:
                 db.quota_class_update(context, quota_class, key, value)
             except exception.QuotaClassNotFound:
                 db.quota_class_create(context, quota_class, key, value)
             except exception.AdminRequired:
                 raise webob.exc.HTTPForbidden()
     return self._format_quota_set(
         quota_class, QUOTAS.get_class_quotas(context, quota_class))
Example #33
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        project_id = id
        params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
        user_id = params.get('user_id', [None])[0]

        bad_keys = []
        force_update = False

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        for key, value in quota_set.items():
            if ((key not in QUOTAS or key in FILTERED_QUOTAS)
                 and key != 'force'):
                bad_keys.append(key)
                continue
            if key == 'force':
                force_update = strutils.bool_from_string(value)
            elif key != 'force' and value:
                try:
                    value = utils.validate_integer(
                        value, key)
                except exception.InvalidInput as e:
                    LOG.warn(e.format_message())
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if len(bad_keys) > 0:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context, project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        try:
            quotas = self._get_quotas(context, id, user_id=user_id,
                                      usages=True)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        LOG.debug("Force update quotas: %s", force_update)

        for key, value in body['quota_set'].iteritems():
            if key == 'force' or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if force_update is not True and value >= 0:
                quota_value = quotas.get(key)
                if quota_value and quota_value['limit'] >= 0:
                    quota_used = (quota_value['in_use'] +
                                  quota_value['reserved'])
                    LOG.debug("Quota %(key)s used: %(quota_used)s, "
                              "value: %(value)s.",
                              {'key': key, 'quota_used': quota_used,
                               'value': value})
                    if quota_used > value:
                        msg = (_("Quota value %(value)s for %(key)s are "
                                "less than already used and reserved "
                                "%(quota_used)s") %
                                {'value': value, 'key': key,
                                 'quota_used': quota_used})
                        raise webob.exc.HTTPBadRequest(explanation=msg)

            minimum = settable_quotas[key]['minimum']
            maximum = settable_quotas[key]['maximum']
            self._validate_quota_limit(value, minimum, maximum)
            try:
                db.quota_create(context, project_id, key, value,
                                user_id=user_id)
            except exception.QuotaExists:
                db.quota_update(context, project_id, key, value,
                                user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        return self._format_quota_set(id, self._get_quotas(context, id,
                                                           user_id=user_id))
Example #34
0
def create(name,
           memory,
           vcpus,
           root_gb,
           ephemeral_gb=0,
           flavorid=None,
           swap=0,
           rxtx_factor=1.0,
           is_public=True):
    """Creates flavors."""
    if not flavorid:
        flavorid = uuid.uuid4()

    kwargs = {
        'memory_mb': memory,
        'vcpus': vcpus,
        'root_gb': root_gb,
        'ephemeral_gb': ephemeral_gb,
        'swap': swap,
        'rxtx_factor': rxtx_factor,
    }

    if isinstance(name, six.string_types):
        name = name.strip()
    # ensure name do not exceed 255 characters
    utils.check_string_length(name, 'name', min_length=1, max_length=255)

    # ensure name does not contain any special characters
    valid_name = VALID_NAME_REGEX.search(name)
    if not valid_name:
        msg = _("Flavor names can only contain alphanumeric characters, "
                "periods, dashes, underscores and spaces.")
        raise exception.InvalidInput(reason=msg)

    # NOTE(vish): Internally, flavorid is stored as a string but it comes
    #             in through json as an integer, so we convert it here.
    flavorid = unicode(flavorid)

    # ensure leading/trailing whitespaces not present.
    if flavorid.strip() != flavorid:
        msg = _("id cannot contain leading and/or trailing whitespace(s)")
        raise exception.InvalidInput(reason=msg)

    # ensure flavor id does not exceed 255 characters
    utils.check_string_length(flavorid, 'id', min_length=1, max_length=255)

    # ensure flavor id does not contain any special characters
    valid_flavor_id = VALID_ID_REGEX.search(flavorid)
    if not valid_flavor_id:
        msg = _("Flavor id can only contain letters from A-Z (both cases), "
                "periods, dashes, underscores and spaces.")
        raise exception.InvalidInput(reason=msg)

    # NOTE(wangbo): validate attributes of the creating flavor.
    # ram and vcpus should be positive ( > 0) integers.
    # disk, ephemeral and swap should be non-negative ( >= 0) integers.
    flavor_attributes = {
        'memory_mb': ('ram', 1),
        'vcpus': ('vcpus', 1),
        'root_gb': ('disk', 0),
        'ephemeral_gb': ('ephemeral', 0),
        'swap': ('swap', 0)
    }

    for key, value in flavor_attributes.items():
        kwargs[key] = utils.validate_integer(kwargs[key], value[0], value[1],
                                             db.MAX_INT)

    # rxtx_factor should be a positive float
    try:
        kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
        if (kwargs['rxtx_factor'] <= 0
                or kwargs['rxtx_factor'] > SQL_SP_FLOAT_MAX):
            raise ValueError()
    except ValueError:
        msg = (_("'rxtx_factor' argument must be a float between 0 and %g") %
               SQL_SP_FLOAT_MAX)
        raise exception.InvalidInput(reason=msg)

    kwargs['name'] = name
    kwargs['flavorid'] = flavorid
    # ensure is_public attribute is boolean
    try:
        kwargs['is_public'] = strutils.bool_from_string(is_public, strict=True)
    except ValueError:
        raise exception.InvalidInput(reason=_("is_public must be a boolean"))

    try:
        return db.flavor_create(context.get_admin_context(), kwargs)
    except db_exc.DBError as e:
        LOG.exception(_LE('DB error: %s'), e)
        raise exception.FlavorCreateFailed()
Example #35
0
    def create(self, req, body):
        """Creates a new server for a given user."""
        if not self.is_valid_body(body, 'server'):
            raise exc.HTTPUnprocessableEntity()

        context = req.environ['nova.context']
        server_dict = body['server']
        password = self._get_server_admin_password(server_dict)

        if 'name' not in server_dict:
            msg = _("Server name is not defined")
            raise exc.HTTPBadRequest(explanation=msg)

        name = server_dict['name']
        self._validate_server_name(name)
        name = name.strip()

        image_uuid = self._image_from_req_data(body)

        personality = server_dict.get('personality')
        config_drive = None
        if self.ext_mgr.is_loaded('os-config-drive'):
            config_drive = server_dict.get('config_drive')

        injected_files = []
        if personality:
            injected_files = self._get_injected_files(personality)

        sg_names = []
        if self.ext_mgr.is_loaded('os-security-groups'):
            security_groups = server_dict.get('security_groups')
            if security_groups is not None:
                sg_names = [
                    sg['name'] for sg in security_groups if sg.get('name')
                ]
        if not sg_names:
            sg_names.append('default')

        sg_names = list(set(sg_names))

        requested_networks = None
        if (self.ext_mgr.is_loaded('os-networks') or utils.is_neutron()):
            requested_networks = server_dict.get('networks')

        if requested_networks is not None:
            if not isinstance(requested_networks, list):
                expl = _('Bad networks format')
                raise exc.HTTPBadRequest(explanation=expl)
            requested_networks = self._get_requested_networks(
                requested_networks)

        (access_ip_v4, ) = server_dict.get('accessIPv4'),
        if access_ip_v4 is not None:
            self._validate_access_ipv4(access_ip_v4)

        (access_ip_v6, ) = server_dict.get('accessIPv6'),
        if access_ip_v6 is not None:
            self._validate_access_ipv6(access_ip_v6)

        try:
            flavor_id = self._flavor_id_from_req_data(body)
        except ValueError as error:
            msg = _("Invalid flavorRef provided.")
            raise exc.HTTPBadRequest(explanation=msg)

        # optional openstack extensions:
        key_name = None
        if self.ext_mgr.is_loaded('os-keypairs'):
            key_name = server_dict.get('key_name')

        user_data = None
        if self.ext_mgr.is_loaded('os-user-data'):
            user_data = server_dict.get('user_data')
        self._validate_user_data(user_data)

        availability_zone = None
        if self.ext_mgr.is_loaded('os-availability-zone'):
            availability_zone = server_dict.get('availability_zone')

        block_device_mapping = None
        block_device_mapping_v2 = None
        legacy_bdm = True
        if self.ext_mgr.is_loaded('os-volumes'):
            block_device_mapping = server_dict.get('block_device_mapping', [])
            if not isinstance(block_device_mapping, list):
                msg = _('block_device_mapping must be a list')
                raise exc.HTTPBadRequest(explanation=msg)
            for bdm in block_device_mapping:
                try:
                    block_device.validate_device_name(bdm.get("device_name"))
                    block_device.validate_and_default_volume_size(bdm)
                except exception.InvalidBDMFormat as e:
                    raise exc.HTTPBadRequest(explanation=e.format_message())

                if 'delete_on_termination' in bdm:
                    bdm['delete_on_termination'] = strutils.bool_from_string(
                        bdm['delete_on_termination'])

            if self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot'):
                # Consider the new data format for block device mapping
                block_device_mapping_v2 = server_dict.get(
                    'block_device_mapping_v2', [])
                # NOTE (ndipanov):  Disable usage of both legacy and new
                #                   block device format in the same request
                if block_device_mapping and block_device_mapping_v2:
                    expl = _('Using different block_device_mapping syntaxes '
                             'is not allowed in the same request.')
                    raise exc.HTTPBadRequest(explanation=expl)

                if not isinstance(block_device_mapping_v2, list):
                    msg = _('block_device_mapping_v2 must be a list')
                    raise exc.HTTPBadRequest(explanation=msg)

                # Assume legacy format
                legacy_bdm = not bool(block_device_mapping_v2)

                try:
                    block_device_mapping_v2 = [
                        block_device.BlockDeviceDict.from_api(bdm_dict)
                        for bdm_dict in block_device_mapping_v2
                    ]
                except exception.InvalidBDMFormat as e:
                    raise exc.HTTPBadRequest(explanation=e.format_message())

        block_device_mapping = (block_device_mapping
                                or block_device_mapping_v2)

        ret_resv_id = False
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = 1
        max_count = 1
        if self.ext_mgr.is_loaded('os-multiple-create'):
            ret_resv_id = server_dict.get('return_reservation_id', False)
            min_count = server_dict.get('min_count', 1)
            max_count = server_dict.get('max_count', min_count)

        try:
            min_count = utils.validate_integer(min_count,
                                               "min_count",
                                               min_value=1)
            max_count = utils.validate_integer(max_count,
                                               "max_count",
                                               min_value=1)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        auto_disk_config = False
        if self.ext_mgr.is_loaded('OS-DCF'):
            auto_disk_config = server_dict.get('auto_disk_config')

        scheduler_hints = {}
        if self.ext_mgr.is_loaded('OS-SCH-HNT'):
            scheduler_hints = server_dict.get('scheduler_hints', {})

        check_server_group_quota = \
            self.ext_mgr.is_loaded('os-server-group-quotas')

        try:
            _get_inst_type = flavors.get_flavor_by_flavor_id
            inst_type = _get_inst_type(flavor_id,
                                       ctxt=context,
                                       read_deleted="no")

            (instances, resv_id) = self.compute_api.create(
                context,
                inst_type,
                image_uuid,
                display_name=name,
                display_description=name,
                key_name=key_name,
                metadata=server_dict.get('metadata', {}),
                access_ip_v4=access_ip_v4,
                access_ip_v6=access_ip_v6,
                injected_files=injected_files,
                admin_password=password,
                min_count=min_count,
                max_count=max_count,
                requested_networks=requested_networks,
                security_group=sg_names,
                user_data=user_data,
                availability_zone=availability_zone,
                config_drive=config_drive,
                block_device_mapping=block_device_mapping,
                auto_disk_config=auto_disk_config,
                scheduler_hints=scheduler_hints,
                legacy_bdm=legacy_bdm,
                check_server_group_quota=check_server_group_quota)
        except (exception.QuotaError, exception.PortLimitExceeded) as error:
            raise exc.HTTPForbidden(explanation=error.format_message(),
                                    headers={'Retry-After': 0})
        except exception.InvalidMetadataSize as error:
            raise exc.HTTPRequestEntityTooLarge(
                explanation=error.format_message())
        except exception.ImageNotFound as error:
            msg = _("Can not find requested image")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.FlavorNotFound as error:
            msg = _("Invalid flavorRef provided.")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.KeypairNotFound as error:
            msg = _("Invalid key_name provided.")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.ConfigDriveInvalidValue:
            msg = _("Invalid config_drive provided.")
            raise exc.HTTPBadRequest(explanation=msg)
        except messaging.RemoteError as err:
            msg = "%(err_type)s: %(err_msg)s" % {
                'err_type': err.exc_type,
                'err_msg': err.value
            }
            raise exc.HTTPBadRequest(explanation=msg)
        except UnicodeDecodeError as error:
            msg = "UnicodeError: %s" % error
            raise exc.HTTPBadRequest(explanation=msg)
        except (exception.ImageNotActive, exception.FlavorDiskTooSmall,
                exception.FlavorMemoryTooSmall, exception.NetworkNotFound,
                exception.PortNotFound, exception.FixedIpAlreadyInUse,
                exception.SecurityGroupNotFound,
                exception.InstanceUserDataTooLarge,
                exception.InstanceUserDataMalformed) as error:
            raise exc.HTTPBadRequest(explanation=error.format_message())
        except (exception.ImageNUMATopologyIncomplete,
                exception.ImageNUMATopologyForbidden,
                exception.ImageNUMATopologyAsymmetric,
                exception.ImageNUMATopologyCPUOutOfRange,
                exception.ImageNUMATopologyCPUDuplicates,
                exception.ImageNUMATopologyCPUsUnassigned,
                exception.ImageNUMATopologyMemoryOutOfRange) as error:
            raise exc.HTTPBadRequest(explanation=error.format_message())
        except (exception.PortInUse, exception.InstanceExists,
                exception.NoUniqueMatch) as error:
            raise exc.HTTPConflict(explanation=error.format_message())
        except exception.Invalid as error:
            raise exc.HTTPBadRequest(explanation=error.format_message())

        # If the caller wanted a reservation_id, return it
        if ret_resv_id:
            return wsgi.ResponseObject({'reservation_id': resv_id})

        req.cache_db_instances(instances)
        server = self._view_builder.create(req, instances[0])

        if CONF.enable_instance_password:
            server['server']['adminPass'] = password

        robj = wsgi.ResponseObject(server)

        return self._add_location(robj)
Example #36
0
    def _create_backup(self, req, id, body):
        """Backup a server instance.

        Images now have an `image_type` associated with them, which can be
        'snapshot' or the backup type, like 'daily' or 'weekly'.

        If the image_type is backup-like, then the rotation factor can be
        included and that will cause the oldest backups that exceed the
        rotation factor to be deleted.

        """
        context = req.environ["nova.context"]
        authorize(context, 'createBackup')
        entity = body["createBackup"]

        try:
            image_name = entity["name"]
            backup_type = entity["backup_type"]
            rotation = entity["rotation"]

        except KeyError as missing_key:
            msg = _("createBackup entity requires %s attribute") % missing_key
            raise exc.HTTPBadRequest(explanation=msg)

        except TypeError:
            msg = _("Malformed createBackup entity")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            rotation = utils.validate_integer(rotation,
                                              "rotation",
                                              min_value=0)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        props = {}
        metadata = entity.get('metadata', {})
        common.check_img_metadata_properties_quota(context, metadata)
        try:
            props.update(metadata)
        except ValueError:
            msg = _("Invalid metadata")
            raise exc.HTTPBadRequest(explanation=msg)

        instance = common.get_instance(self.compute_api, context, id)
        try:
            image = self.compute_api.backup(context,
                                            instance,
                                            image_name,
                                            backup_type,
                                            rotation,
                                            extra_properties=props)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(
                state_error, 'createBackup', id)
        except exception.InvalidRequest as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        resp = webob.Response(status_int=202)

        # build location of newly-created image entity if rotation is not zero
        if rotation > 0:
            image_id = str(image['id'])
            image_ref = os.path.join(req.application_url, 'images', image_id)
            resp.headers['Location'] = image_ref

        return resp
Example #37
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context, project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we figure
        # out if we have bad keys or if we need to forcibly set quotas or
        # if some of the values for the quotas can be converted to integers.
        for key, value in quota_set.items():
            if (key not in self.supported_quotas
                and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # NOTE(dims): Pass #2 - In this loop for quota_set.items(), based on
        # force_update flag we validate the quota limit. A loop just for
        # the validation of min/max values ensure that we can bail out if
        # any of the items in the set is bad.
        valid_quotas = {}
        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if not force_update:
                minimum = settable_quotas[key]['minimum']
                maximum = settable_quotas[key]['maximum']
                self._validate_quota_limit(key, value, minimum, maximum)
            valid_quotas[key] = value

        # NOTE(dims): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one
        # shot without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                objects.Quotas.create_limit(context, project_id,
                                            key, value, user_id=user_id)
            except exception.QuotaExists:
                objects.Quotas.update_limit(context, project_id,
                                            key, value, user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        values = self._get_quotas(context, id, user_id=user_id)
        return self._format_quota_set(None, values)
    def _create_backup(self, req, id, body):
        """Backup a server instance.

        Images now have an `image_type` associated with them, which can be
        'snapshot' or the backup type, like 'daily' or 'weekly'.

        If the image_type is backup-like, then the rotation factor can be
        included and that will cause the oldest backups that exceed the
        rotation factor to be deleted.

        """
        context = req.environ["nova.context"]
        authorize(context, 'createBackup')
        entity = body["createBackup"]

        try:
            image_name = entity["name"]
            backup_type = entity["backup_type"]
            rotation = entity["rotation"]

        except KeyError as missing_key:
            msg = _("createBackup entity requires %s attribute") % missing_key
            raise exc.HTTPBadRequest(explanation=msg)

        except TypeError:
            msg = _("Malformed createBackup entity")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            rotation = utils.validate_integer(rotation, "rotation",
                                              min_value=0)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        props = {}
        metadata = entity.get('metadata', {})
        common.check_img_metadata_properties_quota(context, metadata)
        try:
            props.update(metadata)
        except ValueError:
            msg = _("Invalid metadata")
            raise exc.HTTPBadRequest(explanation=msg)

        instance = common.get_instance(self.compute_api, context, id)
        try:
            image = self.compute_api.backup(context, instance, image_name,
                    backup_type, rotation, extra_properties=props)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'createBackup', id)
        except exception.InvalidRequest as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        resp = webob.Response(status_int=202)

        # build location of newly-created image entity if rotation is not zero
        if rotation > 0:
            image_id = str(image['id'])
            image_ref = common.url_join(req.application_url, 'images',
                                        image_id)
            resp.headers['Location'] = image_ref

        return resp
Example #39
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        project_id = id
        params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
        user_id = params.get('user_id', [None])[0]

        bad_keys = []
        force_update = False

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        for key, value in quota_set.items():
            if ((key not in QUOTAS or key in FILTERED_QUOTAS)
                    and key != 'force'):
                bad_keys.append(key)
                continue
            if key == 'force':
                force_update = strutils.bool_from_string(value)
            elif key != 'force' and value:
                try:
                    value = utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    LOG.warn(e.format_message())
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if len(bad_keys) > 0:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context,
                                                         project_id,
                                                         user_id=user_id)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        try:
            quotas = self._get_quotas(context,
                                      id,
                                      user_id=user_id,
                                      usages=True)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        LOG.debug(_("Force update quotas: %s"), force_update)

        for key, value in body['quota_set'].iteritems():
            if key == 'force' or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if force_update is not True and value >= 0:
                quota_value = quotas.get(key)
                if quota_value and quota_value['limit'] >= 0:
                    quota_used = (quota_value['in_use'] +
                                  quota_value['reserved'])
                    LOG.debug(
                        _("Quota %(key)s used: %(quota_used)s, "
                          "value: %(value)s."), {
                              'key': key,
                              'quota_used': quota_used,
                              'value': value
                          })
                    if quota_used > value:
                        msg = (_("Quota value %(value)s for %(key)s are "
                                 "less than already used and reserved "
                                 "%(quota_used)s") % {
                                     'value': value,
                                     'key': key,
                                     'quota_used': quota_used
                                 })
                        raise webob.exc.HTTPBadRequest(explanation=msg)

            minimum = settable_quotas[key]['minimum']
            maximum = settable_quotas[key]['maximum']
            self._validate_quota_limit(value, minimum, maximum)
            try:
                db.quota_create(context,
                                project_id,
                                key,
                                value,
                                user_id=user_id)
            except exception.QuotaExists:
                db.quota_update(context,
                                project_id,
                                key,
                                value,
                                user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        return self._format_quota_set(
            id, self._get_quotas(context, id, user_id=user_id))
Example #40
0
    def create(self, req, body):
        """Creates a new server for a given user."""
        if not self.is_valid_body(body, 'server'):
            raise exc.HTTPUnprocessableEntity()

        context = req.environ['nova.context']
        server_dict = body['server']
        password = self._get_server_admin_password(server_dict)

        if 'name' not in server_dict:
            msg = _("Server name is not defined")
            raise exc.HTTPBadRequest(explanation=msg)

        name = server_dict['name']
        self._validate_server_name(name)
        name = name.strip()

        image_uuid = self._image_from_req_data(body)

        personality = server_dict.get('personality')
        config_drive = None
        if self.ext_mgr.is_loaded('os-config-drive'):
            config_drive = server_dict.get('config_drive')

        injected_files = []
        if personality:
            injected_files = self._get_injected_files(personality)

        sg_names = []
        if self.ext_mgr.is_loaded('os-security-groups'):
            security_groups = server_dict.get('security_groups')
            if security_groups is not None:
                sg_names = [
                    sg['name'] for sg in security_groups if sg.get('name')
                ]
        if not sg_names:
            sg_names.append('default')

        sg_names = list(set(sg_names))

        requested_networks = self._determine_requested_networks(server_dict)

        (access_ip_v4, ) = server_dict.get('accessIPv4'),
        if access_ip_v4 is not None:
            self._validate_access_ipv4(access_ip_v4)

        (access_ip_v6, ) = server_dict.get('accessIPv6'),
        if access_ip_v6 is not None:
            self._validate_access_ipv6(access_ip_v6)

        flavor_id = self._flavor_id_from_req_data(body)

        # optional openstack extensions:
        key_name = self._extract(server_dict, 'os-keypairs', 'key_name')
        availability_zone = self._extract(server_dict, 'os-availability-zone',
                                          'availability_zone')
        user_data = self._extract(server_dict, 'os-user-data', 'user_data')
        self._validate_user_data(user_data)

        legacy_bdm, block_device_mapping = self._extract_bdm(server_dict)

        ret_resv_id = False
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = 1
        max_count = 1
        if self.ext_mgr.is_loaded('os-multiple-create'):
            ret_resv_id = server_dict.get('return_reservation_id', False)
            min_count = server_dict.get('min_count', 1)
            max_count = server_dict.get('max_count', min_count)

        try:
            min_count = utils.validate_integer(min_count,
                                               "min_count",
                                               min_value=1)
            max_count = utils.validate_integer(max_count,
                                               "max_count",
                                               min_value=1)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        auto_disk_config = False
        if self.ext_mgr.is_loaded('OS-DCF'):
            auto_disk_config = server_dict.get('auto_disk_config')

        scheduler_hints = {}
        if self.ext_mgr.is_loaded('OS-SCH-HNT'):
            scheduler_hints = server_dict.get('scheduler_hints', {})

        check_server_group_quota = self.ext_mgr.is_loaded(
            'os-server-group-quotas')
        try:
            _get_inst_type = flavors.get_flavor_by_flavor_id
            inst_type = _get_inst_type(flavor_id,
                                       ctxt=context,
                                       read_deleted="no")

            (instances, resv_id) = self.compute_api.create(
                context,
                inst_type,
                image_uuid,
                display_name=name,
                display_description=name,
                key_name=key_name,
                metadata=server_dict.get('metadata', {}),
                access_ip_v4=access_ip_v4,
                access_ip_v6=access_ip_v6,
                injected_files=injected_files,
                admin_password=password,
                min_count=min_count,
                max_count=max_count,
                requested_networks=requested_networks,
                security_group=sg_names,
                user_data=user_data,
                availability_zone=availability_zone,
                config_drive=config_drive,
                block_device_mapping=block_device_mapping,
                auto_disk_config=auto_disk_config,
                scheduler_hints=scheduler_hints,
                legacy_bdm=legacy_bdm,
                check_server_group_quota=check_server_group_quota)
        except (exception.QuotaError, exception.PortLimitExceeded) as error:
            raise exc.HTTPForbidden(explanation=error.format_message(),
                                    headers={'Retry-After': 0})
        except messaging.RemoteError as err:
            msg = "%(err_type)s: %(err_msg)s" % {
                'err_type': err.exc_type,
                'err_msg': err.value
            }
            raise exc.HTTPBadRequest(explanation=msg)
        except UnicodeDecodeError as error:
            msg = "UnicodeError: %s" % error
            raise exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            # The remaining cases can be handled in a standard fashion.
            self._handle_create_exception(*sys.exc_info())

        # If the caller wanted a reservation_id, return it
        if ret_resv_id:
            return wsgi.ResponseObject({'reservation_id': resv_id})

        req.cache_db_instances(instances)
        server = self._view_builder.create(req, instances[0])

        if CONF.enable_instance_password:
            server['server']['adminPass'] = password

        robj = wsgi.ResponseObject(server)

        return self._add_location(robj)
Example #41
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME % 'delete', target={})

        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            if not uuidutils.is_uuid_like(id):
                msg = _('Invalid uuid %s') % id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(id, 'id')
            except exception.InvalidInput as exc:
                raise webob.exc.HTTPBadRequest(
                    explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                # Check to see if there are any instances on this compute host
                # because if there are, we need to block the service (and
                # related compute_nodes record) delete since it will impact
                # resource accounting in Placement and orphan the compute node
                # resource provider.
                num_instances = objects.InstanceList.get_count_by_hosts(
                    context, [service['host']])
                if num_instances:
                    raise webob.exc.HTTPConflict(
                        explanation=_('Unable to delete compute service that '
                                      'is hosting instances. Migrate or '
                                      'delete the instances first.'))

                # Similarly, check to see if the are any in-progress migrations
                # involving this host because if there are we need to block the
                # service delete since we could orphan resource providers and
                # break the ability to do things like confirm/revert instances
                # in VERIFY_RESIZE status.
                compute_nodes = objects.ComputeNodeList.get_all_by_host(
                    context, service.host)
                self._assert_no_in_progress_migrations(context, id,
                                                       compute_nodes)

                aggrs = self.aggregate_api.get_aggregates_by_host(
                    context, service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(
                        context, ag.id, service.host)
                # remove the corresponding resource provider record from
                # placement for the compute nodes managed by this service;
                # remember that an ironic compute service can manage multiple
                # nodes
                for compute_node in compute_nodes:
                    try:
                        self.placementclient.delete_resource_provider(
                            context, compute_node, cascade=True)
                    except ks_exc.ClientException as e:
                        LOG.error(
                            "Failed to delete compute node resource provider "
                            "for compute node %s: %s", compute_node.uuid,
                            six.text_type(e))
                # remove the host_mapping of this host.
                try:
                    hm = objects.HostMapping.get_by_host(context, service.host)
                    hm.destroy()
                except exception.HostMappingNotFound:
                    # It's possible to startup a nova-compute service and then
                    # delete it (maybe it was accidental?) before mapping it to
                    # a cell using discover_hosts, so we just ignore this.
                    pass
            service.destroy()

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
        except exception.ServiceNotUnique:
            explanation = _("Service id %s refers to multiple services.") % id
            raise webob.exc.HTTPBadRequest(explanation=explanation)
Example #42
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context,
                                                         project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']
        for key, value in quota_set.items():
            if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    value = utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        LOG.debug("force update quotas: %s", force_update)

        if len(bad_keys) > 0:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            quotas = self._get_quotas(context,
                                      id,
                                      user_id=user_id,
                                      usages=True)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if force_update is not True and value >= 0:
                quota_value = quotas.get(key)
                if quota_value and quota_value['limit'] >= 0:
                    quota_used = (quota_value['in_use'] +
                                  quota_value['reserved'])
                    LOG.debug(
                        "Quota %(key)s used: %(quota_used)s, "
                        "value: %(value)s.", {
                            'key': key,
                            'quota_used': quota_used,
                            'value': value
                        })
                    if quota_used > value:
                        msg = (_("Quota value %(value)s for %(key)s are "
                                 "less than already used and reserved "
                                 "%(quota_used)s") % {
                                     'value': value,
                                     'key': key,
                                     'quota_used': quota_used
                                 })
                        raise webob.exc.HTTPBadRequest(explanation=msg)

            minimum = settable_quotas[key]['minimum']
            maximum = settable_quotas[key]['maximum']
            self._validate_quota_limit(value, minimum, maximum)
            try:
                db.quota_create(context,
                                project_id,
                                key,
                                value,
                                user_id=user_id)
            except exception.QuotaExists:
                db.quota_update(context,
                                project_id,
                                key,
                                value,
                                user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        return {'quota_set': self._get_quotas(context, id, user_id=user_id)}
Example #43
0
def validate_and_default_volume_size(bdm):
    if bdm.get("volume_size"):
        try:
            bdm["volume_size"] = utils.validate_integer(bdm["volume_size"], "volume_size", min_value=0)
        except exception.InvalidInput as e:
            raise exception.InvalidBDMFormat(details="Invalid volume_size.")
Example #44
0
def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
           swap=0, rxtx_factor=1.0, is_public=True):
    """Creates flavors."""
    if not flavorid:
        flavorid = uuid.uuid4()

    kwargs = {
        'memory_mb': memory,
        'vcpus': vcpus,
        'root_gb': root_gb,
        'ephemeral_gb': ephemeral_gb,
        'swap': swap,
        'rxtx_factor': rxtx_factor,
    }

    # ensure name do not exceed 255 characters
    utils.check_string_length(name, 'name', min_length=1, max_length=255)

    # ensure name does not contain any special characters
    valid_name = VALID_NAME_OR_ID_REGEX.search(name)
    if not valid_name:
        msg = _("names can only contain [a-zA-Z0-9_.- ]")
        raise exception.InvalidInput(reason=msg)

    # NOTE(vish): Internally, flavorid is stored as a string but it comes
    #             in through json as an integer, so we convert it here.
    flavorid = unicode(flavorid)

    # ensure leading/trailing whitespaces not present.
    if flavorid.strip() != flavorid:
        msg = _("id cannot contain leading and/or trailing whitespace(s)")
        raise exception.InvalidInput(reason=msg)

    # ensure flavor id does not exceed 255 characters
    utils.check_string_length(flavorid, 'id', min_length=1,
                              max_length=255)

    # ensure flavor id does not contain any special characters
    valid_flavor_id = VALID_NAME_OR_ID_REGEX.search(flavorid)
    if not valid_flavor_id:
        msg = _("id can only contain [a-zA-Z0-9_.- ]")
        raise exception.InvalidInput(reason=msg)

    # Some attributes are positive ( > 0) integers
    for option in ['memory_mb', 'vcpus']:
        kwargs[option] = utils.validate_integer(kwargs[option], option, 1,
                                                sys.maxint)

    # Some attributes are non-negative ( >= 0) integers
    for option in ['root_gb', 'ephemeral_gb', 'swap']:
        kwargs[option] = utils.validate_integer(kwargs[option], option, 0,
                                                sys.maxint)

    # rxtx_factor should be a positive float
    try:
        kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
        if kwargs['rxtx_factor'] <= 0:
            raise ValueError()
    except ValueError:
        msg = _("'rxtx_factor' argument must be a positive float")
        raise exception.InvalidInput(reason=msg)

    kwargs['name'] = name
    kwargs['flavorid'] = flavorid
    # ensure is_public attribute is boolean
    try:
        kwargs['is_public'] = strutils.bool_from_string(
            is_public, strict=True)
    except ValueError:
        raise exception.InvalidInput(reason=_("is_public must be a boolean"))

    try:
        return db.flavor_create(context.get_admin_context(), kwargs)
    except db_exc.DBError as e:
        LOG.exception(_('DB error: %s') % e)
        raise exception.InstanceTypeCreateFailed()
Example #45
0
def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
           swap=0, rxtx_factor=1.0, is_public=True):
    """Creates flavors."""
    if not flavorid:
        flavorid = uuidutils.generate_uuid()

    kwargs = {
        'memory_mb': memory,
        'vcpus': vcpus,
        'root_gb': root_gb,
        'ephemeral_gb': ephemeral_gb,
        'swap': swap,
        'rxtx_factor': rxtx_factor,
    }

    if isinstance(name, six.string_types):
        name = name.strip()
    # ensure name do not exceed 255 characters
    utils.check_string_length(name, 'name', min_length=1, max_length=255)

    # ensure name does not contain any special characters
    valid_name = parameter_types.valid_name_regex_obj.search(name)
    if not valid_name:
        msg = _("Flavor names can only contain printable characters "
                "and horizontal spaces.")
        raise exception.InvalidInput(reason=msg)

    # NOTE(vish): Internally, flavorid is stored as a string but it comes
    #             in through json as an integer, so we convert it here.
    flavorid = six.text_type(flavorid)

    # ensure leading/trailing whitespaces not present.
    if flavorid.strip() != flavorid:
        msg = _("id cannot contain leading and/or trailing whitespace(s)")
        raise exception.InvalidInput(reason=msg)

    # ensure flavor id does not exceed 255 characters
    utils.check_string_length(flavorid, 'id', min_length=1,
                              max_length=255)

    # ensure flavor id does not contain any special characters
    valid_flavor_id = VALID_ID_REGEX.search(flavorid)
    if not valid_flavor_id:
        msg = _("Flavor id can only contain letters from A-Z (both cases), "
                "periods, dashes, underscores and spaces.")
        raise exception.InvalidInput(reason=msg)

    # NOTE(wangbo): validate attributes of the creating flavor.
    # ram and vcpus should be positive ( > 0) integers.
    # disk, ephemeral and swap should be non-negative ( >= 0) integers.
    flavor_attributes = {
        'memory_mb': ('ram', 1),
        'vcpus': ('vcpus', 1),
        'root_gb': ('disk', 0),
        'ephemeral_gb': ('ephemeral', 0),
        'swap': ('swap', 0)
    }

    for key, value in flavor_attributes.items():
        kwargs[key] = utils.validate_integer(kwargs[key], value[0], value[1],
                                             db.MAX_INT)

    # rxtx_factor should be a positive float
    try:
        kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
        if (kwargs['rxtx_factor'] <= 0 or
                kwargs['rxtx_factor'] > db.SQL_SP_FLOAT_MAX):
            raise ValueError()
    except ValueError:
        msg = (_("'rxtx_factor' argument must be a float between 0 and %g") %
               db.SQL_SP_FLOAT_MAX)
        raise exception.InvalidInput(reason=msg)

    kwargs['name'] = name
    kwargs['flavorid'] = flavorid
    # ensure is_public attribute is boolean
    try:
        kwargs['is_public'] = strutils.bool_from_string(
            is_public, strict=True)
    except ValueError:
        raise exception.InvalidInput(reason=_("is_public must be a boolean"))

    flavor = objects.Flavor(context=context.get_admin_context(), **kwargs)
    flavor.create()
    return flavor
Example #46
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            if not uuidutils.is_uuid_like(id):
                msg = _('Invalid uuid %s') % id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(id, 'id')
            except exception.InvalidInput as exc:
                raise webob.exc.HTTPBadRequest(
                    explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                # Check to see if there are any instances on this compute host
                # because if there are, we need to block the service (and
                # related compute_nodes record) delete since it will impact
                # resource accounting in Placement and orphan the compute node
                # resource provider.
                # TODO(mriedem): Use a COUNT SQL query-based function instead
                # of InstanceList.get_uuids_by_host for performance.
                instance_uuids = objects.InstanceList.get_uuids_by_host(
                    context, service['host'])
                if instance_uuids:
                    raise webob.exc.HTTPConflict(
                        explanation=_('Unable to delete compute service that '
                                      'is hosting instances. Migrate or '
                                      'delete the instances first.'))

                aggrs = self.aggregate_api.get_aggregates_by_host(
                    context, service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(
                        context, ag.id, service.host)
                # remove the corresponding resource provider record from
                # placement for this compute node
                self.placementclient.delete_resource_provider(
                    context, service.compute_node, cascade=True)
                # remove the host_mapping of this host.
                try:
                    hm = objects.HostMapping.get_by_host(context, service.host)
                    hm.destroy()
                except exception.HostMappingNotFound:
                    # It's possible to startup a nova-compute service and then
                    # delete it (maybe it was accidental?) before mapping it to
                    # a cell using discover_hosts, so we just ignore this.
                    pass
            self.host_api.service_delete(context, id)

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
        except exception.ServiceNotUnique:
            explanation = _("Service id %s refers to multiple services.") % id
            raise webob.exc.HTTPBadRequest(explanation=explanation)
def create(name,
           memory,
           vcpus,
           root_gb,
           ephemeral_gb=0,
           flavorid=None,
           swap=0,
           rxtx_factor=1.0,
           is_public=True,
           description=None):
    """Creates flavors."""
    if not flavorid:
        flavorid = uuidutils.generate_uuid()

    kwargs = {
        'memory_mb': memory,
        'vcpus': vcpus,
        'root_gb': root_gb,
        'ephemeral_gb': ephemeral_gb,
        'swap': swap,
        'rxtx_factor': rxtx_factor,
        'description': description
    }

    if isinstance(name, six.string_types):
        name = name.strip()

    # NOTE(vish): Internally, flavorid is stored as a string but it comes
    #             in through json as an integer, so we convert it here.
    flavorid = six.text_type(flavorid)

    # NOTE(wangbo): validate attributes of the creating flavor.
    # ram and vcpus should be positive ( > 0) integers.
    # disk, ephemeral and swap should be non-negative ( >= 0) integers.
    flavor_attributes = {
        'memory_mb': ('ram', 1),
        'vcpus': ('vcpus', 1),
        'root_gb': ('disk', 0),
        'ephemeral_gb': ('ephemeral', 0),
        'swap': ('swap', 0)
    }

    for key, value in flavor_attributes.items():
        kwargs[key] = utils.validate_integer(kwargs[key], value[0], value[1],
                                             db.MAX_INT)

    # rxtx_factor should be a positive float
    try:
        kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
        if (kwargs['rxtx_factor'] <= 0
                or kwargs['rxtx_factor'] > db.SQL_SP_FLOAT_MAX):
            raise ValueError()
    except ValueError:
        msg = (_("'rxtx_factor' argument must be a float between 0 and %g") %
               db.SQL_SP_FLOAT_MAX)
        raise exception.InvalidInput(reason=msg)

    kwargs['name'] = name
    kwargs['flavorid'] = flavorid
    # ensure is_public attribute is boolean
    try:
        kwargs['is_public'] = strutils.bool_from_string(is_public, strict=True)
    except ValueError:
        raise exception.InvalidInput(reason=_("is_public must be a boolean"))

    flavor = objects.Flavor(context=context.get_admin_context(), **kwargs)
    flavor.create()
    return flavor
Example #48
0
    def create(self, req, body):
        """Creates a new server for a given user."""
        if not self.is_valid_body(body, 'server'):
            raise exc.HTTPUnprocessableEntity()

        context = req.environ['nova.context']
        server_dict = body['server']
        password = self._get_server_admin_password(server_dict)

        if 'name' not in server_dict:
            msg = _("Server name is not defined")
            raise exc.HTTPBadRequest(explanation=msg)

        name = server_dict['name']
        self._validate_server_name(name)
        name = name.strip()

        image_uuid = self._image_from_req_data(body)

        personality = server_dict.get('personality')
        config_drive = None
        if self.ext_mgr.is_loaded('os-config-drive'):
            config_drive = server_dict.get('config_drive')

        injected_files = []
        if personality:
            injected_files = self._get_injected_files(personality)

        sg_names = []
        if self.ext_mgr.is_loaded('os-security-groups'):
            security_groups = server_dict.get('security_groups')
            if security_groups is not None:
                try:
                    sg_names = [sg['name'] for sg in security_groups
                                if sg.get('name')]
                except AttributeError:
                    msg = _("Invalid input for field/attribute %(path)s."
                           " Value: %(value)s. %(message)s") % {
                               'path': 'security_groups',
                               'value': security_groups,
                               'message': ''
                           }
                    raise exc.HTTPBadRequest(explanation=msg)
        if not sg_names:
            sg_names.append('default')

        sg_names = list(set(sg_names))

        requested_networks = self._determine_requested_networks(server_dict)

        (access_ip_v4, ) = server_dict.get('accessIPv4'),
        if access_ip_v4 is not None:
            self._validate_access_ipv4(access_ip_v4)

        (access_ip_v6, ) = server_dict.get('accessIPv6'),
        if access_ip_v6 is not None:
            self._validate_access_ipv6(access_ip_v6)

        flavor_id = self._flavor_id_from_req_data(body)

        # optional openstack extensions:
        key_name = self._extract(server_dict, 'os-keypairs', 'key_name')
        availability_zone = self._extract(server_dict, 'os-availability-zone',
                                          'availability_zone')
        user_data = self._extract(server_dict, 'os-user-data', 'user_data')
        self._validate_user_data(user_data)

        legacy_bdm, block_device_mapping = self._extract_bdm(server_dict)

        ret_resv_id = False
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = 1
        max_count = 1
        if self.ext_mgr.is_loaded('os-multiple-create'):
            ret_resv_id = server_dict.get('return_reservation_id', False)
            min_count = server_dict.get('min_count', 1)
            max_count = server_dict.get('max_count', min_count)

        try:
            min_count = utils.validate_integer(
                min_count, "min_count", min_value=1)
            max_count = utils.validate_integer(
                max_count, "max_count", min_value=1)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        auto_disk_config = False
        if self.ext_mgr.is_loaded('OS-DCF'):
            auto_disk_config = server_dict.get('auto_disk_config')

        scheduler_hints = {}
        if self.ext_mgr.is_loaded('OS-SCH-HNT'):
            scheduler_hints = server_dict.get('scheduler_hints', {})

        check_server_group_quota = self.ext_mgr.is_loaded(
                'os-server-group-quotas')
        try:
            _get_inst_type = flavors.get_flavor_by_flavor_id
            inst_type = _get_inst_type(flavor_id, ctxt=context,
                                       read_deleted="no")

            (instances, resv_id) = self.compute_api.create(context,
                        inst_type,
                        image_uuid,
                        display_name=name,
                        display_description=name,
                        key_name=key_name,
                        metadata=server_dict.get('metadata', {}),
                        access_ip_v4=access_ip_v4,
                        access_ip_v6=access_ip_v6,
                        injected_files=injected_files,
                        admin_password=password,
                        min_count=min_count,
                        max_count=max_count,
                        requested_networks=requested_networks,
                        security_group=sg_names,
                        user_data=user_data,
                        availability_zone=availability_zone,
                        config_drive=config_drive,
                        block_device_mapping=block_device_mapping,
                        auto_disk_config=auto_disk_config,
                        scheduler_hints=scheduler_hints,
                        legacy_bdm=legacy_bdm,
                        check_server_group_quota=check_server_group_quota)
        except (exception.QuotaError,
                exception.PortLimitExceeded) as error:
            raise exc.HTTPForbidden(
                explanation=error.format_message(),
                headers={'Retry-After': 0})
        except messaging.RemoteError as err:
            msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,
                                                 'err_msg': err.value}
            raise exc.HTTPBadRequest(explanation=msg)
        except UnicodeDecodeError as error:
            msg = "UnicodeError: %s" % error
            raise exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            # The remaining cases can be handled in a standard fashion.
            self._handle_create_exception(*sys.exc_info())

        # If the caller wanted a reservation_id, return it
        if ret_resv_id:
            return wsgi.ResponseObject({'reservation_id': resv_id})

        req.cache_db_instances(instances)
        server = self._view_builder.create(req, instances[0])

        if CONF.enable_instance_password:
            server['server']['adminPass'] = password

        robj = wsgi.ResponseObject(server)

        return self._add_location(robj)
Example #49
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        try:
            # NOTE(alex_xu): back-compatible with db layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            nova.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        try:
            # NOTE(alex_xu): back-compatible with db layer hard-code admin
            # permission checks.
            nova.context.authorize_project_context(context, id)
            settable_quotas = QUOTAS.get_settable_quotas(context,
                                                         project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we figure
        # out if we have bad keys or if we need to forcibly set quotas or
        # if some of the values for the quotas can be converted to integers.
        for key, value in quota_set.items():
            if (key not in self.supported_quotas
                    and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # NOTE(dims): Pass #2 - In this loop for quota_set.items(), based on
        # force_update flag we validate the quota limit. A loop just for
        # the validation of min/max values ensure that we can bail out if
        # any of the items in the set is bad.
        valid_quotas = {}
        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if not force_update:
                minimum = settable_quotas[key]['minimum']
                maximum = settable_quotas[key]['maximum']
                self._validate_quota_limit(key, value, minimum, maximum)
            valid_quotas[key] = value

        # NOTE(dims): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one
        # shot without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                objects.Quotas.create_limit(context,
                                            project_id,
                                            key,
                                            value,
                                            user_id=user_id)
            except exception.QuotaExists:
                objects.Quotas.update_limit(context,
                                            project_id,
                                            key,
                                            value,
                                            user_id=user_id)
        values = self._get_quotas(context, id, user_id=user_id)
        return self._format_quota_set(None, values)
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        project_id = id
        child_list = []
        parent_id = None
        if hasattr(context, 'auth_token') and hasattr(context, 'project_id'):
            if (context.auth_token and context.project_id):
                token = context.auth_token
                headers = {
                    "X-Auth-Token": token,
                    "Content-type": "application/json",
                    "Accept": "text/json"
                }
                params = {}
                auth_host = KEYSTONE_CONF.keystone_authtoken.auth_host
                auth_port = int(KEYSTONE_CONF.keystone_authtoken.auth_port)
                auth_server = '%s:%s' % (auth_host, auth_port)
                """ The keytsone output for the following url is used for
                checking whether the given project is a root project or not """

                auth_url = '/%s/%s/%s' % ("v3", "projects", project_id)
                conn = httplib.HTTPConnection(auth_server)
                conn.request("GET",
                             auth_url,
                             json.dumps(params),
                             headers=headers)
                response = conn.getresponse()
                data = response.read()
                data = json.loads(data)
                if not "project" in data:
                    raise webob.exc.HTTPForbidden()

                try:
                    parent_id = data["project"]["parent_id"]
                except:
                    pass

                target = {"project_id": parent_id}
                try:
                    if parent_id:
                        authorize_update(context, target=target)
                        nova.context.authorize_root_or_parent_project_context(
                            context, parent_id)
                    else:
                        authorize_root_update(context)
                        nova.context.authorize_root_or_parent_project_context(
                            context, project_id)
                except exception.Forbidden:
                    raise webob.exc.HTTPForbidden()

                # The following url is for finding the subtree

                if parent_id:
                    auth_url = '/%s/%s/%s?%s' % ("v3", "projects", parent_id,
                                                 "subtree")
                else:
                    auth_url = '/%s/%s/%s?%s' % ("v3", "projects", project_id,
                                                 "subtree")

                conn.request("GET",
                             auth_url,
                             json.dumps(params),
                             headers=headers)
                response = conn.getresponse()
                data = response.read()
                data = json.loads(data)
                subtree = []
                try:
                    subtree = data["project"]["subtree"]
                except:
                    pass
                for item in subtree:
                    project_info = item["project"]
                    try:
                        if project_info["parent_id"] == parent_id:
                            child_list.append(project_info["id"])
                    except:
                        pass
        if parent_id is not None:
            if id not in child_list:
                raise exception.InvalidParent(parent_id=parent_id,
                                              project_id=project_id)

        ################
        params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
        user_id = params.get('user_id', [None])[0]

        quota_set = body['quota_set']
        force_update = strutils.bool_from_string(
            quota_set.get('force', 'False'))

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context,
                                                         project_id,
                                                         parent_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        for key, value in quota_set.items():
            if (key not in self.supported_quotas
                    and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    value = utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())
        LOG.debug("Force update quotas: %s", force_update)

        for key, value in body['quota_set'].iteritems():
            if key == 'force' or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if not force_update:
                minimum = settable_quotas[key]['minimum']
                maximum = settable_quotas[key]['maximum']
                self._validate_quota_limit(key, value, minimum, maximum)
            try:
                objects.Quotas.create_limit(context,
                                            project_id,
                                            key,
                                            value,
                                            user_id=user_id)
            except exception.QuotaExists:
                objects.Quotas.update_limit(context,
                                            project_id,
                                            key,
                                            value,
                                            user_id=user_id)
            if parent_id:
                db.quota_allocated_update(context, parent_id, child_list)
            else:
                db.quota_allocated_update(context, project_id, child_list)
        return self._format_quota_set(
            id, self._get_quotas(context, id, user_id=user_id))
Example #51
0
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context, project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        for key, value in quota_set.items():
            if (key not in self.supported_quotas
                and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    value = utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if not force_update:
                minimum = settable_quotas[key]['minimum']
                maximum = settable_quotas[key]['maximum']
                self._validate_quota_limit(key, value, minimum, maximum)

            try:
                objects.Quotas.create_limit(context, project_id,
                                            key, value, user_id=user_id)
            except exception.QuotaExists:
                objects.Quotas.update_limit(context, project_id,
                                            key, value, user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        values = self._get_quotas(context, id, user_id=user_id)
        return self._format_quota_set(None, values)
    def update(self, req, id, body):
        context = req.environ['nova.context']
        authorize_update(context)
        params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
        project_id = params.get('project_id', [None])[0]
        user_id = params.get('user_id', [None])[0]
        
        bad_keys = []
        try:
            if (user_id == None) and (project_id == None):
                 nova.context.authorize_domain_context(context, id)
                 settable_quotas = QUOTAS.get_settable_quotas_for_domain(context, id)
            elif user_id:
                 nova.context.authorize_project_user_context(context, project_id, user_id)
                 settable_quotas = QUOTAS.get_settable_quotas_for_domain(context, id, project_id=project_id, user_id=user_id)
                 LOG.debug(_("Inside the User Quota %(key)s used: "), {'key': settable_quotas})
                 if len(settable_quotas) <= 1:
                     ## because settable_quotas dict may have project_id as one key
                     raise webob.exc.HTTPBadRequest(explanation='The quotas for the project are not set. So, first define the quotas for the project')
            else:
                 nova.context.authorize_domain_project_context(context, id, project_id)
                 settable_quotas = QUOTAS.get_settable_quotas_for_domain(context, id, project_id=project_id)
                 LOG.debug(_("Inside the Project Quota %(key)s used: "), {'key': settable_quotas})
                 if len(settable_quotas) <= 1:
                     ## because settable_quotas dict may have domain_id as one key
                     raise webob.exc.HTTPBadRequest(explanation='The quotas for the domain of this project are not set. So, first define the quotas for the domain')

        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']
        parNode_notFoundKeys = []
        if 'notfound_keys' in settable_quotas:
            parNode_notFoundKeys = settable_quotas['notfound_keys']
        for key, value in quota_set.items():
            if (key not in QUOTAS and
                    key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue

            if key in parNode_notFoundKeys:
                excepMessage = 'The quota value for resource ' + key + ' is not defined for the parent'
                raise webob.exc.HTTPBadRequest(explanation=excepMessage)

            if key not in NON_QUOTA_KEYS and value:
                try:
                    value = utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    LOG.warn(e.format_message())
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if len(bad_keys) > 0:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        LOG.debug(_("Quota %(key)s used: "),
                              {'key': settable_quotas})

        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            value = int(value)
            if value >= 0:
                quota_value = settable_quotas.get(key)
                if quota_value and quota_value['limit'] >= 0:
                    quota_used = (quota_value['in_use'] +
                                  quota_value['reserved'])
                    LOG.debug(_("Quota %(key)s used: %(quota_used)s, "
                                "value: %(value)s."),
                              {'key': key, 'quota_used': quota_used,
                               'value': value})
                    if quota_used > value:
                        msg = (_("Quota value %(value)s for %(key)s are "
                                "less than already used and reserved "
                                "%(quota_used)s") %
                                {'value': value, 'key': key,
                                 'quota_used': quota_used})
                        raise webob.exc.HTTPBadRequest(explanation=msg)

            minimum = settable_quotas[key]['minimum']
            maximum = settable_quotas[key]['maximum']
            self._validate_quota_limit(key, value, minimum, maximum)
            try:
                if (user_id == None) and (project_id == None):
                    db.domain_quota_create(context, id, key, value)
                else:
                    db.quota_create(context, project_id, key, value,
                                user_id=user_id)
            except exception.DomainQuotaExists:
                db.domain_quota_update(context, id, key, value)
            except exception.QuotaExists:
                db.quota_update(context, project_id, key, value, user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        return {'quota_set': self._get_quotas(context, id, user_id=user_id, project_id=project_id)}
Example #53
0
    def update(self, req, id, body):
        context = req.environ["nova.context"]
        authorize_update(context)
        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded("os-extended-quotas"):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded("os-user-quotas"):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get("QUERY_STRING", ""))
            user_id = params.get("user_id", [None])[0]

        try:
            settable_quotas = QUOTAS.get_settable_quotas(context, project_id, user_id=user_id)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, "quota_set"):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body["quota_set"]
        for key, value in quota_set.items():
            if key not in QUOTAS and key not in NON_QUOTA_KEYS:
                bad_keys.append(key)
                continue
            if key == "force" and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    value = utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    LOG.warn(e.format_message())
                    raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        LOG.debug(_("force update quotas: %s") % force_update)

        if len(bad_keys) > 0:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            quotas = self._get_quotas(context, id, user_id=user_id, usages=True)
        except exception.NotAuthorized:
            raise webob.exc.HTTPForbidden()

        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if force_update is not True and value >= 0:
                quota_value = quotas.get(key)
                if quota_value and quota_value["limit"] >= 0:
                    quota_used = quota_value["in_use"] + quota_value["reserved"]
                    LOG.debug(
                        _("Quota %(key)s used: %(quota_used)s, " "value: %(value)s."),
                        {"key": key, "quota_used": quota_used, "value": value},
                    )
                    if quota_used > value:
                        msg = _(
                            "Quota value %(value)s for %(key)s are "
                            "less than already used and reserved "
                            "%(quota_used)s"
                        ) % {"value": value, "key": key, "quota_used": quota_used}
                        raise webob.exc.HTTPBadRequest(explanation=msg)

            minimum = settable_quotas[key]["minimum"]
            maximum = settable_quotas[key]["maximum"]
            self._validate_quota_limit(value, minimum, maximum)
            try:
                db.quota_create(context, project_id, key, value, user_id=user_id)
            except exception.QuotaExists:
                db.quota_update(context, project_id, key, value, user_id=user_id)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        return {"quota_set": self._get_quotas(context, id, user_id=user_id)}
Example #54
0
def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
           swap=0, rxtx_factor=1.0, is_public=True):
    """Creates flavors."""
    if not flavorid:
        flavorid = uuid.uuid4()

    kwargs = {
        'memory_mb': memory,
        'vcpus': vcpus,
        'root_gb': root_gb,
        'ephemeral_gb': ephemeral_gb,
        'swap': swap,
        'rxtx_factor': rxtx_factor,
    }

    if isinstance(name, six.string_types):
        name = name.strip()
    # ensure name do not exceed 255 characters
    utils.check_string_length(name, 'name', min_length=1, max_length=255)

    # ensure name does not contain any special characters
    valid_name = VALID_NAME_OR_ID_REGEX.search(name)
    if not valid_name:
        msg = _("names can only contain [a-zA-Z0-9_.- ]")
        raise exception.InvalidInput(reason=msg)

    # NOTE(vish): Internally, flavorid is stored as a string but it comes
    #             in through json as an integer, so we convert it here.
    flavorid = unicode(flavorid)

    # ensure leading/trailing whitespaces not present.
    if flavorid.strip() != flavorid:
        msg = _("id cannot contain leading and/or trailing whitespace(s)")
        raise exception.InvalidInput(reason=msg)

    # ensure flavor id does not exceed 255 characters
    utils.check_string_length(flavorid, 'id', min_length=1,
                              max_length=255)

    # ensure flavor id does not contain any special characters
    valid_flavor_id = VALID_NAME_OR_ID_REGEX.search(flavorid)
    if not valid_flavor_id:
        msg = _("id can only contain [a-zA-Z0-9_.- ]")
        raise exception.InvalidInput(reason=msg)

    # Some attributes are positive ( > 0) integers
    for option in ['memory_mb', 'vcpus']:
        kwargs[option] = utils.validate_integer(kwargs[option], option, 1,
                                                sys.maxint)

    # Some attributes are non-negative ( >= 0) integers
    for option in ['root_gb', 'ephemeral_gb', 'swap']:
        kwargs[option] = utils.validate_integer(kwargs[option], option, 0,
                                                sys.maxint)

    # rxtx_factor should be a positive float
    try:
        kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
        if kwargs['rxtx_factor'] <= 0:
            raise ValueError()
    except ValueError:
        msg = _("'rxtx_factor' argument must be a positive float")
        raise exception.InvalidInput(reason=msg)

    kwargs['name'] = name
    kwargs['flavorid'] = flavorid
    # ensure is_public attribute is boolean
    try:
        kwargs['is_public'] = strutils.bool_from_string(
            is_public, strict=True)
    except ValueError:
        raise exception.InvalidInput(reason=_("is_public must be a boolean"))

    try:
        return db.flavor_create(context.get_admin_context(), kwargs)
    except db_exc.DBError as e:
        LOG.exception(_('DB error: %s') % e)
        raise exception.InstanceTypeCreateFailed()