Merge "vm state and task state management"

This commit is contained in:
Jenkins 2012-06-25 18:24:30 +00:00 committed by Gerrit Code Review
commit aa802e0204
19 changed files with 421 additions and 203 deletions

View File

@ -8,6 +8,143 @@ Preconditions for commands
The following diagrams show the required virtual machine (VM) states and
task states for various commands issued by the user:
.. graphviz::
digraph states {
node [fontsize=10 fontname="Monospace"]
/* states */
building [label="BUILDING"]
active [label="ACTIVE"]
paused [label="PAUSED"]
suspended [label="SUSPENDED"]
stopped [label="STOPPED"]
rescued [label="RESCUED"]
resized [label="RESIZED"]
soft_deleted [label="SOFT_DELETED"]
deleted [label="DELETED"]
error [label="ERROR"]
/* apis */
create [shape="rectangle"]
create -> active
create -> error
building -> create
delete [shape="rectangle"]
delete -> deleted
building -> delete
paused -> delete
suspended -> delete
stopped -> delete
rescued -> delete
soft_deleted -> delete
error -> delete
soft_delete [shape="rectangle"]
soft_delete -> soft_deleted
soft_delete -> error
active -> soft_delete
stopped -> soft_delete
restore [shape="rectangle"]
restore -> active
restore -> error
soft_deleted -> restore
pause [shape="rectangle"]
pause -> paused
pause -> error
active -> pause
unpause [shape="rectangle"]
unpause -> active
unpause -> error
paused -> unpause
suspend [shape="rectangle"]
suspend -> suspended
suspend -> error
active -> suspend
resume [shape="rectangle"]
resume -> active
resume -> error
suspended -> resume
start [shape="rectangle"]
start -> active
start -> error
stopped -> start
stop [shape="rectangle"]
stop -> stopped
stop -> error
active -> stop
rescue [shape="rectangle"]
rescue -> rescued
rescue -> error
active -> rescue
stopped -> rescue
unrescue [shape="rectangle"]
unrescue -> active
rescued -> unrescue
resize [shape="rectangle"]
resize -> resized
resize -> error
active -> resize
stopped -> resize
confirm_resize [shape="rectangle"]
confirm_resize -> active
confirm_resize -> error
resized -> confirm_resize
confirm_resize [shape="rectangle"]
revert_resize -> active
revert_resize -> error
resized -> revert_resize
snapshot [shape="rectangle"]
snapshot -> active
snapshot -> stopped
snapshot -> error
active -> snapshot
stopped -> snapshot
backup [shape="rectangle"]
backup -> active
backup -> stopped
backup -> error
active -> backup
stopped -> backup
rebuild [shape="rectangle"]
rebuild -> active
rebuild -> error
active -> rebuild
stopped -> rebuild
set_admin_password [shape="rectangle"]
set_admin_password -> active
set_admin_password -> error
active -> set_admin_password
reboot [shape="rectangle"]
reboot -> active
reboot -> error
active -> reboot
stopped -> reboot
rescued -> reboot
live_migrate [shape="rectangle"]
live_migrate -> active
live_migrate -> error
active -> live_migrate
}
.. image:: /images/PowerStates1.png

View File

@ -71,26 +71,22 @@ _STATE_DESCRIPTION_MAP = {
None: inst_state.PENDING,
vm_states.ACTIVE: inst_state.RUNNING,
vm_states.BUILDING: inst_state.PENDING,
vm_states.REBUILDING: inst_state.PENDING,
vm_states.DELETED: inst_state.TERMINATED,
vm_states.SOFT_DELETE: inst_state.TERMINATED,
vm_states.SOFT_DELETED: inst_state.TERMINATED,
vm_states.STOPPED: inst_state.STOPPED,
vm_states.SHUTOFF: inst_state.SHUTOFF,
vm_states.MIGRATING: inst_state.MIGRATE,
vm_states.RESIZING: inst_state.RESIZE,
vm_states.PAUSED: inst_state.PAUSE,
vm_states.SUSPENDED: inst_state.SUSPEND,
vm_states.RESCUED: inst_state.RESCUE,
vm_states.RESIZED: inst_state.RESIZE,
}
def _state_description(vm_state, shutdown_terminate):
def _state_description(vm_state, _shutdown_terminate):
"""Map the vm state to the server status string"""
if (vm_state == vm_states.SHUTOFF and
not shutdown_terminate):
name = inst_state.STOPPED
else:
name = _STATE_DESCRIPTION_MAP.get(vm_state, vm_state)
# Note(maoy): We do not provide EC2 compatibility
# in shutdown_terminate flag behavior. So we ignore
# it here.
name = _STATE_DESCRIPTION_MAP.get(vm_state, vm_state)
return {'code': inst_state.name_to_code(name),
'name': name}
@ -1454,11 +1450,10 @@ class CloudController(object):
vm_state = instance['vm_state']
# if the instance is in subtle state, refuse to proceed.
if vm_state not in (vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.STOPPED):
if vm_state not in (vm_states.ACTIVE, vm_states.STOPPED):
raise exception.InstanceNotRunning(instance_id=ec2_instance_id)
if vm_state in (vm_states.ACTIVE, vm_states.SHUTOFF):
if vm_state == vm_states.ACTIVE:
restart_instance = True
self.compute_api.stop(context, instance)

View File

