def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ['nova.context'] context.can(ms_policies.POLICY_ROOT % 'migrate') host_name = None if (api_version_request.is_supported(req, min_version='2.56') and body['migrate'] is not None): host_name = body['migrate'].get('host') instance = common.get_instance(self.compute_api, context, id) try: self.compute_api.resize(req.environ['nova.context'], instance, host_name=host_name) except (exception.TooManyInstances, exception.QuotaError) as e: raise exc.HTTPForbidden(explanation=e.format_message()) except (exception.InstanceIsLocked, exception.CannotMigrateWithTargetHost) as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'migrate', id) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except (exception.NoValidHost, exception.ComputeHostNotFound, exception.CannotMigrateToSameHost) as e: raise exc.HTTPBadRequest(explanation=e.format_message())
def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" context = req.environ["nova.context"] instance = self._get_server(context, req, instance_id) try: self.compute_api.resize(context, instance, flavor_id, **kwargs) except exception.QuotaError as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message(), headers={'Retry-After': 0}) except exception.FlavorNotFound: msg = _("Unable to locate requested flavor.") raise exc.HTTPBadRequest(explanation=msg) except exception.CannotResizeToSameFlavor: msg = _("Resize requires a flavor change.") raise exc.HTTPBadRequest(explanation=msg) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'resize') except exception.ImageNotAuthorized: msg = _("You are not authorized to access the image " "the instance was started with.") raise exc.HTTPUnauthorized(explanation=msg) except exception.ImageNotFound: msg = _("Image that the instance was started " "with could not be found.") raise exc.HTTPBadRequest(explanation=msg) except exception.Invalid: msg = _("Invalid instance image.") raise exc.HTTPBadRequest(explanation=msg) return webob.Response(status_int=202)
def attach(self, req, id, body): server_id = id context = req.environ['nova.context'] authorize_attach(context) volume_id = body['attach']['volume_id'] device = body['attach'].get('device') disk_bus = body['attach'].get('disk_bus') device_type = body['attach'].get('device_type') instance = common.get_instance(self.compute_api, context, server_id, want_objects=True) try: self.compute_api.attach_volume(context, instance, volume_id, device, disk_bus=disk_bus, device_type=device_type) except exception.VolumeNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'attach_volume', server_id) except exception.InvalidVolume as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InvalidDevicePath as e: raise exc.HTTPBadRequest(explanation=e.format_message())
def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ['nova.context'] authorize(context, 'migrate') instance = common.get_instance(self.compute_api, context, id, want_objects=True) try: self.compute_api.resize(req.environ['nova.context'], instance) except exception.TooManyInstances as e: raise exc.HTTPRequestEntityTooLarge(explanation=e.format_message()) except exception.QuotaError as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message(), headers={'Retry-After': 0}) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'migrate') except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.NoValidHost as e: raise exc.HTTPBadRequest(explanation=e.format_message()) return webob.Response(status_int=202)
def _rescue(self, req, id, body): """Rescue an instance.""" context = req.environ["nova.context"] authorize(context) if body['rescue'] and 'adminPass' in body['rescue']: password = body['rescue']['adminPass'] else: password = utils.generate_password() instance = common.get_instance(self.compute_api, context, id) try: rescue_image_ref = None if self.ext_mgr.is_loaded("os-extended-rescue-with-image"): if body['rescue'] and 'rescue_image_ref' in body['rescue']: rescue_image_ref = self._rescue_image_validation( body['rescue']['rescue_image_ref']) self.compute_api.rescue(context, instance, rescue_password=password, rescue_image_ref=rescue_image_ref) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'rescue', id) except exception.InvalidVolume as volume_error: raise exc.HTTPConflict(explanation=volume_error.format_message()) except exception.InstanceNotRescuable as non_rescuable: raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) return {'adminPass': password}
def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" context = req.environ["nova.context"] authorize(context, action="resize") instance = self._get_server(context, req, instance_id) try: self.compute_api.resize(context, instance, flavor_id, **kwargs) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message(), headers={"Retry-After": 0}) except exception.FlavorNotFound: msg = _("Unable to locate requested flavor.") raise exc.HTTPBadRequest(explanation=msg) except exception.CannotResizeToSameFlavor: msg = _("Resize requires a flavor change.") raise exc.HTTPBadRequest(explanation=msg) except (exception.CannotResizeDisk, exception.AutoDiskConfigDisabledByImage) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "resize", instance_id) except exception.ImageNotAuthorized: msg = _("You are not authorized to access the image " "the instance was started with.") raise exc.HTTPUnauthorized(explanation=msg) except exception.ImageNotFound: msg = _("Image that the instance was started " "with could not be found.") raise exc.HTTPBadRequest(explanation=msg) except (exception.NoValidHost, exception.AutoDiskConfigDisabledByImage) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.Invalid: msg = _("Invalid instance image.") raise exc.HTTPBadRequest(explanation=msg)
def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ["nova.context"] authorize(context, "migrate") # ADD by zhjh 2014-12-03 migrate instance to a selected host. host = body["migrate"]["host"] if (isinstance(body["migrate"], dict) and "host" in body["migrate"]) else None try: instance = self.compute_api.get(context, id, want_objects=True) # MOD by zhjh 2014-12-04 migrate instance to a selected host. # self.compute_api.resize(req.environ['nova.context'], instance) self.compute_api.resize(req.environ["nova.context"], instance, host=host) except exception.QuotaError as error: raise exc.HTTPRequestEntityTooLarge(explanation=error.format_message(), headers={"Retry-After": 0}) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "migrate") except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except Exception as e: LOG.exception(_("Error in migrate %s"), e) raise exc.HTTPBadRequest() return webob.Response(status_int=202)
def _action_reboot(self, req, id, body): if 'reboot' in body and 'type' in body['reboot']: if not isinstance(body['reboot']['type'], six.string_types): msg = _("Argument 'type' for reboot must be a string") LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) valid_reboot_types = ['HARD', 'SOFT'] reboot_type = body['reboot']['type'].upper() if not valid_reboot_types.count(reboot_type): msg = _("Argument 'type' for reboot is not HARD or SOFT") LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) else: msg = _("Missing argument 'type' for reboot") LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) context = req.environ['nova.context'] instance = self._get_server(context, req, id) try: self.compute_api.reboot(context, instance, reboot_type) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'reboot') return webob.Response(status_int=202)
def _update_instance_metadata(self, context, server_id, metadata, delete=False): try: server = common.get_instance(self.compute_api, context, server_id, want_objects=True) return self.compute_api.update_instance_metadata(context, server, metadata, delete) except exception.InvalidMetadata as error: raise exc.HTTPBadRequest(explanation=error.format_message()) except exception.InvalidMetadataSize as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message()) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'update metadata')
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ["nova.context"] entity = body.get("createImage", {}) image_name = entity.get("name") if not image_name: msg = _("createImage entity requires name attribute") raise exc.HTTPBadRequest(explanation=msg) props = {} metadata = entity.get("metadata", {}) common.check_img_metadata_quota_limit(context, metadata) try: props.update(metadata) except ValueError: msg = _("Invalid metadata") raise exc.HTTPBadRequest(explanation=msg) instance = self._get_server(context, id) try: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=props) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "createImage") # build location of newly-created image entity image_id = str(image["id"]) image_ref = os.path.join(req.application_url, context.project_id, "images", image_id) resp = webob.Response(status_int=202) resp.headers["Location"] = image_ref return resp
def _update_instance_metadata(self, context, server_id, metadata, delete=False): try: server = self.compute_api.get(context, server_id, want_objects=True) return self.compute_api.update_instance_metadata(context, server, metadata, delete) except exception.InstanceNotFound: msg = _('Server does not exist') raise exc.HTTPNotFound(explanation=msg) except (ValueError, AttributeError): msg = _("Malformed request body") raise exc.HTTPBadRequest(explanation=msg) except exception.InvalidMetadata as error: raise exc.HTTPBadRequest(explanation=error.format_message()) except exception.InvalidMetadataSize as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message()) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'update metadata')
def delete(self, req, server_id, id): """Deletes an existing metadata.""" context = req.environ['nova.context'] metadata = self._get_metadata(context, server_id) if id not in metadata: msg = _("Metadata item was not found") raise exc.HTTPNotFound(explanation=msg) try: server = self.compute_api.get(context, server_id, want_objects=True) self.compute_api.delete_instance_metadata(context, server, id) except exception.InstanceNotFound: msg = _('Server does not exist') raise exc.HTTPNotFound(explanation=msg) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'delete metadata')
def index(self, req, server_id): context = req.environ["nova.context"] context.can(sd_policies.BASE_POLICY_NAME) instance = common.get_instance(self.compute_api, context, server_id) try: if api_version_request.is_supported(req, min_version='2.48'): diagnostics = self.compute_api.get_instance_diagnostics( context, instance) return self._view_builder.instance_diagnostics(diagnostics) return self.compute_api.get_diagnostics(context, instance) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'get_diagnostics', server_id) except exception.InstanceNotReady as e: raise webob.exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceDiagnosticsNotSupported: # NOTE(snikitin): During upgrade we may face situation when env # has new API and old compute. New compute returns a # Diagnostics object. Old compute returns a dictionary. So we # can't perform a request correctly if compute is too old. msg = _('Compute node is too old. You must complete the ' 'upgrade process to be able to get standardized ' 'diagnostics data which is available since v2.48. However ' 'you are still able to get diagnostics data in ' 'non-standardized format which is available until v2.47.') raise webob.exc.HTTPBadRequest(explanation=msg) except NotImplementedError: common.raise_feature_not_supported()
def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host.""" context = req.environ["nova.context"] authorize(context, 'migrate_live') try: block_migration = body["migrate_live"]["block_migration"] disk_over_commit = body["migrate_live"]["disk_over_commit"] host = body["migrate_live"]["host"] except (TypeError, KeyError): msg = _("host, block_migration and disk_over_commit must " "be specified for live migration.") raise exc.HTTPBadRequest(explanation=msg) try: instance = self.compute_api.get(context, id) self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host) except (exception.ComputeServiceUnavailable, exception.InvalidHypervisorType, exception.UnableToMigrateToSelf, exception.DestinationHypervisorTooOld, exception.NoValidHost, exception.InvalidLocalStorage, exception.InvalidSharedStorage, exception.MigrationPreCheckError) as ex: raise exc.HTTPBadRequest(explanation=ex.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'migrate_live') except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) return webob.Response(status_int=202)
def _rescue(self, req, id, body): """Rescue an instance.""" context = req.environ["nova.context"] authorize(context) if body['rescue'] and 'adminPass' in body['rescue']: password = body['rescue']['adminPass'] else: password = utils.generate_password() instance = self._get_instance(context, id, want_objects=True) try: self.compute_api.rescue(context, instance, rescue_password=password) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'rescue') except exception.InvalidVolume as volume_error: raise exc.HTTPConflict(explanation=volume_error.format_message()) except exception.InstanceNotRescuable as non_rescuable: raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) except NotImplementedError: msg = _("The rescue operation is not implemented by this " "cloud.") raise exc.HTTPNotImplemented(explanation=msg) return {'adminPass': password}
def attach(self, req, id, body): server_id = id context = req.environ["nova.context"] authorize_attach(context) volume_id = body["attach"]["volume_id"] device = body["attach"].get("device") disk_bus = body["attach"].get("disk_bus") device_type = body["attach"].get("device_type") LOG.audit( _("Attach volume %(volume_id)s to instance %(server_id)s " "at %(device)s"), {"volume_id": volume_id, "device": device, "server_id": server_id}, context=context, ) instance = common.get_instance(self.compute_api, context, server_id, want_objects=True) try: self.compute_api.attach_volume( context, instance, volume_id, device, disk_bus=disk_bus, device_type=device_type ) except exception.VolumeNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "attach_volume") except exception.InvalidVolume as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InvalidDevicePath as e: raise exc.HTTPBadRequest(explanation=e.format_message())
def _action_reboot(self, req, id, body): if "reboot" in body and "type" in body["reboot"]: if not isinstance(body["reboot"]["type"], six.string_types): msg = _("Argument 'type' for reboot must be a string") LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) valid_reboot_types = ["HARD", "SOFT"] reboot_type = body["reboot"]["type"].upper() if not valid_reboot_types.count(reboot_type): msg = _("Argument 'type' for reboot is not HARD or SOFT") LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) else: msg = _("Missing argument 'type' for reboot") LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) context = req.environ["nova.context"] instance = self._get_server(context, req, id) try: self.compute_api.reboot(context, instance, reboot_type) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "reboot", id)
def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ['nova.context'] authorize(context, 'migrate') instance = common.get_instance(self.compute_api, context, id, want_objects=True) host = None if self.ext_mgr.is_loaded('os-migrate-host'): migrate_body = body.get('migrate') host = migrate_body.get('host') if migrate_body else None LOG.debug("Going to try to cold migrate %(uuid)s to %(host)s", {"uuid":instance["uuid"], "host":(host or "another host")}) try: self.compute_api.resize(req.environ['nova.context'], instance, migrate_host=host) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'migrate') except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.NoValidHost as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except Exception as e: LOG.exception(_LE("Error in migrate %s"), e) raise exc.HTTPBadRequest() return webob.Response(status_int=202)
def _unshelve(self, req, id, body): """Restore an instance from shelved mode.""" context = req.environ["nova.context"] context.can(shelve_policies.POLICY_ROOT % 'unshelve') instance = common.get_instance(self.compute_api, context, id) # We could potentially move this check to conductor and avoid the # extra API call to neutron when we support move operations with ports # having resource requests. if (instance.vm_state == vm_states.SHELVED_OFFLOADED and common.instance_has_port_with_resource_request( context, instance.uuid, self.network_api) and not common.supports_port_resource_request_during_move( req)): msg = _("The unshelve action on a server with ports having " "resource requests, like a port with a QoS minimum " "bandwidth policy, is not supported with this " "microversion") raise exc.HTTPBadRequest(explanation=msg) try: self.compute_api.unshelve(context, instance) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'unshelve', id)
def _action_rebuild(self, req, id, body): """Rebuild an instance with the given attributes.""" rebuild_dict = body["rebuild"] image_href = rebuild_dict["imageRef"] image_href = self._image_uuid_from_href(image_href) password = self._get_server_admin_password(rebuild_dict) context = req.environ["nova.context"] authorize(context, action="rebuild") instance = self._get_server(context, req, id) attr_map = {"name": "display_name", "metadata": "metadata"} rebuild_kwargs = {} if list(self.rebuild_extension_manager): self.rebuild_extension_manager.map(self._rebuild_extension_point, rebuild_dict, rebuild_kwargs) for request_attribute, instance_attribute in attr_map.items(): try: rebuild_kwargs[instance_attribute] = rebuild_dict[request_attribute] except (KeyError, TypeError): pass try: self.compute_api.rebuild(context, instance, image_href, password, **rebuild_kwargs) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "rebuild", id) except exception.InstanceNotFound: msg = _("Instance could not be found") raise exc.HTTPNotFound(explanation=msg) except exception.ImageNotFound: msg = _("Cannot find image for rebuild") raise exc.HTTPBadRequest(explanation=msg) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except ( exception.ImageNotActive, exception.FlavorDiskTooSmall, exception.FlavorMemoryTooSmall, exception.InvalidMetadata, exception.AutoDiskConfigDisabledByImage, ) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) instance = self._get_server(context, req, id) view = self._view_builder.show(req, instance, extend_address=False) # Add on the admin_password attribute since the view doesn't do it # unless instance passwords are disabled if CONF.enable_instance_password: view["server"]["adminPass"] = password robj = wsgi.ResponseObject(view) return self._add_location(robj)
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] entity = body.get("createImage", {}) image_name = entity.get("name") if not image_name: msg = _("createImage entity requires name attribute") raise exc.HTTPBadRequest(explanation=msg) 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 = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) try: if self.compute_api.is_volume_backed_instance(context, instance, bdms): policy.enforce(context, 'compute:snapshot_volume_backed', {'project_id': context.project_id, 'user_id': context.user_id}) image = self.compute_api.snapshot_volume_backed( context, instance, image_name, extra_properties=props) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=props) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'createImage', id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image['id']) url_prefix = self._view_builder._update_glance_link_prefix( req.application_url) image_ref = os.path.join(url_prefix, context.project_id, 'images', image_id) resp = webob.Response(status_int=202) resp.headers['Location'] = image_ref return resp
def _check_instance_in_valid_state(self, context, server_id, action): instance = common.get_instance(self.compute_api, context, server_id) if instance.vm_state not in (vm_states.ACTIVE, vm_states.PAUSED, vm_states.SUSPENDED, vm_states.STOPPED): exc = exception.InstanceInvalidState( attr="vm_state", instance_uuid=instance.uuid, state=instance.vm_state, method=action ) common.raise_http_conflict_for_instance_invalid_state(exc, action, server_id)
def attach(self, req, id, body): server_id = id context = req.environ['nova.context'] authorize_attach(context) volume_id = body['attach']['volume_id'] device = body['attach'].get('device') LOG.audit(_("Attach volume %(volume_id)s to instance %(server_id)s " "at %(device)s"), {'volume_id': volume_id, 'device': device, 'server_id': server_id}, context=context) try: instance = self.compute_api.get(context, server_id) self.compute_api.attach_volume(context, instance, volume_id, device) except (exception.InstanceNotFound, exception.VolumeNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'attach_volume') except exception.InvalidVolume as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InvalidDevicePath as e: raise exc.HTTPBadRequest(explanation=e.format_message())
def _rescue(self, req, id, body): """Rescue an instance.""" context = req.environ["nova.context"] authorize(context) if body['rescue'] and 'adminPass' in body['rescue']: password = body['rescue']['adminPass'] else: password = utils.generate_password() instance = common.get_instance(self.compute_api, context, id, want_objects=True) rescue_image_ref = None if body['rescue'] and 'rescue_image_ref' in body['rescue']: rescue_image_ref = body['rescue']['rescue_image_ref'] try: self.compute_api.rescue(context, instance, rescue_password=password, rescue_image_ref=rescue_image_ref) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'rescue') except exception.InvalidVolume as volume_error: raise exc.HTTPConflict(explanation=volume_error.format_message()) except exception.InstanceNotRescuable as non_rescuable: raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) if CONF.enable_instance_password: return {'adminPass': password} else: return {}
def create(self, req, server_id, body): """Attach an interface to an instance.""" context = req.environ['nova.context'] authorize(context) network_id = None port_id = None req_ip = None if body: attachment = body['interfaceAttachment'] network_id = attachment.get('net_id', None) port_id = attachment.get('port_id', None) try: req_ip = attachment['fixed_ips'][0]['ip_address'] except Exception: pass if network_id and port_id: msg = _("Must not input both network_id and port_id") raise exc.HTTPBadRequest(explanation=msg) if req_ip and not network_id: msg = _("Must input network_id when request IP address") raise exc.HTTPBadRequest(explanation=msg) if req_ip: try: netaddr.IPAddress(req_ip) except netaddr.AddrFormatError as e: raise exc.HTTPBadRequest(explanation=six.text_type(e)) try: instance = common.get_instance(self.compute_api, context, server_id) LOG.info(_LI("Attach interface"), instance=instance) vif = self.compute_api.attach_interface(context, instance, network_id, port_id, req_ip) except (exception.PortNotFound, exception.NetworkNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) except (exception.FixedIpAlreadyInUse, exception.InterfaceAttachFailedNoNetwork, exception.NoMoreFixedIps, exception.PortInUse, exception.NetworkDuplicated, exception.NetworkAmbiguous, exception.PortNotUsable) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except NotImplementedError: msg = _("Network driver does not support this function.") raise webob.exc.HTTPNotImplemented(explanation=msg) except exception.InterfaceAttachFailed: msg = _("Failed to attach interface") raise webob.exc.HTTPInternalServerError(explanation=msg) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'attach_interface', server_id) return self.show(req, server_id, vif['id'])
def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ['nova.context'] authorize(context, 'migrate') #MOD by zhjh 2015-02-13 migrate instance to a selected host. host = body["migrate"]["host"] if (isinstance(body['migrate'], dict) and 'host' in body['migrate']) else None instance = common.get_instance(self.compute_api, context, id, want_objects=True) try: #MOD by zhjh 2015-02-13 migrate instance to a selected host. #self.compute_api.resize(req.environ['nova.context'], instance) self.compute_api.resize(req.environ['nova.context'], instance, host=host) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'migrate') except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.NoValidHost as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except Exception as e: LOG.exception(_LE("Error in migrate %s"), e) raise exc.HTTPBadRequest() return webob.Response(status_int=202)
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ["nova.context"] authorize(context, action="create_image") entity = body["createImage"] image_name = entity["name"] metadata = entity.get("metadata", {}) common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(context, instance.uuid) try: if self.compute_api.is_volume_backed_instance(context, instance, bdms): authorize(context, action="create_image:allow_volume_backed") image = self.compute_api.snapshot_volume_backed( context, instance, image_name, extra_properties=metadata ) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=metadata) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "createImage", id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image["id"]) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers["Location"] = image_ref return resp
def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host.""" context = req.environ["nova.context"] authorize(context, action='migrate_live') block_migration = body["os-migrateLive"]["block_migration"] disk_over_commit = body["os-migrateLive"]["disk_over_commit"] host = body["os-migrateLive"]["host"] block_migration = strutils.bool_from_string(block_migration, strict=True) disk_over_commit = strutils.bool_from_string(disk_over_commit, strict=True) try: instance = common.get_instance(self.compute_api, context, id) self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host) except (exception.NoValidHost, exception.ComputeServiceUnavailable, exception.InvalidHypervisorType, exception.InvalidCPUInfo, exception.UnableToMigrateToSelf, exception.DestinationHypervisorTooOld, exception.InvalidLocalStorage, exception.InvalidSharedStorage, exception.HypervisorUnavailable, exception.MigrationPreCheckError, exception.LiveMigrationWithOldNovaNotSafe) as ex: raise exc.HTTPBadRequest(explanation=ex.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'os-migrateLive', id)
def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" az=body.get('migrate') context = req.environ['nova.context'] authorize(context, 'migrate') instance = common.get_instance(self.compute_api, context, id, want_objects=True) if az is not None: availability_zone = instance.availability_zone if az == availability_zone: msg = _("The target azone can't be the same one.") raise exc.HTTPBadRequest(explanation=msg) migrateThread = MigrateThread(context,instance,az) migrateThread.start() else: try: self.compute_api.resize(req.environ['nova.context'], instance) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'migrate') except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.NoValidHost as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except Exception as e: LOG.exception(_LE("Error in migrate %s"), e) raise exc.HTTPBadRequest() return webob.Response(status_int=202)
def _update_instance_metadata(self, context, server_id, metadata, delete=False): try: server = self.compute_api.get(context, server_id) return self.compute_api.update_instance_metadata(context, server, metadata, delete) except exception.InstanceNotFound: msg = _('Server does not exist') raise exc.HTTPNotFound(explanation=msg) except exception.InvalidMetadata as error: raise exc.HTTPBadRequest(explanation=error.format_message()) except exception.InvalidMetadataSize as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message()) except exception.QuotaError as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message(), headers={'Retry-After': 0}) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'update metadata')
def _unshelve(self, req, id, body): """Restore an instance from shelved mode.""" context = req.environ["nova.context"] context.can(shelve_policies.POLICY_ROOT % 'unshelve') instance = common.get_instance(self.compute_api, context, id) new_az = None unshelve_dict = body['unshelve'] support_az = api_version_request.is_supported(req, '2.77') if support_az and unshelve_dict: new_az = unshelve_dict['availability_zone'] # We could potentially move this check to conductor and avoid the # extra API call to neutron when we support move operations with ports # having resource requests. if (instance.vm_state == vm_states.SHELVED_OFFLOADED and common.instance_has_port_with_resource_request( instance.uuid, self.network_api) and not common.supports_port_resource_request_during_move(req)): msg = _("The unshelve action on a server with ports having " "resource requests, like a port with a QoS minimum " "bandwidth policy, is not supported with this " "microversion") raise exc.HTTPBadRequest(explanation=msg) try: self.compute_api.unshelve(context, instance, new_az=new_az) except (exception.InstanceIsLocked, exception.UnshelveInstanceInvalidState, exception.MismatchVolumeAZException) as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'unshelve', id) except exception.InvalidRequest as e: raise exc.HTTPBadRequest(explanation=e.format_message())
def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host.""" context = req.environ['nova.context'] authorize(context, 'migrate') instance = common.get_instance(self.compute_api, context, id, want_objects=True) try: self.compute_api.resize(req.environ['nova.context'], instance) except exception.TooManyInstances as e: raise exc.HTTPRequestEntityTooLarge(explanation=e.format_message()) except exception.QuotaError as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message(), headers={'Retry-After': 0}) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'migrate') except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) return webob.Response(status_int=202)
def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" context = req.environ["nova.context"] context.can(server_policies.SERVERS % 'resize') instance = self._get_server(context, req, instance_id) try: self.compute_api.resize(context, instance, flavor_id, **kwargs) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.QuotaError as error: raise exc.HTTPForbidden( explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'resize', instance_id) except exception.ImageNotAuthorized: msg = _("You are not authorized to access the image " "the instance was started with.") raise exc.HTTPUnauthorized(explanation=msg) except exception.ImageNotFound: msg = _("Image that the instance was started " "with could not be found.") raise exc.HTTPBadRequest(explanation=msg) except (exception.AutoDiskConfigDisabledByImage, exception.CannotResizeDisk, exception.CannotResizeToSameFlavor, exception.FlavorNotFound, exception.NoValidHost, exception.PciRequestAliasNotDefined) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.Invalid: msg = _("Invalid instance image.") raise exc.HTTPBadRequest(explanation=msg)
def delete(self, req, server_id, id): """Detach an interface from an instance.""" context = req.environ['nova.context'] context.can(ai_policies.BASE_POLICY_NAME) context.can(ai_policies.POLICY_ROOT % 'delete') port_id = id instance = common.get_instance(self.compute_api, context, server_id, expected_attrs=['device_metadata']) try: self.compute_api.detach_interface(context, instance, port_id=port_id) except exception.PortNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except NotImplementedError: common.raise_feature_not_supported() except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'detach_interface', server_id)
def delete(self, req, server_id, id): """Detach an interface from an instance.""" context = req.environ['nova.context'] authorize(context) port_id = id instance = common.get_instance(self.compute_api, context, server_id, want_objects=True) LOG.audit(_("Detach interface %s"), port_id, instance=instance) try: self.compute_api.detach_interface(context, instance, port_id=port_id) except exception.PortNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except NotImplementedError: msg = _("Network driver does not support this function.") raise webob.exc.HTTPNotImplemented(explanation=msg) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'detach_interface', server_id) return webob.Response(status_int=202)
def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" context = req.environ["nova.context"] instance = self._get_server(context, req, instance_id) try: self.compute_api.resize(context, instance, flavor_id, **kwargs) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message(), headers={'Retry-After': 0}) except exception.FlavorNotFound: msg = _("Unable to locate requested flavor.") raise exc.HTTPBadRequest(explanation=msg) except exception.CannotResizeToSameFlavor: msg = _("Resize requires a flavor change.") raise exc.HTTPBadRequest(explanation=msg) except exception.CannotResizeDisk as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'resize') except exception.ImageNotAuthorized: msg = _("You are not authorized to access the image " "the instance was started with.") raise exc.HTTPUnauthorized(explanation=msg) except exception.ImageNotFound: msg = _("Image that the instance was started " "with could not be found.") raise exc.HTTPBadRequest(explanation=msg) except exception.Invalid: msg = _("Invalid instance image.") raise exc.HTTPBadRequest(explanation=msg) return webob.Response(status_int=202)
def _action_reboot(self, req, id, body): if 'reboot' in body and 'type' in body['reboot']: valid_reboot_types = ['HARD', 'SOFT'] reboot_type = body['reboot']['type'].upper() if not valid_reboot_types.count(reboot_type): msg = _("Argument 'type' for reboot is not HARD or SOFT") LOG.exception(msg) raise exc.HTTPBadRequest(explanation=msg) else: msg = _("Missing argument 'type' for reboot") LOG.exception(msg) raise exc.HTTPBadRequest(explanation=msg) context = req.environ['nova.context'] instance = self._get_server(context, id) try: self.compute_api.reboot(context, instance, reboot_type) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'reboot') except Exception, e: LOG.exception(_("Error in reboot %s"), e) raise exc.HTTPUnprocessableEntity()
def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host.""" context = req.environ["nova.context"] authorize(context, 'migrate_live') block_migration = body["os-migrateLive"]["block_migration"] disk_over_commit = body["os-migrateLive"]["disk_over_commit"] host = body["os-migrateLive"]["host"] block_migration = strutils.bool_from_string(block_migration, strict=True) disk_over_commit = strutils.bool_from_string(disk_over_commit, strict=True) try: instance = common.get_instance(self.compute_api, context, id, want_objects=True) self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host) except (exception.NoValidHost, exception.ComputeServiceUnavailable, exception.InvalidHypervisorType, exception.InvalidCPUInfo, exception.UnableToMigrateToSelf, exception.DestinationHypervisorTooOld, exception.InvalidLocalStorage, exception.InvalidSharedStorage, exception.HypervisorUnavailable, exception.InstanceNotRunning, exception.MigrationPreCheckError, exception.LiveMigrationWithOldNovaNotSafe) as ex: raise exc.HTTPBadRequest(explanation=ex.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'os-migrateLive') return webob.Response(status_int=202)
def delete(self, req, server_id, id): """Deletes an existing metadata.""" context = req.environ['nova.context'] metadata = self._get_metadata(context, server_id) if id not in metadata: msg = _("Metadata item was not found") raise exc.HTTPNotFound(explanation=msg) server = common.get_instance(self.compute_api, context, server_id) try: self.compute_api.delete_instance_metadata(context, server, id) except exception.InstanceNotFound: msg = _('Server does not exist') raise exc.HTTPNotFound(explanation=msg) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'delete metadata', server_id)
def _rescue(self, req, id, body): """Rescue an instance.""" context = req.environ["nova.context"] authorize(context) if body['rescue'] and 'adminPass' in body['rescue']: password = body['rescue']['adminPass'] else: password = utils.generate_password() instance = self._get_instance(context, id, want_objects=True) try: self.compute_api.rescue(context, instance, rescue_password=password) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'rescue') except exception.InvalidVolume as volume_error: raise exc.HTTPConflict(explanation=volume_error.format_message()) except exception.InstanceNotRescuable as non_rescuable: raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) return {'adminPass': password}
def _update_instance_metadata(self, context, server_id, metadata, delete=False): try: server = self.compute_api.get(context, server_id, want_objects=True) return self.compute_api.update_instance_metadata( context, server, metadata, delete) except exception.InstanceNotFound: msg = _('Server does not exist') raise exc.HTTPNotFound(explanation=msg) except (ValueError, AttributeError): msg = _("Malformed request body") raise exc.HTTPBadRequest(explanation=msg) except exception.InvalidMetadata as error: raise exc.HTTPBadRequest(explanation=error.format_message()) except exception.InvalidMetadataSize as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message()) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'update metadata', server_id)
def change_password(self, req, id, body): context = req.environ['nova.context'] authorize(context) password = body['changePassword']['adminPass'] instance = common.get_instance(self.compute_api, context, id) try: self.compute_api.set_admin_password(context, instance, password) except exception.InstancePasswordSetFailed as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as e: raise common.raise_http_conflict_for_instance_invalid_state( e, 'changePassword', id) except NotImplementedError: msg = _("Unable to set password on instance") common.raise_feature_not_supported(msg=msg)
def change_password(self, req, id, body): context = req.environ['nova.context'] authorize(context) password = body['change_password']['admin_password'] try: instance = self.compute_api.get(context, id, want_objects=True) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) try: self.compute_api.set_admin_password(context, instance, password) except exception.InstancePasswordSetFailed as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as e: raise common.raise_http_conflict_for_instance_invalid_state( e, 'change_password') except NotImplementedError: msg = _("Unable to set password on instance") raise exc.HTTPNotImplemented(explanation=msg)
def change_password(self, req, id, body): context = req.environ['nova.context'] instance = common.get_instance(self.compute_api, context, id) context.can(ap_policies.BASE_POLICY_NAME, target={'project_id': instance.project_id}) password = body['changePassword']['adminPass'] try: self.compute_api.set_admin_password(context, instance, password) except (exception.InstancePasswordSetFailed, exception.SetAdminPasswdNotSupported, exception.InstanceAgentNotEnabled) as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as e: raise common.raise_http_conflict_for_instance_invalid_state( e, 'changePassword', id) except NotImplementedError: msg = _("Unable to set password on instance") common.raise_feature_not_supported(msg=msg)
def _action_change_password(self, req, id, body): context = req.environ['nova.context'] if (not body.get('changePassword') or 'adminPass' not in body['changePassword']): msg = _("No adminPass was specified") raise exc.HTTPBadRequest(explanation=msg) password = self._get_server_admin_password(body['changePassword']) server = self._get_server(context, req, id) try: self.compute_api.set_admin_password(context, server, password) except exception.InstancePasswordSetFailed as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as e: raise common.raise_http_conflict_for_instance_invalid_state( e, 'changePassword', id) except NotImplementedError: msg = _("Unable to set password on instance") raise exc.HTTPNotImplemented(explanation=msg) return webob.Response(status_int=202)
def change_password(self, req, id, body): context = req.environ['nova.context'] authorize(context) if 'admin_password' not in body['change_password']: msg = _("No admin_password was specified") raise exc.HTTPBadRequest(explanation=msg) password = body['change_password']['admin_password'] if not isinstance(password, basestring): msg = _("Invalid admin password") raise exc.HTTPBadRequest(explanation=msg) try: instance = self.compute_api.get(context, id) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) try: self.compute_api.set_admin_password(context, instance, password) except exception.InstancePasswordSetFailed as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as e: raise common.raise_http_conflict_for_instance_invalid_state( e, 'change_password') except NotImplementedError: msg = _("Unable to set password on instance") raise exc.HTTPNotImplemented(explanation=msg)
def _action_rebuild(self, req, id, body): """Rebuild an instance with the given attributes""" try: body = body['rebuild'] except (KeyError, TypeError): raise exc.HTTPBadRequest(_("Invalid request body")) try: image_href = body["imageRef"] except (KeyError, TypeError): msg = _("Could not parse imageRef from request.") raise exc.HTTPBadRequest(explanation=msg) image_href = self._image_uuid_from_href(image_href) try: password = body['adminPass'] except (KeyError, TypeError): password = utils.generate_password(FLAGS.password_length) context = req.environ['nova.context'] instance = self._get_server(context, id) attr_map = { 'personality': 'files_to_inject', 'name': 'display_name', 'accessIPv4': 'access_ip_v4', 'accessIPv6': 'access_ip_v6', 'metadata': 'metadata', 'auto_disk_config': 'auto_disk_config', } if 'accessIPv4' in body: self._validate_access_ipv4(body['accessIPv4']) if 'accessIPv6' in body: self._validate_access_ipv6(body['accessIPv6']) kwargs = {} for request_attribute, instance_attribute in attr_map.items(): try: kwargs[instance_attribute] = body[request_attribute] except (KeyError, TypeError): pass self._validate_metadata(kwargs.get('metadata', {})) if 'files_to_inject' in kwargs: personality = kwargs['files_to_inject'] kwargs['files_to_inject'] = self._get_injected_files(personality) try: self.compute_api.rebuild(context, instance, image_href, password, **kwargs) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'rebuild') except exception.InstanceNotFound: msg = _("Instance could not be found") raise exc.HTTPNotFound(explanation=msg) except exception.ImageNotFound: msg = _("Cannot find image for rebuild") raise exc.HTTPBadRequest(explanation=msg) instance = self._get_server(context, id) self._add_instance_faults(context, [instance]) view = self._view_builder.show(req, instance) # Add on the adminPass attribute since the view doesn't do it # unless instance passwords are disabled if FLAGS.enable_instance_password: view['server']['adminPass'] = password robj = wsgi.ResponseObject(view) return self._add_location(robj)
def _evacuate(self, req, id, body): """Permit admins to evacuate a server from a failed host to a new one. If host is empty, the scheduler will select one. """ context = req.environ["nova.context"] authorize(context) if not self.is_valid_body(body, "evacuate"): raise exc.HTTPBadRequest(_("Malformed request body")) evacuate_body = body["evacuate"] host = evacuate_body.get("host") if (not host and not self.ext_mgr.is_loaded('os-extended-evacuate-find-host')): msg = _("host must be specified.") raise exc.HTTPBadRequest(explanation=msg) try: on_shared_storage = strutils.bool_from_string( evacuate_body["onSharedStorage"]) except (TypeError, KeyError): msg = _("onSharedStorage must be specified.") raise exc.HTTPBadRequest(explanation=msg) password = None if 'adminPass' in evacuate_body: # check that if requested to evacuate server on shared storage # password not specified if on_shared_storage: msg = _("admin password can't be changed on existing disk") raise exc.HTTPBadRequest(explanation=msg) password = evacuate_body['adminPass'] elif not on_shared_storage: password = utils.generate_password() if host is not None: try: self.host_api.service_get_by_compute_host(context, host) except exception.NotFound: msg = _("Compute host %s not found.") % host raise exc.HTTPNotFound(explanation=msg) instance = common.get_instance(self.compute_api, context, id, want_objects=True) try: if instance.host == host: msg = _("The target host can't be the same one.") raise exc.HTTPBadRequest(explanation=msg) self.compute_api.evacuate(context, instance, host, on_shared_storage, password) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'evacuate', id) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.ComputeServiceInUse as e: raise exc.HTTPBadRequest(explanation=e.format_message()) if password: return {'adminPass': password}
def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host.""" context = req.environ["nova.context"] context.can(ms_policies.POLICY_ROOT % 'migrate_live') host = body["os-migrateLive"]["host"] block_migration = body["os-migrateLive"]["block_migration"] force = None async_ = api_version_request.is_supported(req, min_version='2.34') if api_version_request.is_supported(req, min_version='2.30'): force = self._get_force_param_for_live_migration(body, host) if api_version_request.is_supported(req, min_version='2.25'): if block_migration == 'auto': block_migration = None else: block_migration = strutils.bool_from_string(block_migration, strict=True) disk_over_commit = None else: disk_over_commit = body["os-migrateLive"]["disk_over_commit"] block_migration = strutils.bool_from_string(block_migration, strict=True) disk_over_commit = strutils.bool_from_string(disk_over_commit, strict=True) # NOTE(stephenfin): we need 'numa_topology' because of the # 'LiveMigrationTask._check_instance_has_no_numa' check in the # conductor instance = common.get_instance(self.compute_api, context, id, expected_attrs=['numa_topology']) # We could potentially move this check to conductor and avoid the # extra API call to neutron when we support move operations with ports # having resource requests. if (common.instance_has_port_with_resource_request( context, instance.uuid, self.network_api) and not common.supports_port_resource_request_during_move(req)): msg = _("The os-migrateLive action on a server with ports having " "resource requests, like a port with a QoS minimum " "bandwidth policy, is not supported with this " "microversion") raise exc.HTTPBadRequest(explanation=msg) try: self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host, force, async_) except (exception.NoValidHost, exception.ComputeServiceUnavailable, exception.InvalidHypervisorType, exception.InvalidCPUInfo, exception.UnableToMigrateToSelf, exception.DestinationHypervisorTooOld, exception.InvalidLocalStorage, exception.InvalidSharedStorage, exception.HypervisorUnavailable, exception.MigrationPreCheckError) as ex: if async_: with excutils.save_and_reraise_exception(): LOG.error( "Unexpected exception received from " "conductor during pre-live-migration checks " "'%(ex)s'", {'ex': ex}) else: raise exc.HTTPBadRequest(explanation=ex.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.ComputeHostNotFound as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'os-migrateLive', id)
def _action_rebuild(self, req, id, body): """Rebuild an instance with the given attributes.""" body = body['rebuild'] try: image_href = body["imageRef"] except (KeyError, TypeError): msg = _("Could not parse imageRef from request.") raise exc.HTTPBadRequest(explanation=msg) image_href = self._image_uuid_from_href(image_href) password = self._get_server_admin_password(body) context = req.environ['nova.context'] instance = self._get_server(context, req, id) attr_map = { 'personality': 'files_to_inject', 'name': 'display_name', 'accessIPv4': 'access_ip_v4', 'accessIPv6': 'access_ip_v6', 'metadata': 'metadata', 'auto_disk_config': 'auto_disk_config', } kwargs = {} # take the preserve_ephemeral value into account only when the # corresponding extension is active if (self.ext_mgr.is_loaded('os-preserve-ephemeral-rebuild') and 'preserve_ephemeral' in body): kwargs['preserve_ephemeral'] = strutils.bool_from_string( body['preserve_ephemeral'], strict=True) if 'accessIPv4' in body: self._validate_access_ipv4(body['accessIPv4']) if 'accessIPv6' in body: self._validate_access_ipv6(body['accessIPv6']) if 'name' in body: self._validate_server_name(body['name']) for request_attribute, instance_attribute in attr_map.items(): try: kwargs[instance_attribute] = body[request_attribute] except (KeyError, TypeError): pass self._validate_metadata(kwargs.get('metadata', {})) if 'files_to_inject' in kwargs: personality = kwargs.pop('files_to_inject') files_to_inject = self._get_injected_files(personality) else: files_to_inject = None try: self.compute_api.rebuild(context, instance, image_href, password, files_to_inject=files_to_inject, **kwargs) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'rebuild', id) except exception.InstanceNotFound: msg = _("Instance could not be found") raise exc.HTTPNotFound(explanation=msg) except exception.InvalidMetadataSize as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message()) except exception.ImageNotFound: msg = _("Cannot find image for rebuild") raise exc.HTTPBadRequest(explanation=msg) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except (exception.ImageNotActive, exception.FlavorDiskTooSmall, exception.FlavorMemoryTooSmall, exception.InvalidMetadata, exception.AutoDiskConfigDisabledByImage) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) instance = self._get_server(context, req, id) view = self._view_builder.show(req, instance) # Add on the adminPass attribute since the view doesn't do it # unless instance passwords are disabled if CONF.enable_instance_password: view['server']['adminPass'] = password robj = wsgi.ResponseObject(view) return self._add_location(robj)
def create(self, req, server_id, body): """Attach an interface to an instance.""" context = req.environ['nova.context'] instance = common.get_instance(self.compute_api, context, server_id) context.can(ai_policies.POLICY_ROOT % 'create', target={'project_id': instance.project_id}) network_id = None port_id = None req_ip = None tag = None if body: attachment = body['interfaceAttachment'] network_id = attachment.get('net_id', None) port_id = attachment.get('port_id', None) tag = attachment.get('tag', None) try: req_ip = attachment['fixed_ips'][0]['ip_address'] except Exception: pass if network_id and port_id: msg = _("Must not input both network_id and port_id") raise exc.HTTPBadRequest(explanation=msg) if req_ip and not network_id: msg = _("Must input network_id when request IP address") raise exc.HTTPBadRequest(explanation=msg) try: vif = self.compute_api.attach_interface(context, instance, network_id, port_id, req_ip, tag=tag) except (exception.InterfaceAttachFailedNoNetwork, exception.NetworkAmbiguous, exception.NoMoreFixedIps, exception.PortNotUsable, exception.AttachInterfaceNotSupported, exception.SecurityGroupCannotBeApplied, exception.NetworkInterfaceTaggedAttachNotSupported, exception.NetworksWithQoSPolicyNotSupported, exception.InterfaceAttachPciClaimFailed, exception.InterfaceAttachResourceAllocationFailed, exception.ForbiddenPortsWithAccelerator, exception.ExtendedResourceRequestOldCompute, ) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except ( exception.InstanceIsLocked, exception.FixedIpAlreadyInUse, exception.PortInUse, ) as e: raise exc.HTTPConflict(explanation=e.format_message()) except (exception.PortNotFound, exception.NetworkNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.PortLimitExceeded as e: raise exc.HTTPForbidden(explanation=e.format_message()) except exception.InterfaceAttachFailed as e: raise webob.exc.HTTPInternalServerError( explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'attach_interface', server_id) return self.show(req, server_id, vif['id'])
def _action_rebuild(self, req, id, body): """Rebuild an instance with the given attributes.""" rebuild_dict = body['rebuild'] image_href = rebuild_dict["imageRef"] image_href = self._image_uuid_from_href(image_href) password = self._get_server_admin_password(rebuild_dict) context = req.environ['nova.context'] instance = self._get_server(context, req, id) attr_map = { 'name': 'display_name', 'metadata': 'metadata', } rebuild_kwargs = {} if list(self.rebuild_extension_manager): self.rebuild_extension_manager.map(self._rebuild_extension_point, rebuild_dict, rebuild_kwargs) for request_attribute, instance_attribute in attr_map.items(): try: rebuild_kwargs[instance_attribute] = rebuild_dict[ request_attribute] except (KeyError, TypeError): pass try: self.compute_api.rebuild(context, instance, image_href, password, **rebuild_kwargs) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'rebuild', id) except exception.InstanceNotFound: msg = _("Instance could not be found") raise exc.HTTPNotFound(explanation=msg) except exception.ImageNotFound: msg = _("Cannot find image for rebuild") raise exc.HTTPBadRequest(explanation=msg) except exception.QuotaError as error: raise exc.HTTPForbidden(explanation=error.format_message()) except (exception.ImageNotActive, exception.FlavorDiskTooSmall, exception.FlavorMemoryTooSmall, exception.InvalidMetadata, exception.AutoDiskConfigDisabledByImage) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) instance = self._get_server(context, req, id) view = self._view_builder.show(req, instance) # Add on the admin_password attribute since the view doesn't do it # unless instance passwords are disabled if CONF.enable_instance_password: view['server']['adminPass'] = password robj = wsgi.ResponseObject(view) return self._add_location(robj)
def _migrate_extend(self, req, id, body): """Permit admin to migrate a server to a new host lively.""" if not self._get_ics_session(): return dict(vms=[], error='CANNOT_CONNECT_ICS') if not id: return dict(vms=[], error='VM_ID_NULL') try: vm_info = self.ics_manager.vm.get_info(id) except exception as e: return { "success": False, "message": "Vm is not found and get vm_info has errors" } nodeTarget = body["migrateExtend"]["nodeTarget"] if vm_info: if vm_info['migratable'] == False: return { 'success': False, 'code': '300089', "message": 'The vm is not allowed to migrate' } if vm_info['hostId'] == nodeTarget: return { 'success': False, 'code': '300090', "message": 'The hostId which vm belongs is same as the target_hostId, Please check' } context = req.environ['nova.context'] # if not hostInfo or hostInfo['id']: # return {'success': False, "vmId": id, "hostId": nodeTarget, "result": "HOST IS NULL"} # instance = common.get_instance(self.compute_api, context, id) hostInfo = None try: # make sure the target host is exist or not hostInfo = self.ics_manager.host.get_host(nodeTarget) if hostInfo.get('code'): return { 'success': False, "vmId": id, "hostId": nodeTarget, "result": hostInfo['message'] } # do the migration result = self.ics_manager.vm.live_migrate(id, nodeTarget) res = { 'success': True, "vmId": id, "hostId": nodeTarget, "result": result } return dict(migrateExtend=res) # body = self.ics_manager.vm.get_info(self, '0557ec0b_4e52_4228_a067_e6a7699ebe97') # result = self.ics_manager.host.get_host('5d85f39c-ef38-4bc3-991a-6f250b32221f') # print json.dumps(result) + "\n\n" except (exception.TooManyInstances, exception.QuotaError) as e: raise exc.HTTPForbidden(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'migrateExtend', id) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.NoValidHost as e: raise exc.HTTPBadRequest(explanation=e.format_message())
def _evacuate(self, req, id, body): """Permit admins to evacuate a server from a failed host to a new one. """ context = req.environ["nova.context"] instance = common.get_instance(self.compute_api, context, id) context.can(evac_policies.BASE_POLICY_NAME, target={ 'user_id': instance.user_id, 'project_id': instance.project_id }) evacuate_body = body["evacuate"] host = evacuate_body.get("host") force = None on_shared_storage = self._get_on_shared_storage(req, evacuate_body) if api_version_request.is_supported(req, min_version='2.29'): force = body["evacuate"].get("force", False) force = strutils.bool_from_string(force, strict=True) if force is True and not host: message = _("Can't force to a non-provided destination") raise exc.HTTPBadRequest(explanation=message) if api_version_request.is_supported(req, min_version='2.14'): password = self._get_password_v214(req, evacuate_body) else: password = self._get_password(req, evacuate_body, on_shared_storage) if host is not None: try: self.host_api.service_get_by_compute_host(context, host) except (exception.ComputeHostNotFound, exception.HostMappingNotFound): msg = _("Compute host %s not found.") % host raise exc.HTTPNotFound(explanation=msg) if instance.host == host: msg = _("The target host can't be the same one.") raise exc.HTTPBadRequest(explanation=msg) if (common.instance_has_port_with_resource_request( context, instance.uuid, self.network_api) and not common.supports_port_resource_request_during_move(req)): msg = _("The evacuate server operation with port having QoS " "policy is not supported.") raise exc.HTTPBadRequest(explanation=msg) try: self.compute_api.evacuate(context, instance, host, on_shared_storage, password, force) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'evacuate', id) except exception.ComputeServiceInUse as e: raise exc.HTTPBadRequest(explanation=e.format_message()) if (not api_version_request.is_supported(req, min_version='2.14') and CONF.api.enable_instance_password): return {'adminPass': password} else: return None
def _evacuate(self, req, id, body): """Permit admins to evacuate a server from a failed host to a new one. """ context = req.environ["nova.context"] instance = common.get_instance(self.compute_api, context, id) context.can(evac_policies.BASE_POLICY_NAME, target={'user_id': instance.user_id, 'project_id': instance.project_id}) evacuate_body = body["evacuate"] host = evacuate_body.get("host") force = None on_shared_storage = self._get_on_shared_storage(req, evacuate_body) if api_version_request.is_supported(req, min_version='2.29'): force = body["evacuate"].get("force", False) force = strutils.bool_from_string(force, strict=True) if force is True and not host: message = _("Can't force to a non-provided destination") raise exc.HTTPBadRequest(explanation=message) if api_version_request.is_supported(req, min_version='2.14'): password = self._get_password_v214(req, evacuate_body) else: password = self._get_password(req, evacuate_body, on_shared_storage) if host is not None: try: self.host_api.service_get_by_compute_host(context, host) except (exception.ComputeHostNotFound, exception.HostMappingNotFound): msg = _("Compute host %s not found.") % host raise exc.HTTPNotFound(explanation=msg) if instance.host == host: msg = _("The target host can't be the same one.") raise exc.HTTPBadRequest(explanation=msg) # We could potentially move this check to conductor and avoid the # extra API call to neutron when we support move operations with ports # having resource requests. if (common.instance_has_port_with_resource_request( instance.uuid, self.network_api) and not common.supports_port_resource_request_during_move()): LOG.warning("The evacuate action on a server with ports " "having resource requests, like a port with a QoS " "minimum bandwidth policy, is not supported until " "every nova-compute is upgraded to Ussuri") msg = _("The evacuate action on a server with ports having " "resource requests, like a port with a QoS minimum " "bandwidth policy, is not supported by this cluster right " "now") raise exc.HTTPBadRequest(explanation=msg) try: self.compute_api.evacuate(context, instance, host, on_shared_storage, password, force) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'evacuate', id) except exception.ComputeServiceInUse as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.ForbiddenWithAccelerators as e: raise exc.HTTPForbidden(explanation=e.format_message()) except exception.OperationNotSupportedForVTPM as e: raise exc.HTTPConflict(explanation=e.format_message()) if (not api_version_request.is_supported(req, min_version='2.14') and CONF.api.enable_instance_password): return {'adminPass': password} else: return None
state='fake_state', method='fake_method') try: common.raise_http_conflict_for_instance_invalid_state(exc, 'meow') except Exception, e: self.assertTrue(isinstance(e, webob.exc.HTTPConflict)) msg = str(e) self.assertEqual( msg, "Cannot 'meow' while instance is in fake_attr fake_state") else: self.fail("webob.exc.HTTPConflict was not raised") # Incorrect args exc = exception.InstanceInvalidState() try: common.raise_http_conflict_for_instance_invalid_state(exc, 'meow') except Exception, e: self.assertTrue(isinstance(e, webob.exc.HTTPConflict)) msg = str(e) self.assertEqual(msg, "Instance is in an invalid state for 'meow'") else: self.fail("webob.exc.HTTPConflict was not raised") def test_check_img_metadata_properties_quota_valid_metadata(self): ctxt = test_utils.get_test_admin_context() metadata1 = {"key": "value"} actual = common.check_img_metadata_properties_quota(ctxt, metadata1) self.assertEqual(actual, None) metadata2 = {"key": "v" * 260} actual = common.check_img_metadata_properties_quota(ctxt, metadata2)
def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host.""" context = req.environ["nova.context"] # NOTE(stephenfin): we need 'numa_topology' because of the # 'LiveMigrationTask._check_instance_has_no_numa' check in the # conductor instance = common.get_instance(self.compute_api, context, id, expected_attrs=['numa_topology']) context.can(ms_policies.POLICY_ROOT % 'migrate_live', target={'project_id': instance.project_id}) host = body["os-migrateLive"]["host"] block_migration = body["os-migrateLive"]["block_migration"] force = None async_ = api_version_request.is_supported(req, min_version='2.34') if api_version_request.is_supported(req, min_version='2.30'): force = self._get_force_param_for_live_migration(body, host) if api_version_request.is_supported(req, min_version='2.25'): if block_migration == 'auto': block_migration = None else: block_migration = strutils.bool_from_string(block_migration, strict=True) disk_over_commit = None else: disk_over_commit = body["os-migrateLive"]["disk_over_commit"] block_migration = strutils.bool_from_string(block_migration, strict=True) disk_over_commit = strutils.bool_from_string(disk_over_commit, strict=True) try: self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host, force, async_) except (exception.NoValidHost, exception.ComputeServiceUnavailable, exception.InvalidHypervisorType, exception.InvalidCPUInfo, exception.UnableToMigrateToSelf, exception.DestinationHypervisorTooOld, exception.InvalidLocalStorage, exception.InvalidSharedStorage, exception.HypervisorUnavailable, exception.MigrationPreCheckError, exception.ForbiddenPortsWithAccelerator) as ex: if async_: with excutils.save_and_reraise_exception(): LOG.error( "Unexpected exception received from " "conductor during pre-live-migration checks " "'%(ex)s'", {'ex': ex}) else: raise exc.HTTPBadRequest(explanation=ex.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except ( exception.ComputeHostNotFound, exception.ExtendedResourceRequestOldCompute, ) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'os-migrateLive', id)
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 = int(rotation) except ValueError: msg = _("createBackup attribute 'rotation' must be an integer") raise exc.HTTPBadRequest(explanation=msg) if rotation < 0: msg = _("createBackup attribute 'rotation' must be greater " "than or equal to zero") raise exc.HTTPBadRequest(explanation=msg) 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) try: instance = self.compute_api.get(context, id, want_objects=True) except exception.NotFound: msg = _("Instance not found") raise exc.HTTPNotFound(explanation=msg) 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') 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 _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] entity = body.get("createImage", {}) image_name = entity.get("name") if not image_name: msg = _("createImage entity requires name attribute") raise exc.HTTPBadRequest(explanation=msg) 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 = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) try: if self.compute_api.is_volume_backed_instance( context, instance, bdms): img = instance.image_ref if not img: properties = bdms.root_metadata( context, self.compute_api.image_api, self.compute_api.volume_api) image_meta = {'properties': properties} else: image_meta = self.compute_api.image_api.get(context, img) image = self.compute_api.snapshot_volume_backed( context, instance, image_meta, image_name, extra_properties=props) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=props) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'createImage', id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image['id']) url_prefix = self._view_builder._update_glance_link_prefix( req.application_url) image_ref = os.path.join(url_prefix, context.project_id, 'images', image_id) resp = webob.Response(status_int=202) resp.headers['Location'] = image_ref return resp
def detach(self, req, id, body): server_id = id context = req.environ['nova.context'] authorize_detach(context) if not self.is_valid_body(body, 'detach'): raise exc.HTTPBadRequest(_("The request body invalid")) try: volume_id = body['detach']['volume_id'] except KeyError: raise exc.HTTPBadRequest( _("Could not find volume_id from request" "parameter")) self._validate_volume_id(volume_id) LOG.audit( _("Detach volume %(volume_id)s from " "instance %(server_id)s"), { "volume_id": volume_id, "server_id": id, "context": context }) try: instance = self.compute_api.get(context, server_id) except exception.InstanceNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) try: volume = self.volume_api.get(context, volume_id) except exception.VolumeNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) bdms = self.compute_api.get_instance_bdms(context, instance) if not bdms: msg = _("Volume %(volume_id)s is not attached to the " "instance %(server_id)s") % { 'server_id': server_id, 'volume_id': volume_id } LOG.debug(msg) raise exc.HTTPNotFound(explanation=msg) for bdm in bdms: if bdm['volume_id'] != volume_id: continue try: self.compute_api.detach_volume(context, instance, volume) break except exception.VolumeUnattached: # The volume is not attached. Treat it as NotFound # by falling through. pass except exception.InvalidVolume as e: raise exc.HTTPBadRequest(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'detach_volume') else: msg = _("Volume %(volume_id)s is not attached to the " "instance %(server_id)s") % { 'server_id': server_id, 'volume_id': volume_id } raise exc.HTTPNotFound(explanation=msg)