summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorEd Leafe <ed@leafe.com>2011-07-07 15:24:12 +0000
committerEd Leafe <ed@leafe.com>2011-07-07 15:24:12 +0000
commit6b83e1cd31f5e138af20fbd5c118d55da092eb35 (patch)
treee58c899c777f117a9c1415720f87973610b45502 /nova
parent9d29dc60d904f2c5037d03cead71933dc62777ff (diff)
downloadnova-6b83e1cd31f5e138af20fbd5c118d55da092eb35.tar.gz
nova-6b83e1cd31f5e138af20fbd5c118d55da092eb35.tar.xz
nova-6b83e1cd31f5e138af20fbd5c118d55da092eb35.zip
Added API and supporting code for rebooting or shutting down XenServer hosts.
Diffstat (limited to 'nova')
-rw-r--r--nova/api/openstack/contrib/hosts.py26
-rw-r--r--nova/compute/api.py6
-rw-r--r--nova/compute/manager.py6
-rw-r--r--nova/tests/test_hosts.py32
-rw-r--r--nova/virt/driver.py4
-rw-r--r--nova/virt/fake.py4
-rw-r--r--nova/virt/hyperv.py4
-rw-r--r--nova/virt/libvirt/connection.py4
-rw-r--r--nova/virt/vmwareapi_conn.py4
-rw-r--r--nova/virt/xenapi/vmops.py21
-rw-r--r--nova/virt/xenapi_conn.py4
11 files changed, 108 insertions, 7 deletions
diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py
index 55e57e1a4..cc71cadbd 100644
--- a/nova/api/openstack/contrib/hosts.py
+++ b/nova/api/openstack/contrib/hosts.py
@@ -78,6 +78,12 @@ class HostController(object):
else:
explanation = _("Invalid status: '%s'") % raw_val
raise webob.exc.HTTPBadRequest(explanation=explanation)
+ elif key == "power_state":
+ if val in ("reboot", "off", "on"):
+ return self._set_power_state(req, id, val)
+ else:
+ explanation = _("Invalid status: '%s'") % raw_val
+ raise webob.exc.HTTPBadRequest(explanation=explanation)
else:
explanation = _("Invalid update setting: '%s'") % raw_key
raise webob.exc.HTTPBadRequest(explanation=explanation)
@@ -89,8 +95,28 @@ class HostController(object):
LOG.audit(_("Setting host %(host)s to %(state)s.") % locals())
result = self.compute_api.set_host_enabled(context, host=host,
enabled=enabled)
+ if result not in ("enabled", "disabled"):
+ # An error message was returned
+ raise webob.exc.HTTPBadRequest(explanation=result)
return {"host": host, "status": result}
+ def _set_power_state(self, req, host, power_state):
+ """Turns the specified host on/off, or reboots the host."""
+ context = req.environ['nova.context']
+ if power_state == "on":
+ raise webob.exc.HTTPNotImplemented()
+ if power_state == "reboot":
+ msg = _("Rebooting host %(host)s")
+ else:
+ msg = _("Powering off host %(host)s.")
+ LOG.audit(msg % locals())
+ result = self.compute_api.set_power_state(context, host=host,
+ power_state=power_state)
+ if result != power_state:
+ # An error message was returned
+ raise webob.exc.HTTPBadRequest(explanation=result)
+ return {"host": host, "power_state": result}
+
class Hosts(extensions.ExtensionDescriptor):
def get_name(self):
diff --git a/nova/compute/api.py b/nova/compute/api.py
index b0eedcd64..71e11c5ea 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -917,6 +917,12 @@ class API(base.Base):
return self._call_compute_message("set_host_enabled", context,
instance_id=None, host=host, params={"enabled": enabled})
+ def set_power_state(self, context, host, power_state):
+ """Turns the specified host on/off, or reboots the host."""
+ return self._call_compute_message("set_power_state", context,
+ instance_id=None, host=host,
+ params={"power_state": power_state})
+
@scheduler_api.reroute_compute("diagnostics")
def get_diagnostics(self, context, instance_id):
"""Retrieve diagnostics for the given instance."""
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 91a604934..eb8e4df3c 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -881,6 +881,12 @@ class ComputeManager(manager.SchedulerDependentManager):
return self.driver.set_host_enabled(host, enabled)
@exception.wrap_exception
+ def set_power_state(self, context, instance_id=None, host=None,
+ power_state=None):
+ """Turns the specified host on/off, or reboots the host."""
+ return self.driver.set_power_state(host, power_state)
+
+ @exception.wrap_exception
def get_diagnostics(self, context, instance_id):
"""Retrieve diagnostics for an instance on this host."""
instance_ref = self.db.instance_get(context, instance_id)
diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py
index 548f81f8b..5a52e36e2 100644
--- a/nova/tests/test_hosts.py
+++ b/nova/tests/test_hosts.py
@@ -48,6 +48,16 @@ def stub_set_host_enabled(context, host, enabled):
return status
+def stub_set_power_state(context, host, power_state):
+ # We'll simulate success and failure by assuming
+ # that 'host_c1' always succeeds, and 'host_c2'
+ # always fails
+ if host == "host_c1":
+ return power_state
+ else:
+ return "fail"
+
+
class FakeRequest(object):
environ = {"nova.context": context.get_admin_context()}
@@ -62,6 +72,8 @@ class HostTestCase(test.TestCase):
self.stubs.Set(scheduler_api, 'get_host_list', stub_get_host_list)
self.stubs.Set(self.controller.compute_api, 'set_host_enabled',
stub_set_host_enabled)
+ self.stubs.Set(self.controller.compute_api, 'set_power_state',
+ stub_set_power_state)
def test_list_hosts(self):
"""Verify that the compute hosts are returned."""
@@ -87,15 +99,27 @@ class HostTestCase(test.TestCase):
result_c2 = self.controller.update(self.req, "host_c2", body=en_body)
self.assertEqual(result_c2["status"], "disabled")
+ def test_power_state(self):
+ en_body = {"power_state": "reboot"}
+ result_c1 = self.controller.update(self.req, "host_c1", body=en_body)
+ self.assertEqual(result_c1["power_state"], "reboot")
+ result_c2 = self.controller.update(self.req, "host_c2", body=en_body)
+ self.assertEqual(result_c2["power_state"], "fail")
+
+ def test_bad_power_state_value(self):
+ bad_body = {"power_state": "bad"}
+ result = self.controller.update(self.req, "host_c1", body=bad_body)
+ self.assertEqual(str(result.wrapped_exc)[:15], "400 Bad Request")
+
def test_bad_status_value(self):
bad_body = {"status": "bad"}
- self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
- self.req, "host_c1", body=bad_body)
+ result = self.controller.update(self.req, "host_c1", body=bad_body)
+ self.assertEqual(str(result.wrapped_exc)[:15], "400 Bad Request")
def test_bad_update_key(self):
bad_body = {"crazy": "bad"}
- self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
- self.req, "host_c1", body=bad_body)
+ result = self.controller.update(self.req, "host_c1", body=bad_body)
+ self.assertEqual(str(result.wrapped_exc)[:15], "400 Bad Request")
def test_bad_host(self):
self.assertRaises(exception.HostNotFound, self.controller.update,
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index 3c4a073bf..eed32d8d6 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -253,3 +253,7 @@ class ComputeDriver(object):
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
raise NotImplementedError()
+
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ raise NotImplementedError()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index ea0a59f21..0596079e8 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -518,3 +518,7 @@ class FakeConnection(driver.ComputeDriver):
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
pass
+
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ pass
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index 5c1dc772d..a438ff2e8 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -503,3 +503,7 @@ class HyperVConnection(driver.ComputeDriver):
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
pass
+
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ pass
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index b80a3daee..2a02e5a2d 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -1595,3 +1595,7 @@ class LibvirtConnection(driver.ComputeDriver):
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
pass
+
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ pass
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py
index d80e14931..0136225dd 100644
--- a/nova/virt/vmwareapi_conn.py
+++ b/nova/virt/vmwareapi_conn.py
@@ -194,6 +194,10 @@ class VMWareESXConnection(driver.ComputeDriver):
"""Sets the specified host's ability to accept new instances."""
pass
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ pass
+
class VMWareAPISession(object):
"""
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index cb96930c1..ec90ba9fe 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -936,10 +936,25 @@ class VMOps(object):
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
args = {"enabled": json.dumps(enabled)}
- json_resp = self._call_xenhost("set_host_enabled", args)
- resp = json.loads(json_resp)
+ xenapi_resp = self._call_xenhost("set_host_enabled", args)
+ try:
+ resp = json.loads(xenapi_resp)
+ except TypeError as e:
+ # Already logged; return the message
+ return xenapi_resp.details[-1]
return resp["status"]
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ args = {"power_state": power_state}
+ xenapi_resp = self._call_xenhost("set_power_state", args)
+ try:
+ resp = json.loads(xenapi_resp)
+ except TypeError as e:
+ # Already logged; return the message
+ return xenapi_resp.details[-1]
+ return resp["power_state"]
+
def _call_xenhost(self, method, arg_dict):
"""There will be several methods that will need this general
handling for interacting with the xenhost plugin, so this abstracts
@@ -953,7 +968,7 @@ class VMOps(object):
#args={"params": arg_dict})
ret = self._session.wait_for_task(task, task_id)
except self.XenAPI.Failure as e:
- ret = None
+ ret = e
LOG.error(_("The call to %(method)s returned an error: %(e)s.")
% locals())
return ret
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index ec8c44c1c..0b88e0999 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -340,6 +340,10 @@ class XenAPIConnection(driver.ComputeDriver):
"""Sets the specified host's ability to accept new instances."""
return self._vmops.set_host_enabled(host, enabled)
+ def set_power_state(self, host, power_state):
+ """Reboots, shuts down or starts up the host."""
+ return self._vmops.set_power_state(host, power_state)
+
class XenAPISession(object):
"""The session to invoke XenAPI SDK calls"""