@ -30,7 +30,6 @@ STOPPING = 'stopping'
STOPPED = 'stopped'
# non-ec2 value
SHUTOFF = 'shutoff'
MIGRATE = 'migrate'
RESIZE = 'resize'
PAUSE = 'pause'
@ -47,7 +46,6 @@ _NAME_TO_CODE = {
STOPPED: STOPPED_CODE,
# approximation
SHUTOFF: TERMINATED_CODE,
MIGRATE: RUNNING_CODE,
RESIZE: RUNNING_CODE,
PAUSE: STOPPED_CODE,

View File

@ -48,25 +48,26 @@ _STATE_MAP = {
task_states.REBOOTING: 'REBOOT',
task_states.REBOOTING_HARD: 'HARD_REBOOT',
task_states.UPDATING_PASSWORD: 'PASSWORD',
task_states.RESIZE_VERIFY: 'VERIFY_RESIZE',
task_states.REBUILDING: 'REBUILD',
task_states.REBUILD_BLOCK_DEVICE_MAPPING: 'REBUILD',
task_states.REBUILD_SPAWNING: 'REBUILD',
task_states.MIGRATING: 'MIGRATING',
task_states.RESIZE_PREP: 'RESIZE',
task_states.RESIZE_MIGRATING: 'RESIZE',
task_states.RESIZE_MIGRATED: 'RESIZE',
task_states.RESIZE_FINISH: 'RESIZE',
},
vm_states.BUILDING: {
'default': 'BUILD',
},
vm_states.REBUILDING: {
'default': 'REBUILD',
},
vm_states.STOPPED: {
'default': 'STOPPED',
},
vm_states.SHUTOFF: {
'default': 'SHUTOFF',
},
vm_states.MIGRATING: {
'default': 'MIGRATING',
},
vm_states.RESIZING: {
'default': 'RESIZE',
vm_states.RESIZED: {
'default': 'VERIFY_RESIZE',
# Note(maoy): the OS API spec 1.1 doesn't have CONFIRMING_RESIZE
# state so we comment that out for future reference only.
#task_states.RESIZE_CONFIRMING: 'CONFIRMING_RESIZE',
task_states.RESIZE_REVERTING: 'REVERT_RESIZE',
},
vm_states.PAUSED: {
@ -84,7 +85,7 @@ _STATE_MAP = {
vm_states.DELETED: {
'default': 'DELETED',
},
vm_states.SOFT_DELETE: {
vm_states.SOFT_DELETED: {
'default': 'DELETED',
},
}

View File

@ -62,7 +62,7 @@ flags.DECLARE('consoleauth_topic', 'nova.consoleauth')
QUOTAS = quota.QUOTAS
def check_instance_state(vm_state=None, task_state=None):
def check_instance_state(vm_state=None, task_state=(None,)):
"""Decorator to check VM and/or task state before entry to API functions.
If the instance is in the wrong state, the wrapper will raise an exception.
@ -811,7 +811,7 @@ class API(base.Base):
return dict(instance_ref.iteritems())
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
vm_states.ERROR])
def soft_delete(self, context, instance):
"""Terminate an instance."""
@ -865,7 +865,7 @@ class API(base.Base):
task_state=task_states.DELETING,
progress=0)
if instance['task_state'] == task_states.RESIZE_VERIFY:
if instance['vm_state'] == vm_states.RESIZED:
# If in the middle of a resize, use confirm_resize to
# ensure the original instance is cleaned up too
migration_ref = self.db.migration_get_by_instance_and_status(
@ -887,13 +887,9 @@ class API(base.Base):
with excutils.save_and_reraise_exception():
QUOTAS.rollback(context, reservations)
# NOTE(jerdfelt): The API implies that only ACTIVE and ERROR are
# allowed but the EC2 API appears to allow from RESCUED and STOPPED
# too
# NOTE(maoy): we allow delete to be called no matter what vm_state says.
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.BUILDING,
vm_states.ERROR, vm_states.RESCUED,
vm_states.SHUTOFF, vm_states.STOPPED])
@check_instance_state(vm_state=None, task_state=None)
def delete(self, context, instance):
"""Terminate an instance."""
LOG.debug(_("Going to try to terminate instance"), instance=instance)
@ -904,7 +900,7 @@ class API(base.Base):
self._delete(context, instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
@check_instance_state(vm_state=[vm_states.SOFT_DELETED])
def restore(self, context, instance):
"""Restore a previously deleted (but not reclaimed) instance."""
if instance['host']:
@ -921,14 +917,14 @@ class API(base.Base):
deleted_at=None)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
@check_instance_state(vm_state=[vm_states.SOFT_DELETED])
def force_delete(self, context, instance):
"""Force delete a previously deleted (but not reclaimed) instance."""
self._delete(context, instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.RESCUED,
vm_states.ERROR, vm_states.STOPPED],
task_state=[None])
def stop(self, context, instance, do_cast=True):
"""Stop an instance."""
@ -943,7 +939,7 @@ class API(base.Base):
self.compute_rpcapi.stop_instance(context, instance, cast=do_cast)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.STOPPED, vm_states.SHUTOFF])
@check_instance_state(vm_state=[vm_states.STOPPED])
def start(self, context, instance):
"""Start an instance."""
LOG.debug(_("Going to try to start instance"), instance=instance)
@ -1088,7 +1084,7 @@ class API(base.Base):
sort_dir)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF])
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def backup(self, context, instance, name, backup_type, rotation,
extra_properties=None):
"""Backup the given instance
@ -1106,7 +1102,7 @@ class API(base.Base):
return recv_meta
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF])
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def snapshot(self, context, instance, name, extra_properties=None):
"""Snapshot the given instance.
@ -1201,7 +1197,7 @@ class API(base.Base):
return min_ram, min_disk
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
vm_states.RESCUED],
task_state=[None])
def reboot(self, context, instance, reboot_type):
@ -1222,7 +1218,7 @@ class API(base.Base):
return image_service.show(context, image_id)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED],
task_state=[None])
def rebuild(self, context, instance, image_href, admin_password, **kwargs):
"""Rebuild the given instance with the provided attributes."""
@ -1270,11 +1266,10 @@ class API(base.Base):
self.update(context,
instance,
vm_state=vm_states.REBUILDING,
task_state=task_states.REBUILDING,
# Unfortunately we need to set image_ref early,
# so API users can see it.
image_ref=image_href,
task_state=None,
progress=0,
**kwargs)
@ -1288,8 +1283,7 @@ class API(base.Base):
image_ref=image_href, orig_image_ref=orig_image_ref)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[task_states.RESIZE_VERIFY])
@check_instance_state(vm_state=[vm_states.RESIZED])
def revert_resize(self, context, instance):
"""Reverts a resize, deleting the 'new' instance in the process."""
context = context.elevated()
@ -1301,7 +1295,6 @@ class API(base.Base):
self.update(context,
instance,
vm_state=vm_states.RESIZING,
task_state=task_states.RESIZE_REVERTING)
self.compute_rpcapi.revert_resize(context,
@ -1312,8 +1305,7 @@ class API(base.Base):
{'status': 'reverted'})
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[task_states.RESIZE_VERIFY])
@check_instance_state(vm_state=[vm_states.RESIZED])
def confirm_resize(self, context, instance):
"""Confirms a migration/resize and deletes the 'old' instance."""
context = context.elevated()
@ -1338,7 +1330,7 @@ class API(base.Base):
{'host': migration_ref['dest_compute'], })
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED],
task_state=[None])
def resize(self, context, instance, flavor_id=None, **kwargs):
"""Resize (ie, migrate) a running instance.
@ -1385,7 +1377,6 @@ class API(base.Base):
self.update(context,
instance,
vm_state=vm_states.RESIZING,
task_state=task_states.RESIZE_PREP,
progress=0,
**kwargs)
@ -1424,9 +1415,7 @@ class API(base.Base):
instance=instance, address=address)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
task_state=[None])
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.RESCUED])
def pause(self, context, instance):
"""Pause the given instance."""
self.update(context,
@ -1451,9 +1440,7 @@ class API(base.Base):
return self.compute_rpcapi.get_diagnostics(context, instance=instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
task_state=[None])
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.RESCUED])
def suspend(self, context, instance):
"""Suspend the given instance."""
self.update(context,
@ -1473,9 +1460,7 @@ class API(base.Base):
self.compute_rpcapi.resume_instance(context, instance=instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.STOPPED],
task_state=[None])
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def rescue(self, context, instance, rescue_password=None):
"""Rescue the given instance."""
self.update(context,
@ -1497,8 +1482,7 @@ class API(base.Base):
self.compute_rpcapi.unrescue_instance(context, instance=instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE],
task_state=[None])
@check_instance_state(vm_state=[vm_states.ACTIVE])
def set_admin_password(self, context, instance, password=None):
"""Set the root/admin password for the given instance."""
self.update(context,

View File

@ -811,7 +811,7 @@ class ComputeManager(manager.SchedulerDependentManager):
@checks_instance_lock
@wrap_instance_fault
def power_off_instance(self, context, instance_uuid,
final_state=vm_states.SOFT_DELETE):
final_state=vm_states.SOFT_DELETED):
"""Power off an instance on this host."""
instance = self.db.instance_get_by_uuid(context, instance_uuid)
self._notify_about_instance_usage(context, instance, "power_off.start")
@ -895,16 +895,15 @@ class ComputeManager(manager.SchedulerDependentManager):
self._instance_update(context,
instance_uuid,
power_state=current_power_state,
vm_state=vm_states.REBUILDING,
task_state=None)
task_state=task_states.REBUILDING)
network_info = self._get_instance_nw_info(context, instance)
self.driver.destroy(instance, self._legacy_nw_info(network_info))
instance = self._instance_update(context,
instance_uuid,
vm_state=vm_states.REBUILDING,
task_state=task_states.BLOCK_DEVICE_MAPPING)
task_state=task_states.\
REBUILD_BLOCK_DEVICE_MAPPING)
instance.injected_files = kwargs.get('injected_files', [])
network_info = self.network_api.get_instance_nw_info(context,
@ -913,8 +912,8 @@ class ComputeManager(manager.SchedulerDependentManager):
instance = self._instance_update(context,
instance_uuid,
vm_state=vm_states.REBUILDING,
task_state=task_states.SPAWNING)
task_state=task_states.\
REBUILD_SPAWNING)
# pull in new password here since the original password isn't in the db
instance.admin_pass = kwargs.get('new_pass',
utils.generate_password(FLAGS.password_length))
@ -1459,10 +1458,10 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_ref = self._instance_update(context,
instance_ref.uuid,
vm_state=vm_states.ACTIVE,
vm_state=vm_states.RESIZED,
host=migration_ref['dest_compute'],
launched_at=timeutils.utcnow(),
task_state=task_states.RESIZE_VERIFY)
task_state=None)
self.db.migration_update(context, migration_ref.id,
{'status': 'finished'})
@ -2340,9 +2339,10 @@ class ComputeManager(manager.SchedulerDependentManager):
_set_migration_to_error(migration_id, reason % locals(),
instance=instance)
continue
if instance['task_state'] != task_states.RESIZE_VERIFY:
state = instance['task_state']
reason = _("In %(state)s task_state, not RESIZE_VERIFY")
if instance['vm_state'] != vm_states.RESIZED \
and instance['task_state'] != None:
state = instance['vm_state']
reason = _("In %(state)s vm_state, not RESIZED")
_set_migration_to_error(migration_id, reason % locals(),
instance=instance)
continue
@ -2407,7 +2407,7 @@ class ComputeManager(manager.SchedulerDependentManager):
each loop to allow the periodic task eventlet to do other work.
If the instance is not found on the hypervisor, but is in the database,
then it will be set to power_state.NOSTATE.
then a stop() API will be called on the instance.
"""
db_instances = self.db.instance_get_all_by_host(context, self.host)
@ -2422,68 +2422,114 @@ class ComputeManager(manager.SchedulerDependentManager):
# Allow other periodic tasks to do some work...
greenthread.sleep(0)
db_power_state = db_instance['power_state']
if db_instance['task_state'] is not None:
LOG.info(_("During sync_power_state the instance has a "
"pending task. Skip."), instance=db_instance)
continue
# No pending tasks. Now try to figure out the real vm_power_state.
try:
vm_instance = self.driver.get_info(db_instance)
vm_power_state = vm_instance['state']
except exception.InstanceNotFound:
# This exception might have been caused by a race condition
# between _sync_power_states and live migrations. Two cases
# are possible as documented below. To this aim, refresh the
# DB instance state.
try:
u = self.db.instance_get_by_uuid(context,
db_instance['uuid'])
if self.host != u['host']:
# on the sending end of nova-compute _sync_power_state
# may have yielded to the greenthread performing a live
# migration; this in turn has changed the resident-host
# for the VM; However, the instance is still active, it
# is just in the process of migrating to another host.
# This implies that the compute source must relinquish
# control to the compute destination.
LOG.info(_("During the sync_power process the "
"instance has moved from "
"host %(src)s to host %(dst)s") %
{'src': self.host,
'dst': u['host']},
instance=db_instance)
elif (u['host'] == self.host and
u['vm_state'] == vm_states.MIGRATING):
# on the receiving end of nova-compute, it could happen
# that the DB instance already report the new resident
# but the actual VM has not showed up on the hypervisor
# yet. In this case, let's allow the loop to continue
# and run the state sync in a later round
LOG.info(_("Instance is in the process of "
"migrating to this host. Wait next "
"sync_power cycle before setting "
"power state to NOSTATE"),
instance=db_instance)
else:
LOG.warn(_("Instance found in database but not "
"known by hypervisor. Setting power "
"state to NOSTATE"), locals(),
instance=db_instance)
vm_power_state = power_state.NOSTATE
except exception.InstanceNotFound:
# no need to update vm_state for deleted instances
continue
if vm_power_state == db_power_state:
vm_power_state = power_state.NOSTATE
# Note(maoy): the above get_info call might take a long time,
# for example, because of a broken libvirt driver.
# We re-query the DB to get the latest instance info to minimize
# (not eliminate) race condition.
u = self.db.instance_get_by_uuid(context,
db_instance['uuid'])
db_power_state = u["power_state"]
vm_state = u['vm_state']
if self.host != u['host']:
# on the sending end of nova-compute _sync_power_state
# may have yielded to the greenthread performing a live
# migration; this in turn has changed the resident-host
# for the VM; However, the instance is still active, it
# is just in the process of migrating to another host.
# This implies that the compute source must relinquish
# control to the compute destination.
LOG.info(_("During the sync_power process the "
"instance has moved from "
"host %(src)s to host %(dst)s") %
{'src': self.host,
'dst': u['host']},
instance=db_instance)
continue
if (vm_power_state in (power_state.NOSTATE,
power_state.SHUTDOWN,
power_state.CRASHED)
and db_instance['vm_state'] == vm_states.ACTIVE):
self._instance_update(context,
db_instance['uuid'],
power_state=vm_power_state,
vm_state=vm_states.SHUTOFF)
else:
elif u['task_state'] is not None:
# on the receiving end of nova-compute, it could happen
# that the DB instance already report the new resident
# but the actual VM has not showed up on the hypervisor
# yet. In this case, let's allow the loop to continue
# and run the state sync in a later round
LOG.info(_("During sync_power_state the instance has a "
"pending task. Skip."), instance=db_instance)
continue
if vm_power_state != db_power_state:
# power_state is always updated from hypervisor to db
self._instance_update(context,
db_instance['uuid'],
power_state=vm_power_state)
db_power_state = vm_power_state
# Note(maoy): Now resolve the discrepancy between vm_state and
# vm_power_state. We go through all possible vm_states.
if vm_state in (vm_states.BUILDING,
vm_states.RESCUED,
vm_states.RESIZED,
vm_states.SUSPENDED,
vm_states.PAUSED,
vm_states.ERROR):
# TODO(maoy): we ignore these vm_state for now.
pass
elif vm_state == vm_states.ACTIVE:
# The only rational power state should be RUNNING
if vm_power_state in (power_state.NOSTATE,
power_state.SHUTDOWN,
power_state.CRASHED):
LOG.warn(_("Instance shutdown by itself. Calling "
"the stop API."), instance=db_instance)
try:
# Note(maoy): here we call the API instead of
# brutally updating the vm_state in the database
# to allow all the hooks and checks to be performed.
self.compute_api.stop(context, db_instance)
except Exception:
# Note(maoy): there is no need to propergate the error
# because the same power_state will be retrieved next
# time and retried.
# For example, there might be another task scheduled.
LOG.exception(_("error during stop() in "
"sync_power_state."))
elif vm_power_state in (power_state.PAUSED,
power_state.SUSPENDED):
LOG.warn(_("Instance is paused or suspended "
"unexpectedly. Calling "
"the stop API."), instance=db_instance)
try:
self.compute_api.stop(context, db_instance)
except Exception:
LOG.exception(_("error during stop() in "
"sync_power_state."))
elif vm_state == vm_states.STOPPED:
if vm_power_state not in (power_state.NOSTATE,
power_state.SHUTDOWN,
power_state.CRASHED):
LOG.warn(_("Instance is not stopped. Calling "
"the stop API."), instance=db_instance)
try:
# Note(maoy): this assumes that the stop API is
# idempotent.
self.compute_api.stop(context, db_instance)
except Exception:
LOG.exception(_("error during stop() in "
"sync_power_state."))
elif vm_state in (vm_states.SOFT_DELETED,
vm_states.DELETED):
if vm_power_state not in (power_state.NOSTATE,
power_state.SHUTDOWN):
# Note(maoy): this should be taken care of periodically in
# _cleanup_running_deleted_instances().
LOG.warn(_("Instance is not (soft-)deleted."),
instance=db_instance)
@manager.periodic_task
def _reclaim_queued_deletes(self, context):
@ -2498,7 +2544,7 @@ class ComputeManager(manager.SchedulerDependentManager):
old_enough = (not instance.deleted_at or
timeutils.is_older_than(instance.deleted_at,
interval))
soft_deleted = instance.vm_state == vm_states.SOFT_DELETE
soft_deleted = instance.vm_state == vm_states.SOFT_DELETED
if soft_deleted and old_enough:
LOG.info(_('Reclaiming deleted instance'), instance=instance)

View File

@ -25,36 +25,74 @@ necessary.
"""
# possible task states during create()
SCHEDULING = 'scheduling'
BLOCK_DEVICE_MAPPING = 'block_device_mapping'
NETWORKING = 'networking'
SPAWNING = 'spawning'
# possible task states during snapshot()
IMAGE_SNAPSHOT = 'image_snapshot'
# possible task states during backup()
IMAGE_BACKUP = 'image_backup'
# possible task states during set_admin_password()
UPDATING_PASSWORD = 'updating_password'
# possible task states during resize()
RESIZE_PREP = 'resize_prep'
RESIZE_MIGRATING = 'resize_migrating'
RESIZE_MIGRATED = 'resize_migrated'
RESIZE_FINISH = 'resize_finish'
RESIZE_REVERTING = 'resize_reverting'
RESIZE_CONFIRMING = 'resize_confirming'
RESIZE_VERIFY = 'resize_verify'
# possible task states during revert_resize()
RESIZE_REVERTING = 'resize_reverting'
# possible task states during confirm_resize()
RESIZE_CONFIRMING = 'resize_confirming'
# possible task states during reboot()
REBOOTING = 'rebooting'
REBOOTING_HARD = 'rebooting_hard'
# possible task states during pause()
PAUSING = 'pausing'
# possible task states during unpause()
UNPAUSING = 'unpausing'
# possible task states during suspend()
SUSPENDING = 'suspending'
# possible task states during resume()
RESUMING = 'resuming'
# possible task states during stop()
STOPPING = 'stopping'
# possible task states during start()
STARTING = 'starting'
# possible task states during soft_delete()
POWERING_OFF = 'powering-off'
# possible task states during restore()
POWERING_ON = 'powering-on'
# possible task states during rescue()
RESCUING = 'rescuing'
# possible task states during unrescue()
UNRESCUING = 'unrescuing'
# possible task states during rebuild()
REBUILDING = 'rebuilding'
REBUILD_BLOCK_DEVICE_MAPPING = "rebuild_block_device_mapping"
REBUILD_SPAWNING = 'rebuild_spawning'
# possible task states during live_migrate()
MIGRATING = "migrating"
# possible task states during delete()
DELETING = 'deleting'
STOPPING = 'stopping'
STARTING = 'starting'

