diff options
Diffstat (limited to 'nova/compute')
| -rw-r--r-- | nova/compute/api.py | 28 | ||||
| -rw-r--r-- | nova/compute/instance_types.py | 12 | ||||
| -rw-r--r-- | nova/compute/manager.py | 87 | ||||
| -rw-r--r-- | nova/compute/power_state.py | 21 |
4 files changed, 106 insertions, 42 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py index c85f0f53a..be26d8ca3 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -32,6 +32,7 @@ from nova import rpc from nova import utils from nova import volume from nova.compute import instance_types +from nova.compute import power_state from nova.scheduler import api as scheduler_api from nova.db import base @@ -501,6 +502,33 @@ class API(base.Base): """Reboot the given instance.""" self._cast_compute_message('reboot_instance', context, instance_id) + def rebuild(self, context, instance_id, image_id, metadata=None, + files_to_inject=None): + """Rebuild the given instance with the provided metadata.""" + instance = db.api.instance_get(context, instance_id) + + if instance["state"] == power_state.BUILDING: + msg = _("Instance already building") + raise exception.BuildInProgress(msg) + + metadata = metadata or {} + self._check_metadata_properties_quota(context, metadata) + + files_to_inject = files_to_inject or [] + self._check_injected_file_quota(context, files_to_inject) + + self.db.instance_update(context, instance_id, {"metadata": metadata}) + + rebuild_params = { + "image_id": image_id, + "injected_files": files_to_inject, + } + + self._cast_compute_message('rebuild_instance', + context, + instance_id, + params=rebuild_params) + def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process.""" context = context.elevated() diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 7e7198b96..1275a6fdd 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -37,11 +37,11 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, try: int(option) except ValueError: - raise exception.InvalidInputException( - _("create arguments must be positive integers")) + raise exception.InvalidInput(reason=_("create arguments must " + "be positive integers")) if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): - raise exception.InvalidInputException( - _("create arguments must be positive integers")) + raise exception.InvalidInput(reason=_("create arguments must " + "be positive integers")) try: db.instance_type_create( @@ -64,7 +64,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, def destroy(name): """Marks instance types as deleted.""" if name is None: - raise exception.InvalidInputException(_("No instance type specified")) + raise exception.InvalidInstanceType(instance_type=name) else: try: db.instance_type_destroy(context.get_admin_context(), name) @@ -76,7 +76,7 @@ def destroy(name): def purge(name): """Removes instance types from database.""" if name is None: - raise exception.InvalidInputException(_("No instance type specified")) + raise exception.InvalidInstanceType(instance_type=name) else: try: db.instance_type_purge(context.get_admin_context(), name) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 93eb547ba..e698b0255 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -137,17 +137,33 @@ class ComputeManager(manager.SchedulerDependentManager): """Initialization for a standalone compute service.""" self.driver.init_host(host=self.host) - def _update_state(self, context, instance_id): + def _update_state(self, context, instance_id, state=None): """Update the state of an instance from the driver info.""" - # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) - try: - info = self.driver.get_info(instance_ref['name']) - state = info['state'] - except exception.NotFound: - state = power_state.FAILED + + if state is None: + try: + info = self.driver.get_info(instance_ref['name']) + except exception.NotFound: + info = None + + if info is not None: + state = info['state'] + else: + state = power_state.FAILED + self.db.instance_set_state(context, instance_id, state) + def _update_launched_at(self, context, instance_id, launched_at=None): + """Update the launched_at parameter of the given instance.""" + data = {'launched_at': launched_at or datetime.datetime.utcnow()} + self.db.instance_update(context, instance_id, data) + + def _update_image_id(self, context, instance_id, image_id): + """Update the image_id for the given instance.""" + data = {'image_id': image_id} + self.db.instance_update(context, instance_id, data) + def get_console_topic(self, context, **kwargs): """Retrieves the console host for a project on this host. @@ -231,24 +247,15 @@ class ComputeManager(manager.SchedulerDependentManager): instance_id) # TODO(vish) check to make sure the availability zone matches - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'spawning') + self._update_state(context, instance_id, power_state.BUILDING) try: self.driver.spawn(instance_ref) - now = datetime.datetime.utcnow() - self.db.instance_update(context, - instance_id, - {'launched_at': now}) - except Exception: # pylint: disable=W0702 - LOG.exception(_("Instance '%s' failed to spawn. Is virtualization" - " enabled in the BIOS?"), instance_id, - context=context) - self.db.instance_set_state(context, - instance_id, - power_state.SHUTDOWN) + except Exception as ex: # pylint: disable=W0702 + msg = _("Instance '%(instance_id)s' failed to spawn. Is " + "virtualization enabled in the BIOS? Details: " + "%(ex)s") % locals() + LOG.exception(msg) if not FLAGS.stub_network and FLAGS.auto_assign_floating_ip: public_ip = self.network_api.allocate_floating_ip(context) @@ -262,6 +269,8 @@ class ComputeManager(manager.SchedulerDependentManager): floating_ip, fixed_ip, affect_auto_assigned=True) + + self._update_launched_at(context, instance_id) self._update_state(context, instance_id) @exception.wrap_exception @@ -318,6 +327,33 @@ class ComputeManager(manager.SchedulerDependentManager): @exception.wrap_exception @checks_instance_lock + def rebuild_instance(self, context, instance_id, image_id): + """Destroy and re-make this instance. + + A 'rebuild' effectively purges all existing data from the system and + remakes the VM with given 'metadata' and 'personalities'. + + :param context: `nova.RequestContext` object + :param instance_id: Instance identifier (integer) + :param image_id: Image identifier (integer) + """ + context = context.elevated() + + instance_ref = self.db.instance_get(context, instance_id) + LOG.audit(_("Rebuilding instance %s"), instance_id, context=context) + + self._update_state(context, instance_id, power_state.BUILDING) + + self.driver.destroy(instance_ref) + instance_ref.image_id = image_id + self.driver.spawn(instance_ref) + + self._update_image_id(context, instance_id, image_id) + self._update_launched_at(context, instance_id) + self._update_state(context, instance_id) + + @exception.wrap_exception + @checks_instance_lock def reboot_instance(self, context, instance_id): """Reboot an instance on this host.""" context = context.elevated() @@ -1073,8 +1109,7 @@ class ComputeManager(manager.SchedulerDependentManager): if vm_instance is None: # NOTE(justinsb): We have to be very careful here, because a # concurrent operation could be in progress (e.g. a spawn) - if db_state == power_state.NOSTATE: - # Assume that NOSTATE => spawning + if db_state == power_state.BUILDING: # TODO(justinsb): This does mean that if we crash during a # spawn, the machine will never leave the spawning state, # but this is just the way nova is; this function isn't @@ -1105,9 +1140,7 @@ class ComputeManager(manager.SchedulerDependentManager): if vm_state != db_state: LOG.info(_("DB/VM state mismatch. Changing state from " "'%(db_state)s' to '%(vm_state)s'") % locals()) - self.db.instance_set_state(context, - db_instance['id'], - vm_state) + self._update_state(context, db_instance['id'], vm_state) # NOTE(justinsb): We no longer auto-remove SHUTOFF instances # It's quite hard to get them back when we do. diff --git a/nova/compute/power_state.py b/nova/compute/power_state.py index ef013b2ef..c468fe6b3 100644 --- a/nova/compute/power_state.py +++ b/nova/compute/power_state.py @@ -30,20 +30,23 @@ SHUTOFF = 0x05 CRASHED = 0x06 SUSPENDED = 0x07 FAILED = 0x08 +BUILDING = 0x09 # TODO(justinsb): Power state really needs to be a proper class, # so that we're not locked into the libvirt status codes and can put mapping # logic here rather than spread throughout the code _STATE_MAP = { - NOSTATE: 'pending', - RUNNING: 'running', - BLOCKED: 'blocked', - PAUSED: 'paused', - SHUTDOWN: 'shutdown', - SHUTOFF: 'shutdown', - CRASHED: 'crashed', - SUSPENDED: 'suspended', - FAILED: 'failed to spawn'} + NOSTATE: 'pending', + RUNNING: 'running', + BLOCKED: 'blocked', + PAUSED: 'paused', + SHUTDOWN: 'shutdown', + SHUTOFF: 'shutdown', + CRASHED: 'crashed', + SUSPENDED: 'suspended', + FAILED: 'failed to spawn', + BUILDING: 'building', +} def name(code): |
