added suspend and resume

This commit is contained in:
Trey Morris
2010-12-20 22:55:11 +00:00
parent 086f2d87be
commit aded4faba9
10 changed files with 157 additions and 4 deletions

View File

@@ -176,6 +176,8 @@ class APIRouter(wsgi.Router):
logging.debug("Including admin operations in API.") logging.debug("Including admin operations in API.")
server_members['pause'] = 'POST' server_members['pause'] = 'POST'
server_members['unpause'] = 'POST' server_members['unpause'] = 'POST'
server_members['suspend'] = 'POST'
server_members['resume'] = 'POST'
mapper.resource("server", "servers", controller=servers.Controller(), mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'}, collection={'detail': 'GET'},

View File

@@ -195,3 +195,25 @@ class Controller(wsgi.Controller):
logging.error("Compute.api::unpause %s", readable) logging.error("Compute.api::unpause %s", readable)
return faults.Fault(exc.HTTPUnprocessableEntity()) return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted() 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()

View File

@@ -298,6 +298,24 @@ class ComputeAPI(base.Base):
{"method": "unpause_instance", {"method": "unpause_instance",
"args": {"instance_id": instance['id']}}) "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): def rescue(self, context, instance_id):
"""Rescue the given instance.""" """Rescue the given instance."""
instance = self.db.instance_get_by_internal_id(context, instance_id) instance = self.db.instance_get_by_internal_id(context, instance_id)

View File

@@ -227,6 +227,38 @@ class ComputeManager(manager.Manager):
instance_id, instance_id,
result)) 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 @exception.wrap_exception
def get_console_output(self, context, instance_id): def get_console_output(self, context, instance_id):
"""Send the console output for an instance.""" """Send the console output for an instance."""

View File

@@ -88,9 +88,13 @@ class ServersTest(unittest.TestCase):
self.stubs.Set(nova.db.api, 'instance_get_floating_address', self.stubs.Set(nova.db.api, 'instance_get_floating_address',
instance_address) instance_address)
self.stubs.Set(nova.compute.api.ComputeAPI, 'pause', self.stubs.Set(nova.compute.api.ComputeAPI, 'pause',
fake_compute_api) fake_compute_api)
self.stubs.Set(nova.compute.api.ComputeAPI, 'unpause', 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 self.allow_admin = FLAGS.allow_admin_api
def tearDown(self): def tearDown(self):
@@ -246,6 +250,30 @@ class ServersTest(unittest.TestCase):
res = req.get_response(nova.api.API('os')) res = req.get_response(nova.api.API('os'))
self.assertEqual(res.status_int, 202) 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): def test_server_reboot(self):
body = dict(server=dict( body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={}, name='server_test', imageId=2, flavorId=2, metadata={},

View File

@@ -135,6 +135,14 @@ class ComputeTestCase(test.TestCase):
self.compute.unpause_instance(self.context, instance_id) self.compute.unpause_instance(self.context, instance_id)
self.compute.terminate_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): def test_reboot(self):
"""Ensure instance can be rebooted""" """Ensure instance can be rebooted"""
instance_id = self._create_instance() instance_id = self._create_instance()

View File

@@ -142,6 +142,14 @@ class FakeConnection(object):
""" """
pass 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): def destroy(self, instance):
""" """
Destroy (shutdown and delete) the specified instance. Destroy (shutdown and delete) the specified instance.

View File

@@ -269,6 +269,14 @@ class LibvirtConnection(object):
def unpause(self, instance, callback): def unpause(self, instance, callback):
raise exception.APIError("unpause not supported for libvirt.") 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 @exception.wrap_exception
def rescue(self, instance): def rescue(self, instance):
self.destroy(instance, False) self.destroy(instance, False)

View File

@@ -144,11 +144,29 @@ class VMOps(object):
task = self._session.call_xenapi('Async.VM.unpause', vm) task = self._session.call_xenapi('Async.VM.unpause', vm)
self._wait_with_callback(task, callback) self._wait_with_callback(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)
self._wait_with_callback(task, callback)
def get_info(self, instance_id): def get_info(self, instance_id):
""" Return data about VM instance """ """ Return data about VM instance """
vm = VMHelper.lookup_blocking(self._session, instance_id) vm = VMHelper.lookup_blocking(self._session, instance_id)
if vm is None: if vm is None:
raise Exception('instance not present %s' % instance_id) raise Exception("get_info: instance not present %s" % instance_id)
rec = self._session.get_xenapi().VM.get_record(vm) rec = self._session.get_xenapi().VM.get_record(vm)
return VMHelper.compile_info(rec) return VMHelper.compile_info(rec)
@@ -156,7 +174,8 @@ class VMOps(object):
"""Return data about VM diagnostics""" """Return data about VM diagnostics"""
vm = VMHelper.lookup(self._session, instance_id) vm = VMHelper.lookup(self._session, instance_id)
if vm is None: if vm is None:
raise Exception("instance not present %s" % instance_id) raise Exception("get_diagnostics: instance not present %s" % \
instance_id)
rec = self._session.get_xenapi().VM.get_record(vm) rec = self._session.get_xenapi().VM.get_record(vm)
return VMHelper.compile_diagnostics(self._session, rec) return VMHelper.compile_diagnostics(self._session, rec)

View File

@@ -132,6 +132,14 @@ class XenAPIConnection(object):
""" Unpause paused VM instance """ """ Unpause paused VM instance """
self._vmops.unpause(instance, callback) 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): def get_info(self, instance_id):
""" Return data about VM instance """ """ Return data about VM instance """
return self._vmops.get_info(instance_id) return self._vmops.get_info(instance_id)