View File

@ -18,24 +18,29 @@
"""Possible vm states for instances.
Compute instance vm states represent the state of an instance as it pertains to
a user or administrator. When combined with task states (task_states.py), a
better picture can be formed regarding the instance's health.
a user or administrator.
vm_state describes a VM's current stable (not transition) state. That is, if
there is no ongoing compute API calls (running tasks), vm_state should reflect
what the customer expect the VM to be. When combined with task states
(task_states.py), a better picture can be formed regarding the instance's
health and progress.
See http://wiki.openstack.org/VMState
"""
ACTIVE = 'active'
BUILDING = 'building'
REBUILDING = 'rebuilding'
ACTIVE = 'active' # VM is running
BUILDING = 'building' # VM only exists in DB
PAUSED = 'paused'
SUSPENDED = 'suspended'
SHUTOFF = 'shutoff'
RESCUED = 'rescued'
DELETED = 'deleted'
STOPPED = 'stopped'
SOFT_DELETE = 'soft-delete'
SUSPENDED = 'suspended' # VM is suspended to disk.
STOPPED = 'stopped' # VM is powered off, the disk image is still there.
RESCUED = 'rescued' # A rescue image is running with the original VM image
# attached.
RESIZED = 'resized' # a VM with the new size is active. The user is expected
# to manually confirm or revert.
MIGRATING = 'migrating'
RESIZING = 'resizing'
SOFT_DELETED = 'soft-delete' # VM is marked as deleted but the disk images are
# still available to restore.
DELETED = 'deleted' # VM is permanently deleted.
ERROR = 'error'

