merge from upstream and fix conflicts

This commit is contained in:
termie 2010-12-28 13:43:35 -08:00
commit 2ed57981b1
13 changed files with 183 additions and 14 deletions

View File

@ -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'},

View File

@ -46,7 +46,8 @@ def _entity_detail(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()

View File

@ -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)

View File

@ -296,6 +296,39 @@ 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."""

View File

@ -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]

View File

@ -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={},

View File

@ -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()

View File

@ -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.

View File

@ -279,6 +279,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)

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -21,6 +21,7 @@ function process_option {
-V|--virtual-env) let always_venv=1; let never_venv=0;;
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
-f|--force) let force=1;;
*) noseargs="$noseargs $1"
esac
}
@ -29,16 +30,19 @@ with_venv=tools/with_venv.sh
always_venv=0
never_venv=0
force=0
noseargs=
for arg in "$@"; do
process_option $arg
done
NOSETESTS="python run_tests.py $noseargs"
if [ $never_venv -eq 1 ]; then
# Just run the test suites in current environment
rm -f nova.sqlite
python run_tests.py $@ 2> run_tests.err.log
$NOSETESTS 2> run_tests.err.log
exit
fi
@ -50,7 +54,7 @@ fi
if [ -e ${venv} ]; then
${with_venv} rm -f nova.sqlite
${with_venv} python run_tests.py $@ 2> run_tests.err.log
${with_venv} $NOSETESTS 2> run_tests.err.log
else
if [ $always_venv -eq 1 ]; then
# Automatically install the virtualenv
@ -63,11 +67,10 @@ else
python tools/install_venv.py
else
rm -f nova.sqlite
#nosetests -v
python run_tests.py 2> run_tests.err.log
$NOSETESTS 2> run_tests.err.log
exit
fi
fi
${with_venv} rm -f nova.sqlite
${with_venv} python run_tests.py $@ 2> run_tests.err.log
${with_venv} $NOSETESTS 2> run_tests.err.log
fi