added suspend and resume
This commit is contained in:
@@ -176,6 +176,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'},
|
||||
|
||||
@@ -195,3 +195,25 @@ class Controller(wsgi.Controller):
|
||||
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()
|
||||
|
||||
@@ -298,6 +298,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)
|
||||
|
||||
@@ -227,6 +227,38 @@ class ComputeManager(manager.Manager):
|
||||
instance_id,
|
||||
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."""
|
||||
|
||||
@@ -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={},
|
||||
|
||||
@@ -135,6 +135,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()
|
||||
|
||||
@@ -142,6 +142,14 @@ 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.
|
||||
|
||||
@@ -269,6 +269,14 @@ class LibvirtConnection(object):
|
||||
def unpause(self, instance, callback):
|
||||
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)
|
||||
|
||||
@@ -144,11 +144,29 @@ class VMOps(object):
|
||||
task = self._session.call_xenapi('Async.VM.unpause', vm)
|
||||
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):
|
||||
""" Return data about VM instance """
|
||||
vm = VMHelper.lookup_blocking(self._session, instance_id)
|
||||
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)
|
||||
return VMHelper.compile_info(rec)
|
||||
|
||||
@@ -156,7 +174,8 @@ class VMOps(object):
|
||||
"""Return data about VM diagnostics"""
|
||||
vm = VMHelper.lookup(self._session, instance_id)
|
||||
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)
|
||||
return VMHelper.compile_diagnostics(self._session, rec)
|
||||
|
||||
|
||||
@@ -132,6 +132,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)
|
||||
|
||||
Reference in New Issue
Block a user