diff options
| author | Cerberus <matt.dietz@rackspace.com> | 2010-12-28 15:49:36 -0600 |
|---|---|---|
| committer | Cerberus <matt.dietz@rackspace.com> | 2010-12-28 15:49:36 -0600 |
| commit | 79abcb065a07ca342cee6b843960dc877300c750 (patch) | |
| tree | 474ed7125d6136bd0fd1db0f85afb5219b2d425a /nova | |
| parent | 902df6eb4968743dd451e54cde27ce88fc83ddaa (diff) | |
| parent | 675ca7c5f38af0fa1150936e881482aa20fdaa45 (diff) | |
Merge from trunk
Diffstat (limited to 'nova')
| -rw-r--r-- | nova/api/openstack/__init__.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/servers.py | 29 | ||||
| -rw-r--r-- | nova/compute/api.py | 18 | ||||
| -rw-r--r-- | nova/compute/manager.py | 33 | ||||
| -rw-r--r-- | nova/compute/power_state.py | 4 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 32 | ||||
| -rw-r--r-- | nova/tests/test_compute.py | 12 | ||||
| -rw-r--r-- | nova/utils.py | 3 | ||||
| -rw-r--r-- | nova/virt/fake.py | 12 | ||||
| -rw-r--r-- | nova/virt/libvirt_conn.py | 8 | ||||
| -rw-r--r-- | nova/virt/xenapi/vm_utils.py | 6 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 20 | ||||
| -rw-r--r-- | nova/virt/xenapi_conn.py | 8 |
13 files changed, 177 insertions, 10 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 1f78820c8..66aceee2d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -93,6 +93,8 @@ class APIRouter(wsgi.Router): logging.debug("Including admin operations in API.") server_members['pause'] = 'POST' server_members['unpause'] = 'POST' + server_members['suspend'] = 'POST' + server_members['resume'] = 'POST' mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index d18889563..2232d24b2 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -43,7 +43,8 @@ def _translate_detail_keys(inst): power_state.NOSTATE: 'build', power_state.RUNNING: 'active', power_state.BLOCKED: 'active', - power_state.PAUSED: 'suspended', + power_state.SUSPENDED: 'suspended', + power_state.PAUSED: 'error', power_state.SHUTDOWN: 'active', power_state.SHUTOFF: 'active', power_state.CRASHED: 'error'} @@ -182,7 +183,7 @@ class Controller(wsgi.Controller): self.compute_api.pause(ctxt, id) except: readable = traceback.format_exc() - logging.error("Compute.api::pause %s", readable) + logging.error(_("Compute.api::pause %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() @@ -193,6 +194,28 @@ class Controller(wsgi.Controller): self.compute_api.unpause(ctxt, id) except: readable = traceback.format_exc() - logging.error("Compute.api::unpause %s", readable) + logging.error(_("Compute.api::unpause %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def suspend(self, req, id): + """permit admins to suspend the server""" + context = req.environ['nova.context'] + try: + self.compute_api.suspend(context, id) + except: + readable = traceback.format_exc() + logging.error(_("compute.api::suspend %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def resume(self, req, id): + """permit admins to resume the server from suspend""" + context = req.environ['nova.context'] + try: + self.compute_api.resume(context, id) + except: + readable = traceback.format_exc() + logging.error(_("compute.api::resume %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() diff --git a/nova/compute/api.py b/nova/compute/api.py index a3245940e..a47703461 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -284,6 +284,24 @@ class ComputeAPI(base.Base): {"method": "unpause_instance", "args": {"instance_id": instance['id']}}) + def suspend(self, context, instance_id): + """suspend the instance with instance_id""" + instance = self.db.instance_get_by_internal_id(context, instance_id) + host = instance['host'] + rpc.cast(context, + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "suspend_instance", + "args": {"instance_id": instance['id']}}) + + def resume(self, context, instance_id): + """resume the instance with instance_id""" + instance = self.db.instance_get_by_internal_id(context, instance_id) + host = instance['host'] + rpc.cast(context, + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "resume_instance", + "args": {"instance_id": instance['id']}}) + def rescue(self, context, instance_id): """Rescue the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ff8202cca..70b175e7c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -297,6 +297,39 @@ class ComputeManager(manager.Manager): result)) @exception.wrap_exception + def suspend_instance(self, context, instance_id): + """suspend the instance with instance_id""" + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + + logging.debug(_('instance %s: suspending'), + instance_ref['internal_id']) + self.db.instance_set_state(context, instance_id, + power_state.NOSTATE, + 'suspending') + self.driver.suspend(instance_ref, + lambda result: self._update_state_callback(self, + context, + instance_id, + result)) + + @exception.wrap_exception + def resume_instance(self, context, instance_id): + """resume the suspended instance with instance_id""" + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + + logging.debug(_('instance %s: resuming'), instance_ref['internal_id']) + self.db.instance_set_state(context, instance_id, + power_state.NOSTATE, + 'resuming') + self.driver.resume(instance_ref, + lambda result: self._update_state_callback(self, + context, + instance_id, + result)) + + @exception.wrap_exception def get_console_output(self, context, instance_id): """Send the console output for an instance.""" context = context.elevated() diff --git a/nova/compute/power_state.py b/nova/compute/power_state.py index cefdf2d9e..37039d2ec 100644 --- a/nova/compute/power_state.py +++ b/nova/compute/power_state.py @@ -26,6 +26,7 @@ PAUSED = 0x03 SHUTDOWN = 0x04 SHUTOFF = 0x05 CRASHED = 0x06 +SUSPENDED = 0x07 def name(code): @@ -36,5 +37,6 @@ def name(code): PAUSED: 'paused', SHUTDOWN: 'shutdown', SHUTOFF: 'shutdown', - CRASHED: 'crashed'} + CRASHED: 'crashed', + SUSPENDED: 'suspended'} return d[code] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3820f5f27..5d23db588 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -88,9 +88,13 @@ class ServersTest(unittest.TestCase): self.stubs.Set(nova.db.api, 'instance_get_floating_address', instance_address) self.stubs.Set(nova.compute.api.ComputeAPI, 'pause', - fake_compute_api) + fake_compute_api) self.stubs.Set(nova.compute.api.ComputeAPI, 'unpause', - fake_compute_api) + fake_compute_api) + self.stubs.Set(nova.compute.api.ComputeAPI, 'suspend', + fake_compute_api) + self.stubs.Set(nova.compute.api.ComputeAPI, 'resume', + fake_compute_api) self.allow_admin = FLAGS.allow_admin_api def tearDown(self): @@ -246,6 +250,30 @@ class ServersTest(unittest.TestCase): res = req.get_response(nova.api.API('os')) self.assertEqual(res.status_int, 202) + def test_server_suspend(self): + FLAGS.allow_admin_api = True + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality={})) + req = webob.Request.blank('/v1.0/servers/1/suspend') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + res = req.get_response(nova.api.API('os')) + self.assertEqual(res.status_int, 202) + + def test_server_resume(self): + FLAGS.allow_admin_api = True + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality={})) + req = webob.Request.blank('/v1.0/servers/1/resume') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + res = req.get_response(nova.api.API('os')) + self.assertEqual(res.status_int, 202) + def test_server_reboot(self): body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 348bb3351..bcb8a1526 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -101,13 +101,13 @@ class ComputeTestCase(test.TestCase): self.compute.run_instance(self.context, instance_id) instances = db.instance_get_all(context.get_admin_context()) - logging.info("Running instances: %s", instances) + logging.info(_("Running instances: %s"), instances) self.assertEqual(len(instances), 1) self.compute.terminate_instance(self.context, instance_id) instances = db.instance_get_all(context.get_admin_context()) - logging.info("After terminating instances: %s", instances) + logging.info(_("After terminating instances: %s"), instances) self.assertEqual(len(instances), 0) def test_run_terminate_timestamps(self): @@ -136,6 +136,14 @@ class ComputeTestCase(test.TestCase): self.compute.unpause_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id) + def test_suspend(self): + """ensure instance can be suspended""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.compute.suspend_instance(self.context, instance_id) + self.compute.resume_instance(self.context, instance_id) + self.compute.terminate_instance(self.context, instance_id) + def test_reboot(self): """Ensure instance can be rebooted""" instance_id = self._create_instance() diff --git a/nova/utils.py b/nova/utils.py index b9045a50c..15112faa2 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -48,7 +48,8 @@ def import_class(import_str): try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) - except (ImportError, ValueError, AttributeError): + except (ImportError, ValueError, AttributeError), exc: + logging.debug(_('Inner Exception: %s'), exc) raise exception.NotFound(_('Class %s cannot be found') % class_str) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 238acf798..706888b0d 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -148,6 +148,18 @@ class FakeConnection(object): """ pass + def suspend(self, instance, callback): + """ + suspend the specified instance + """ + pass + + def resume(self, instance, callback): + """ + resume the specified instance + """ + pass + def destroy(self, instance): """ Destroy (shutdown and delete) the specified instance. diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 67b9fc47e..65cf65098 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -280,6 +280,14 @@ class LibvirtConnection(object): raise exception.APIError("unpause not supported for libvirt.") @exception.wrap_exception + def suspend(self, instance, callback): + raise exception.APIError("suspend not supported for libvirt") + + @exception.wrap_exception + def resume(self, instance, callback): + raise exception.APIError("resume not supported for libvirt") + + @exception.wrap_exception def rescue(self, instance): self.destroy(instance, False) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2f02f4677..47fb6db53 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -39,7 +39,7 @@ XENAPI_POWER_STATE = { 'Halted': power_state.SHUTDOWN, 'Running': power_state.RUNNING, 'Paused': power_state.PAUSED, - 'Suspended': power_state.SHUTDOWN, # FIXME + 'Suspended': power_state.SUSPENDED, 'Crashed': power_state.CRASHED} @@ -283,6 +283,10 @@ class VMHelper(HelperBase): @classmethod def compile_info(cls, record): """Fill record with VM status information""" + logging.info(_("(VM_UTILS) xenserver vm state -> |%s|"), + record['power_state']) + logging.info(_("(VM_UTILS) xenapi power_state -> |%s|"), + XENAPI_POWER_STATE[record['power_state']]) return {'state': XENAPI_POWER_STATE[record['power_state']], 'max_mem': long(record['memory_static_max']) >> 10, 'mem': long(record['memory_dynamic_max']) >> 10, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b1de73641..ba502ffa2 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -188,6 +188,26 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.unpause', vm) self._wait_with_callback(instance.id, task, callback) + def suspend(self, instance, callback): + """suspend the specified instance""" + instance_name = instance.name + vm = VMHelper.lookup(self._session, instance_name) + if vm is None: + raise Exception(_("suspend: instance not present %s") % + instance_name) + task = self._session.call_xenapi('Async.VM.suspend', vm) + self._wait_with_callback(task, callback) + + def resume(self, instance, callback): + """resume the specified instance""" + instance_name = instance.name + vm = VMHelper.lookup(self._session, instance_name) + if vm is None: + raise Exception(_("resume: instance not present %s") % + instance_name) + task = self._session.call_xenapi('Async.VM.resume', vm, False, True) + self._wait_with_callback(task, callback) + def get_info(self, instance_id): """Return data about VM instance""" vm = VMHelper.lookup(self._session, instance_id) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 11c66c974..7f03d6c2b 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -147,6 +147,14 @@ class XenAPIConnection(object): """Unpause paused VM instance""" self._vmops.unpause(instance, callback) + def suspend(self, instance, callback): + """suspend the specified instance""" + self._vmops.suspend(instance, callback) + + def resume(self, instance, callback): + """resume the specified instance""" + self._vmops.resume(instance, callback) + def get_info(self, instance_id): """Return data about VM instance""" return self._vmops.get_info(instance_id) |
