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)
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)
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
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)
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)
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())
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)
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)
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)}
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())
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."))
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."))
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."))
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}}
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)
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)
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 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)
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)
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}}
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)
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}}
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)
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]
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)
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 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))
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))
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()
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)
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
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
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))
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)
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)
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)}
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.")
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()
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
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
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)
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))
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)}
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)}
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()