added suspend and resume
This commit is contained in:
@@ -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'},
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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."""
|
||||||
|
|||||||
@@ -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={},
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user