Merge "Cleans up power_off and power_on semantics"
This commit is contained in:
+10
-23
@@ -967,7 +967,6 @@ class API(base.Base):
|
||||
if instance['host']:
|
||||
self.update(context,
|
||||
instance,
|
||||
vm_state=vm_states.SOFT_DELETE,
|
||||
task_state=task_states.POWERING_OFF,
|
||||
deleted_at=utils.utcnow())
|
||||
|
||||
@@ -1047,15 +1046,18 @@ class API(base.Base):
|
||||
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
|
||||
def restore(self, context, instance):
|
||||
"""Restore a previously deleted (but not reclaimed) instance."""
|
||||
self.update(context,
|
||||
instance,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
task_state=None,
|
||||
deleted_at=None)
|
||||
|
||||
if instance['host']:
|
||||
self.update(context, instance, task_state=task_states.POWERING_ON)
|
||||
self.update(context,
|
||||
instance,
|
||||
task_state=task_states.POWERING_ON,
|
||||
deleted_at=None)
|
||||
self.compute_rpcapi.power_on_instance(context, instance)
|
||||
else:
|
||||
self.update(context,
|
||||
instance,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
task_state=None,
|
||||
deleted_at=None)
|
||||
|
||||
@wrap_check_policy
|
||||
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
|
||||
@@ -1074,9 +1076,7 @@ class API(base.Base):
|
||||
|
||||
self.update(context,
|
||||
instance,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
task_state=task_states.STOPPING,
|
||||
terminated_at=utils.utcnow(),
|
||||
progress=0)
|
||||
|
||||
self.compute_rpcapi.stop_instance(context, instance, cast=do_cast)
|
||||
@@ -1085,23 +1085,10 @@ class API(base.Base):
|
||||
@check_instance_state(vm_state=[vm_states.STOPPED, vm_states.SHUTOFF])
|
||||
def start(self, context, instance):
|
||||
"""Start an instance."""
|
||||
vm_state = instance["vm_state"]
|
||||
instance_uuid = instance["uuid"]
|
||||
LOG.debug(_("Going to try to start instance"), instance=instance)
|
||||
|
||||
if vm_state == vm_states.SHUTOFF:
|
||||
if instance['shutdown_terminate']:
|
||||
LOG.warning(_("Instance %(instance_uuid)s is not "
|
||||
"stopped. (%(vm_state)s") % locals())
|
||||
return
|
||||
|
||||
# NOTE(yamahata): nova compute doesn't reap instances
|
||||
# which initiated shutdown itself. So reap it here.
|
||||
self.stop(context, instance, do_cast=False)
|
||||
|
||||
self.update(context,
|
||||
instance,
|
||||
vm_state=vm_states.STOPPED,
|
||||
task_state=task_states.STARTING)
|
||||
|
||||
# TODO(yamahata): injected_files isn't supported right now.
|
||||
|
||||
+22
-29
@@ -701,24 +701,10 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
self._run_instance(context, instance_uuid, **kwargs)
|
||||
do_run_instance()
|
||||
|
||||
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
|
||||
@checks_instance_lock
|
||||
@wrap_instance_fault
|
||||
def start_instance(self, context, instance_uuid):
|
||||
@utils.synchronized(instance_uuid)
|
||||
def do_start_instance():
|
||||
"""Starting an instance on this host."""
|
||||
# TODO(yamahata): injected_files isn't supported.
|
||||
# Anyway OSAPI doesn't support stop/start yet
|
||||
# FIXME(vish): I've kept the files during stop instance, but
|
||||
# I think start will fail due to the files still
|
||||
self._run_instance(context, instance_uuid)
|
||||
do_start_instance()
|
||||
|
||||
def _shutdown_instance(self, context, instance, action_str):
|
||||
def _shutdown_instance(self, context, instance):
|
||||
"""Shutdown an instance on this host."""
|
||||
context = context.elevated()
|
||||
LOG.audit(_('%(action_str)s instance') % {'action_str': action_str},
|
||||
LOG.audit(_('%(action_str)s instance') % {'action_str': 'Terminating'},
|
||||
context=context, instance=instance)
|
||||
|
||||
self._notify_about_instance_usage(context, instance, "shutdown.start")
|
||||
@@ -765,7 +751,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
"""Delete an instance on this host."""
|
||||
instance_uuid = instance['uuid']
|
||||
self._notify_about_instance_usage(context, instance, "delete.start")
|
||||
self._shutdown_instance(context, instance, 'Terminating')
|
||||
self._shutdown_instance(context, instance)
|
||||
self._cleanup_volumes(context, instance_uuid)
|
||||
instance = self._instance_update(context,
|
||||
instance_uuid,
|
||||
@@ -804,21 +790,26 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
@checks_instance_lock
|
||||
@wrap_instance_fault
|
||||
def stop_instance(self, context, instance_uuid):
|
||||
"""Stopping an instance on this host."""
|
||||
@utils.synchronized(instance_uuid)
|
||||
def do_stop_instance():
|
||||
instance = self.db.instance_get_by_uuid(context, instance_uuid)
|
||||
self._shutdown_instance(context, instance, 'Stopping')
|
||||
self._instance_update(context,
|
||||
instance_uuid,
|
||||
vm_state=vm_states.STOPPED,
|
||||
task_state=None)
|
||||
do_stop_instance()
|
||||
"""Stopping an instance on this host.
|
||||
|
||||
Alias for power_off_instance for compatibility"""
|
||||
self.power_off_instance(context, instance_uuid,
|
||||
final_state=vm_states.STOPPED)
|
||||
|
||||
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
|
||||
@checks_instance_lock
|
||||
@wrap_instance_fault
|
||||
def power_off_instance(self, context, instance_uuid):
|
||||
def start_instance(self, context, instance_uuid):
|
||||
"""Starting an instance on this host.
|
||||
|
||||
Alias for power_on_instance for compatibility"""
|
||||
self.power_on_instance(context, instance_uuid)
|
||||
|
||||
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
|
||||
@checks_instance_lock
|
||||
@wrap_instance_fault
|
||||
def power_off_instance(self, context, instance_uuid,
|
||||
final_state=vm_states.SOFT_DELETE):
|
||||
"""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")
|
||||
@@ -827,6 +818,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
self._instance_update(context,
|
||||
instance_uuid,
|
||||
power_state=current_power_state,
|
||||
vm_state=final_state,
|
||||
task_state=None)
|
||||
self._notify_about_instance_usage(context, instance, "power_off.end")
|
||||
|
||||
@@ -842,6 +834,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
self._instance_update(context,
|
||||
instance_uuid,
|
||||
power_state=current_power_state,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
task_state=None)
|
||||
self._notify_about_instance_usage(context, instance, "power_on.end")
|
||||
|
||||
@@ -2579,7 +2572,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
"'%(name)s' which is marked as "
|
||||
"DELETED but still present on host."),
|
||||
locals(), instance=instance)
|
||||
self._shutdown_instance(context, instance, 'Terminating')
|
||||
self._shutdown_instance(context, instance)
|
||||
self._cleanup_volumes(context, instance['uuid'])
|
||||
else:
|
||||
raise Exception(_("Unrecognized value '%(action)s'"
|
||||
|
||||
@@ -1968,9 +1968,10 @@ class CloudTestCase(test.TestCase):
|
||||
self.assertTrue(result)
|
||||
|
||||
vol = db.volume_get(self.context, vol1['id'])
|
||||
self._assert_volume_detached(vol)
|
||||
self._assert_volume_attached(vol, instance_uuid, '/dev/vdb')
|
||||
|
||||
vol = db.volume_get(self.context, vol2['id'])
|
||||
self._assert_volume_detached(vol)
|
||||
self._assert_volume_attached(vol, instance_uuid, '/dev/vdc')
|
||||
|
||||
self.cloud.start_instances(self.context, [ec2_instance_id])
|
||||
vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
|
||||
@@ -2039,9 +2040,8 @@ class CloudTestCase(test.TestCase):
|
||||
result = self.cloud.stop_instances(self.context, [ec2_instance_id])
|
||||
self.assertTrue(result)
|
||||
|
||||
for vol_id in (vol1['id'], vol2['id']):
|
||||
vol = db.volume_get(self.context, vol_id)
|
||||
self._assert_volume_detached(vol)
|
||||
vol = db.volume_get(self.context, vol2['id'])
|
||||
self._assert_volume_attached(vol, instance_uuid, '/dev/vdc')
|
||||
|
||||
self.cloud.start_instances(self.context, [ec2_instance_id])
|
||||
vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
|
||||
|
||||
@@ -445,7 +445,7 @@ class ComputeTestCase(BaseTestCase):
|
||||
def fake_driver_power_on(self, instance):
|
||||
called['power_on'] = True
|
||||
|
||||
self.stubs.Set(nova.virt.driver.ComputeDriver, 'power_on',
|
||||
self.stubs.Set(nova.virt.fake.FakeDriver, 'power_on',
|
||||
fake_driver_power_on)
|
||||
|
||||
instance = self._create_fake_instance()
|
||||
@@ -463,7 +463,7 @@ class ComputeTestCase(BaseTestCase):
|
||||
def fake_driver_power_off(self, instance):
|
||||
called['power_off'] = True
|
||||
|
||||
self.stubs.Set(nova.virt.driver.ComputeDriver, 'power_off',
|
||||
self.stubs.Set(nova.virt.fake.FakeDriver, 'power_off',
|
||||
fake_driver_power_off)
|
||||
|
||||
instance = self._create_fake_instance()
|
||||
@@ -1700,8 +1700,8 @@ class ComputeTestCase(BaseTestCase):
|
||||
).AndReturn([instance])
|
||||
|
||||
self.mox.StubOutWithMock(self.compute, "_shutdown_instance")
|
||||
self.compute._shutdown_instance(admin_context, instance,
|
||||
'Terminating').AndReturn(None)
|
||||
self.compute._shutdown_instance(admin_context,
|
||||
instance).AndReturn(None)
|
||||
|
||||
self.mox.StubOutWithMock(self.compute, "_cleanup_volumes")
|
||||
self.compute._cleanup_volumes(admin_context,
|
||||
@@ -2256,13 +2256,8 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
check_state(instance_uuid, power_state.NOSTATE, vm_states.SHUTOFF,
|
||||
None)
|
||||
|
||||
start_check_state(instance_uuid,
|
||||
power_state.NOSTATE, vm_states.SHUTOFF, None)
|
||||
|
||||
db.instance_update(self.context, instance_uuid,
|
||||
{'shutdown_terminate': False})
|
||||
start_check_state(instance_uuid, power_state.NOSTATE,
|
||||
vm_states.STOPPED, task_states.STARTING)
|
||||
vm_states.SHUTOFF, task_states.STARTING)
|
||||
|
||||
db.instance_destroy(self.context, instance['id'])
|
||||
|
||||
@@ -2338,6 +2333,10 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
instance = db.instance_get_by_uuid(self.context, instance_uuid)
|
||||
self.assertEqual(instance['task_state'], task_states.POWERING_OFF)
|
||||
|
||||
# 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})
|
||||
|
||||
self.compute_api.force_delete(self.context, instance)
|
||||
|
||||
instance = db.instance_get_by_uuid(self.context, instance_uuid)
|
||||
@@ -2402,9 +2401,8 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
|
||||
self.compute.pause_instance(self.context, instance_uuid)
|
||||
# set the state that the instance gets when pause finishes
|
||||
db.instance_update(self.context, instance['uuid'],
|
||||
instance = db.instance_update(self.context, instance['uuid'],
|
||||
{'vm_state': vm_states.PAUSED})
|
||||
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
|
||||
|
||||
self.compute_api.unpause(self.context, instance)
|
||||
|
||||
@@ -2425,6 +2423,10 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
instance = db.instance_get_by_uuid(self.context, instance_uuid)
|
||||
self.assertEqual(instance['task_state'], task_states.POWERING_OFF)
|
||||
|
||||
# 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})
|
||||
|
||||
self.compute_api.restore(self.context, instance)
|
||||
|
||||
instance = db.instance_get_by_uuid(self.context, instance_uuid)
|
||||
@@ -2766,10 +2768,9 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
{'instance_uuid': instance['uuid'],
|
||||
'status': 'finished'})
|
||||
# set the state that the instance gets when resize finishes
|
||||
db.instance_update(self.context, instance['uuid'],
|
||||
{'task_state': task_states.RESIZE_VERIFY,
|
||||
'vm_state': vm_states.ACTIVE})
|
||||
instance = db.instance_get_by_uuid(context, instance['uuid'])
|
||||
instance = db.instance_update(self.context, instance['uuid'],
|
||||
{'task_state': task_states.RESIZE_VERIFY,
|
||||
'vm_state': vm_states.ACTIVE})
|
||||
|
||||
self.compute_api.confirm_resize(context, instance)
|
||||
self.compute.terminate_instance(context, instance['uuid'])
|
||||
@@ -2787,10 +2788,9 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
{'instance_uuid': instance['uuid'],
|
||||
'status': 'finished'})
|
||||
# set the state that the instance gets when resize finishes
|
||||
db.instance_update(self.context, instance['uuid'],
|
||||
{'task_state': task_states.RESIZE_VERIFY,
|
||||
'vm_state': vm_states.ACTIVE})
|
||||
instance = db.instance_get_by_uuid(context, instance['uuid'])
|
||||
instance = db.instance_update(self.context, instance['uuid'],
|
||||
{'task_state': task_states.RESIZE_VERIFY,
|
||||
'vm_state': vm_states.ACTIVE})
|
||||
|
||||
self.compute_api.revert_resize(context, instance)
|
||||
|
||||
|
||||
@@ -176,6 +176,22 @@ class _VirtDriverTestCase(test.TestCase):
|
||||
self.ctxt, instance_ref, 'dest_host', instance_type_ref,
|
||||
network_info)
|
||||
|
||||
@catch_notimplementederror
|
||||
def test_power_off(self):
|
||||
instance_ref, network_info = self._get_running_instance()
|
||||
self.connection.power_off(instance_ref)
|
||||
|
||||
@catch_notimplementederror
|
||||
def test_test_power_on_running(self):
|
||||
instance_ref, network_info = self._get_running_instance()
|
||||
self.connection.power_on(instance_ref)
|
||||
|
||||
@catch_notimplementederror
|
||||
def test_test_power_on_powered_off(self):
|
||||
instance_ref, network_info = self._get_running_instance()
|
||||
self.connection.power_off(instance_ref)
|
||||
self.connection.power_on(instance_ref)
|
||||
|
||||
@catch_notimplementederror
|
||||
def test_pause(self):
|
||||
instance_ref, network_info = self._get_running_instance()
|
||||
|
||||
@@ -139,6 +139,12 @@ class FakeDriver(driver.ComputeDriver):
|
||||
def finish_revert_migration(self, instance, network_info):
|
||||
pass
|
||||
|
||||
def power_off(self, instance):
|
||||
pass
|
||||
|
||||
def power_on(self, instance):
|
||||
pass
|
||||
|
||||
def pause(self, instance):
|
||||
pass
|
||||
|
||||
|
||||
@@ -718,7 +718,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
finally:
|
||||
libvirt_utils.delete_snapshot(disk_path, snapshot_name)
|
||||
if state == power_state.RUNNING:
|
||||
virt_dom.create()
|
||||
self._create_domain(domain=virt_dom)
|
||||
|
||||
# Upload that image to the image service
|
||||
with libvirt_utils.file_open(out_path) as image_file:
|
||||
@@ -770,7 +770,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
power_state.CRASHED]:
|
||||
LOG.info(_("Instance shutdown successfully."),
|
||||
instance=instance)
|
||||
dom.create()
|
||||
self._create_domain(domain=dom)
|
||||
timer = utils.LoopingCall(self._wait_for_running, instance)
|
||||
return timer.start(interval=0.5)
|
||||
greenthread.sleep(1)
|
||||
@@ -808,26 +808,39 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
@exception.wrap_exception()
|
||||
def pause(self, instance):
|
||||
"""Pause VM instance"""
|
||||
dom = self._lookup_by_name(instance.name)
|
||||
dom = self._lookup_by_name(instance['name'])
|
||||
dom.suspend()
|
||||
|
||||
@exception.wrap_exception()
|
||||
def unpause(self, instance):
|
||||
"""Unpause paused VM instance"""
|
||||
dom = self._lookup_by_name(instance.name)
|
||||
dom = self._lookup_by_name(instance['name'])
|
||||
dom.resume()
|
||||
|
||||
@exception.wrap_exception()
|
||||
def power_off(self, instance):
|
||||
"""Power off the specified instance"""
|
||||
self._destroy(instance)
|
||||
|
||||
@exception.wrap_exception()
|
||||
def power_on(self, instance):
|
||||
"""Power on the specified instance"""
|
||||
dom = self._lookup_by_name(instance['name'])
|
||||
self._create_domain(domain=dom)
|
||||
timer = utils.LoopingCall(self._wait_for_running, instance)
|
||||
return timer.start(interval=0.5)
|
||||
|
||||
@exception.wrap_exception()
|
||||
def suspend(self, instance):
|
||||
"""Suspend the specified instance"""
|
||||
dom = self._lookup_by_name(instance.name)
|
||||
dom = self._lookup_by_name(instance['name'])
|
||||
dom.managedSave(0)
|
||||
|
||||
@exception.wrap_exception()
|
||||
def resume(self, instance):
|
||||
"""resume the specified instance"""
|
||||
dom = self._lookup_by_name(instance.name)
|
||||
dom.create()
|
||||
dom = self._lookup_by_name(instance['name'])
|
||||
self._create_domain(domain=dom)
|
||||
|
||||
@exception.wrap_exception()
|
||||
def resume_state_on_host_boot(self, context, instance, network_info):
|
||||
@@ -2262,7 +2275,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
post_method(ctxt, instance_ref, dest, block_migration)
|
||||
|
||||
timer.f = wait_for_live_migration
|
||||
timer.start(interval=0.5)
|
||||
return timer.start(interval=0.5)
|
||||
|
||||
def pre_live_migration(self, block_device_info):
|
||||
"""Preparation live migration.
|
||||
@@ -2514,7 +2527,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
disk_info_text = self.get_instance_disk_info(instance['name'])
|
||||
disk_info = jsonutils.loads(disk_info_text)
|
||||
|
||||
self._destroy(instance)
|
||||
self.power_off(instance)
|
||||
|
||||
# copy disks to destination
|
||||
# if disk type is qcow2, convert to raw then send to dest.
|
||||
|
||||
Reference in New Issue
Block a user