View File

@ -503,8 +503,7 @@ def _get_host_utilization(context, host, ram_mb, disk_gb):
free_ram_mb -= instance.memory_mb
free_disk_gb -= instance.root_gb
free_disk_gb -= instance.ephemeral_gb
if instance.vm_state in [vm_states.BUILDING, vm_states.REBUILDING,
vm_states.MIGRATING, vm_states.RESIZING]:
if instance.task_state is not None:
work += 1
return dict(free_ram_mb=free_ram_mb,
free_disk_gb=free_disk_gb,
@ -1497,12 +1496,12 @@ def instance_get_all_by_filters(context, filters, sort_key, sort_dir):
# include or exclude both
if filters.pop('deleted'):
deleted = or_(models.Instance.deleted == True,
models.Instance.vm_state == vm_states.SOFT_DELETE)
models.Instance.vm_state == vm_states.SOFT_DELETED)
query_prefix = query_prefix.filter(deleted)
else:
query_prefix = query_prefix.\
filter_by(deleted=False).\
filter(models.Instance.vm_state != vm_states.SOFT_DELETE)
filter(models.Instance.vm_state != vm_states.SOFT_DELETED)
if not context.is_admin:
# If we're not admin context, add appropriate filter..

View File

@ -24,7 +24,7 @@ Scheduler base class that all Schedulers should inherit from
from nova.compute import api as compute_api
from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import vm_states
from nova.compute import task_states
from nova import db
from nova import exception
from nova import flags
@ -228,7 +228,7 @@ class Scheduler(object):
disk_over_commit)
# Changing instance_state.
values = {"vm_state": vm_states.MIGRATING}
values = {"task_state": task_states.MIGRATING}
# update instance state and notify
(old_ref, new_instance_ref) = db.instance_update_and_get_original(

View File

@ -854,7 +854,7 @@ class CloudTestCase(test.TestCase):
test_instance_state(inst_state.RUNNING_CODE, inst_state.RUNNING,
power_state.RUNNING, vm_states.ACTIVE)
test_instance_state(inst_state.STOPPED_CODE, inst_state.STOPPED,
power_state.NOSTATE, vm_states.SHUTOFF,
power_state.NOSTATE, vm_states.STOPPED,
{'shutdown_terminate': False})
def test_describe_instances_no_ipv6(self):

View File

@ -58,7 +58,8 @@ def fake_compute_api_raises_invalid_state(*args, **kwargs):
def fake_compute_api_get(self, context, instance_id):
return {'id': 1, 'uuid': instance_id, 'vm_state': vm_states.ACTIVE}
return {'id': 1, 'uuid': instance_id, 'vm_state': vm_states.ACTIVE,
'task_state': None}
def fake_scheduler_api_live_migration(self, context, block_migration,

View File

@ -19,6 +19,7 @@ import mox
import webob
from nova.api.openstack.compute import servers
from nova.compute import task_states
from nova.compute import vm_states
import nova.db
from nova import exception
@ -461,8 +462,8 @@ class ServerActionsControllerTest(test.TestCase):
context = req.environ['nova.context']
update(context, mox.IgnoreArg(),
image_ref=self._image_href,
vm_state=vm_states.REBUILDING,
task_state=None, progress=0, **attributes).AndReturn(None)
task_state=task_states.REBUILDING,
progress=0, **attributes).AndReturn(None)
self.mox.ReplayAll()
self.controller._action_rebuild(req, FAKE_UUID, body)

View File

@ -1331,16 +1331,17 @@ class ServersControllerTest(test.TestCase):
self.server_delete_called = False
self.stubs.Set(nova.db, 'instance_get_by_uuid',
fakes.fake_instance_get(vm_state=vm_states.RESIZING))
fakes.fake_instance_get(vm_state=vm_states.ACTIVE,
task_state=task_states.RESIZE_PREP))
def instance_destroy_mock(context, id):
def instance_destroy_mock(*args, **kwargs):
self.server_delete_called = True
self.stubs.Set(nova.db, 'instance_destroy', instance_destroy_mock)
self.assertRaises(webob.exc.HTTPConflict,
self.controller.delete,
req,
FAKE_UUID)
self.controller.delete(req, FAKE_UUID)
# Delete shoud be allowed in any case, even during resizing,
# because it may get stuck.
self.assertEqual(self.server_delete_called, True)
class ServerStatusTest(test.TestCase):
@ -1374,7 +1375,8 @@ class ServerStatusTest(test.TestCase):
self.assertEqual(response['server']['status'], 'HARD_REBOOT')
def test_rebuild(self):
response = self._get_with_state(vm_states.REBUILDING)
response = self._get_with_state(vm_states.ACTIVE,
task_states.REBUILDING)
self.assertEqual(response['server']['status'], 'REBUILD')
def test_rebuild_error(self):
@ -1382,16 +1384,16 @@ class ServerStatusTest(test.TestCase):
self.assertEqual(response['server']['status'], 'ERROR')
def test_resize(self):
response = self._get_with_state(vm_states.RESIZING)
response = self._get_with_state(vm_states.ACTIVE,
task_states.RESIZE_PREP)
self.assertEqual(response['server']['status'], 'RESIZE')
def test_verify_resize(self):
response = self._get_with_state(vm_states.ACTIVE,
task_states.RESIZE_VERIFY)
response = self._get_with_state(vm_states.RESIZED, None)
self.assertEqual(response['server']['status'], 'VERIFY_RESIZE')
def test_revert_resize(self):
response = self._get_with_state(vm_states.RESIZING,
response = self._get_with_state(vm_states.RESIZED,
task_states.RESIZE_REVERTING)
self.assertEqual(response['server']['status'], 'REVERT_RESIZE')

View File

@ -1518,7 +1518,7 @@ class ComputeTestCase(BaseTestCase):
instance_id = instance['id']
i_ref = db.instance_get(c, instance_id)
db.instance_update(c, i_ref['uuid'],
{'vm_state': vm_states.MIGRATING,
{'task_state': task_states.MIGRATING,
'power_state': power_state.PAUSED})
v_ref = db.volume_create(c, {'size': 1, 'instance_id': instance_id})
fix_addr = db.fixed_ip_create(c, {'address': '1.1.1.1',
@ -1579,7 +1579,7 @@ class ComputeTestCase(BaseTestCase):
instances = db.instance_get_all(ctxt)
LOG.info(_("After force-killing instances: %s"), instances)
self.assertEqual(len(instances), 1)
self.assertEqual(power_state.NOSTATE, instances[0]['power_state'])
self.assertEqual(task_states.STOPPING, instances[0]['task_state'])
def test_add_instance_fault(self):
exc_info = None
@ -1819,17 +1819,17 @@ class ComputeTestCase(BaseTestCase):
self.assertEqual(call_info['get_nw_info'], 4)
def test_poll_unconfirmed_resizes(self):
instances = [{'uuid': 'fake_uuid1', 'vm_state': vm_states.ACTIVE,
'task_state': task_states.RESIZE_VERIFY},
instances = [{'uuid': 'fake_uuid1', 'vm_state': vm_states.RESIZED,
'task_state': None},
{'uuid': 'noexist'},
{'uuid': 'fake_uuid2', 'vm_state': vm_states.ERROR,
'task_state': task_states.RESIZE_VERIFY},
'task_state': None},
{'uuid': 'fake_uuid3', 'vm_state': vm_states.ACTIVE,
'task_state': task_states.REBOOTING},
{'uuid': 'fake_uuid4', 'vm_state': vm_states.ACTIVE,
'task_state': task_states.RESIZE_VERIFY},
'task_state': None},
{'uuid': 'fake_uuid5', 'vm_state': vm_states.ACTIVE,
'task_state': task_states.RESIZE_VERIFY}]
'task_state': None}]
expected_migration_status = {'fake_uuid1': 'confirmed',
'noexist': 'error',
'fake_uuid2': 'error',
@ -2261,12 +2261,12 @@ class ComputeAPITestCase(BaseTestCase):
# the instance is shutdown by itself
db.instance_update(self.context, instance['uuid'],
{'power_state': power_state.NOSTATE,
'vm_state': vm_states.SHUTOFF})
check_state(instance['uuid'], power_state.NOSTATE, vm_states.SHUTOFF,
'vm_state': vm_states.STOPPED})
check_state(instance['uuid'], power_state.NOSTATE, vm_states.STOPPED,
None)
start_check_state(instance['uuid'], power_state.NOSTATE,
vm_states.SHUTOFF, task_states.STARTING)
vm_states.STOPPED, task_states.STARTING)
db.instance_destroy(self.context, instance['uuid'])
@ -2344,7 +2344,8 @@ class ComputeAPITestCase(BaseTestCase):
# set the state that the instance gets when soft_delete finishes
instance = db.instance_update(self.context, instance['uuid'],
{'vm_state': vm_states.SOFT_DELETE})
{'vm_state': vm_states.SOFT_DELETED,
'task_state': None})
self.compute_api.force_delete(self.context, instance)
@ -2432,7 +2433,8 @@ class ComputeAPITestCase(BaseTestCase):
# set the state that the instance gets when soft_delete finishes
instance = db.instance_update(self.context, instance['uuid'],
{'vm_state': vm_states.SOFT_DELETE})
{'vm_state': vm_states.SOFT_DELETED,
'task_state': None})
self.compute_api.restore(self.context, instance)
@ -2476,7 +2478,7 @@ class ComputeAPITestCase(BaseTestCase):
self.assertEqual(info['image_ref'], image_ref)
instance = db.instance_get_by_uuid(self.context, instance_uuid)
self.assertEqual(instance['vm_state'], vm_states.REBUILDING)
self.assertEqual(instance['task_state'], task_states.REBUILDING)
sys_metadata = db.instance_system_metadata_get(self.context,
instance_uuid)
self.assertEqual(sys_metadata,
@ -2772,8 +2774,8 @@ class ComputeAPITestCase(BaseTestCase):
'status': 'finished'})
# set the state that the instance gets when resize finishes
instance = db.instance_update(self.context, instance['uuid'],
{'task_state': task_states.RESIZE_VERIFY,
'vm_state': vm_states.ACTIVE})
{'task_state': None,
'vm_state': vm_states.RESIZED})
self.compute_api.confirm_resize(context, instance)
self.compute.terminate_instance(context, instance['uuid'])
@ -2792,13 +2794,13 @@ class ComputeAPITestCase(BaseTestCase):
'status': 'finished'})
# set the state that the instance gets when resize finishes
instance = db.instance_update(self.context, instance['uuid'],
{'task_state': task_states.RESIZE_VERIFY,
'vm_state': vm_states.ACTIVE})
{'task_state': None,
'vm_state': vm_states.RESIZED})
self.compute_api.revert_resize(context, instance)
instance = db.instance_get_by_uuid(context, instance['uuid'])
self.assertEqual(instance['vm_state'], vm_states.RESIZING)
self.assertEqual(instance['vm_state'], vm_states.RESIZED)
self.assertEqual(instance['task_state'], task_states.RESIZE_REVERTING)
self.compute.terminate_instance(context, instance['uuid'])

