def delete(self, req, id): """Deletes the specified service.""" context = req.environ['compute.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 an existing agent build.""" context = req.environ['compute.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 update(self, req, id, body): """Update an existing agent build.""" context = req.environ['compute.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 delete(self, req, id): """Deletes an existing agent build.""" context = req.environ['compute.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 _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 update(self, req, id, body): """Update an existing agent build.""" context = req.environ['compute.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 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['compute.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 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['compute.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 update(self, req, id, body): """Update an existing agent build.""" context = req.environ['compute.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['compute.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 compute 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. jacket.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 update(self, req, id, body): context = req.environ['compute.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 compute 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. jacket.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 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 _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["compute.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 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 = 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'] > 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")) flavor = objects.Flavor(context=context.get_admin_context(), **kwargs) flavor.create() return flavor
def update(self, req, id, body): context = req.environ['compute.context'] authorize_update(context) try: # NOTE(alex_xu): back-compatible with compute 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. jacket.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 compute layer hard-code admin # permission checks. jacket.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 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['compute.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) image_uuid_specified = bool(image_uuid) legacy_bdm, block_device_mapping = self._extract_bdm(server_dict, image_uuid_specified) 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', {}) parse_az = self.compute_api.parse_availability_zone availability_zone, host, node = parse_az(context, availability_zone) 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, forced_host=host, forced_node=node, 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()) 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: # 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 _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["compute.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