View File

@ -22,6 +22,7 @@ Tests For Scheduler
from nova.compute import api as compute_api
from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import task_states
from nova.compute import vm_states
from nova import context
from nova import db
@ -454,7 +455,7 @@ class SchedulerTestCase(test.TestCase):
self.driver._live_migration_common_check(self.context, instance,
dest, block_migration, disk_over_commit)
db.instance_update_and_get_original(self.context, instance['id'],
{"vm_state": vm_states.MIGRATING}).AndReturn(
{"task_state": task_states.MIGRATING}).AndReturn(
(instance, instance))
driver.cast_to_compute_host(self.context, instance['host'],
@ -563,7 +564,7 @@ class SchedulerTestCase(test.TestCase):
).AndReturn(True)
db.instance_update_and_get_original(self.context, instance['id'],
{"vm_state": vm_states.MIGRATING}).AndReturn(
{"task_state": task_states.MIGRATING}).AndReturn(
(instance, instance))
driver.cast_to_compute_host(self.context, instance['host'],

View File

@ -26,7 +26,7 @@ import time
from nova import test
from nova.compute import task_states
from nova.compute import vm_states
from nova import db
from nova import flags
from nova import log
@ -150,16 +150,19 @@ class ImageCacheManagerTestCase(test.TestCase):
'host': FLAGS.host,
'name': 'inst-1',
'uuid': '123',
'vm_state': '',
'task_state': ''},
{'image_ref': '2',
'host': FLAGS.host,
'name': 'inst-2',
'uuid': '456',
'vm_state': '',
'task_state': ''},
{'image_ref': '2',
'host': 'remotehost',
'name': 'inst-3',
'uuid': '789',
'vm_state': '',
'task_state': ''}])
image_cache_manager = imagecache.ImageCacheManager()
@ -183,7 +186,8 @@ class ImageCacheManagerTestCase(test.TestCase):
'host': FLAGS.host,
'name': 'inst-1',
'uuid': '123',
'task_state': task_states.RESIZE_VERIFY}])
'vm_state': vm_states.RESIZED,
'task_state': None}])
image_cache_manager = imagecache.ImageCacheManager()
image_cache_manager._list_running_instances(None)
@ -766,11 +770,13 @@ class ImageCacheManagerTestCase(test.TestCase):
'host': FLAGS.host,
'name': 'instance-1',
'uuid': '123',
'vm_state': '',
'task_state': ''},
{'image_ref': '1',
'host': FLAGS.host,
'name': 'instance-2',
'uuid': '456',
'vm_state': '',
'task_state': ''}])
image_cache_manager = imagecache.ImageCacheManager()
@ -865,11 +871,13 @@ class ImageCacheManagerTestCase(test.TestCase):
'host': FLAGS.host,
'name': 'instance-1',
'uuid': '123',
'vm_state': '',
'task_state': ''},
{'image_ref': '1',
'host': FLAGS.host,
'name': 'instance-2',
'uuid': '456',
'vm_state': '',
'task_state': ''}])
def touch(filename):

View File

@ -29,6 +29,7 @@ import re
import time
from nova.compute import task_states
from nova.compute import vm_states
from nova import db
from nova import flags
from nova import log as logging
@ -140,11 +141,9 @@ class ImageCacheManager(object):
resize_states = [task_states.RESIZE_PREP,
task_states.RESIZE_MIGRATING,
task_states.RESIZE_MIGRATED,
task_states.RESIZE_FINISH,
task_states.RESIZE_REVERTING,
task_states.RESIZE_CONFIRMING,
task_states.RESIZE_VERIFY]
if instance['task_state'] in resize_states:
task_states.RESIZE_FINISH]
if instance['task_state'] in resize_states or \
instance['vm_state'] in vm_states.RESIZED:
self.instance_names.add(instance['name'] + '_resize')
image_ref_str = str(instance['image_ref'])

View File

@ -21,6 +21,7 @@ Management class for host-related functions (start, reboot, etc).
import logging
from nova.compute import task_states
from nova.compute import vm_states
from nova import context
from nova import db
@ -80,7 +81,7 @@ class Host(object):
ctxt,
instance.id,
{'host': dest,
'vm_state': vm_states.MIGRATING})
'task_state': task_states.MIGRATING})
notifications.send_update(ctxt, old_ref, new_ref)
self._session.call_xenapi('VM.pool_migrate',