summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark McLoughlin <markmc@redhat.com>2012-12-05 23:13:37 +0000
committerMark McLoughlin <markmc@redhat.com>2012-12-05 23:13:37 +0000
commit1dc6c806b32044420bf17d3076dd712185b7ac07 (patch)
tree9eea9c6755ea2fee73616fd21eebcdd6e088d01b
parent2a33f8d8cefdbde50f89e6d4084d513eadf3a97a (diff)
downloadnova-1dc6c806b32044420bf17d3076dd712185b7ac07.tar.gz
nova-1dc6c806b32044420bf17d3076dd712185b7ac07.tar.xz
nova-1dc6c806b32044420bf17d3076dd712185b7ac07.zip
Fix handling of unimplemented host actions
Fixes bug #1060884 Host actions like enable/disable, maintenance mode and power management are not implemented in most virt drivers. In the case of set_host_enabled() in the libvirt driver, we have a no-op implementation. In other cases we have an implementation which raises NotImplementedError. Since the default implementation is to do this, we should just remove the redundant methods from the drivers which don't actually implement these actions. This ensures NotImplementedError is consistently raised. On the API side, rather than looking at the return value of these methods to determine whether they're implemented, we should just catch NotImplementedError and return a sensible error message. In order to do this, we need to ensure the NotImplementedError is properly deserialized from the RemoteError which will encapsualte it. The fix for bug #1086798 ensures this happens. Finally, add some tests to make sure we properly respond with 501 when the virt driver doesn't implement these actions. Change-Id: I2d4fc51a4b16d5230e61bde75915142ea450557d
-rw-r--r--nova/api/openstack/compute/contrib/hosts.py24
-rw-r--r--nova/openstack/common/rpc/__init__.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_hosts.py36
-rw-r--r--nova/virt/hyperv/driver.py4
-rw-r--r--nova/virt/libvirt/driver.py13
-rw-r--r--nova/virt/vmwareapi/driver.py13
6 files changed, 51 insertions, 40 deletions
diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py
index 0d2b68f11..7da596a78 100644
--- a/nova/api/openstack/compute/contrib/hosts.py
+++ b/nova/api/openstack/compute/contrib/hosts.py
@@ -176,9 +176,11 @@ class HostController(object):
context = req.environ['nova.context']
LOG.audit(_("Putting host %(host)s in maintenance "
"mode %(mode)s.") % locals())
- result = self.api.set_host_maintenance(context, host, mode)
- if result not in ("on_maintenance", "off_maintenance"):
- raise webob.exc.HTTPBadRequest(explanation=result)
+ try:
+ result = self.api.set_host_maintenance(context, host, mode)
+ except NotImplementedError:
+ msg = _("Virt driver does not implement host maintenance mode.")
+ raise webob.exc.HTTPNotImplemented(explanation=msg)
return {"host": host, "maintenance_mode": result}
def _set_enabled_status(self, req, host, enabled):
@@ -186,11 +188,12 @@ class HostController(object):
context = req.environ['nova.context']
state = "enabled" if enabled else "disabled"
LOG.audit(_("Setting host %(host)s to %(state)s.") % locals())
- result = self.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)
+ try:
+ result = self.api.set_host_enabled(context, host=host,
+ enabled=enabled)
+ except NotImplementedError:
+ msg = _("Virt driver does not implement host disabled status.")
+ raise webob.exc.HTTPNotImplemented(explanation=msg)
return {"host": host, "status": result}
def _host_power_action(self, req, host, action):
@@ -200,8 +203,9 @@ class HostController(object):
try:
result = self.api.host_power_action(context, host=host,
action=action)
- except NotImplementedError as e:
- raise webob.exc.HTTPBadRequest(explanation=e.msg)
+ except NotImplementedError:
+ msg = _("Virt driver does not implement host power management.")
+ raise webob.exc.HTTPNotImplemented(explanation=msg)
return {"host": host, "power_action": result}
@wsgi.serializers(xml=HostActionTemplate)
diff --git a/nova/openstack/common/rpc/__init__.py b/nova/openstack/common/rpc/__init__.py
index bf2b2e9e0..a223e8fde 100644
--- a/nova/openstack/common/rpc/__init__.py
+++ b/nova/openstack/common/rpc/__init__.py
@@ -50,6 +50,7 @@ rpc_opts = [
default=['nova.openstack.common.exception',
'nova.exception',
'cinder.exception',
+ 'exceptions',
],
help='Modules of exceptions that are permitted to be recreated'
'upon receiving exception data from an rpc call.'),
diff --git a/nova/tests/api/openstack/compute/contrib/test_hosts.py b/nova/tests/api/openstack/compute/contrib/test_hosts.py
index b91715190..8469c7eba 100644
--- a/nova/tests/api/openstack/compute/contrib/test_hosts.py
+++ b/nova/tests/api/openstack/compute/contrib/test_hosts.py
@@ -46,6 +46,8 @@ def stub_service_get_all(self, req):
def stub_set_host_enabled(context, host, enabled):
+ if host == "notimplemented":
+ raise NotImplementedError()
# We'll simulate success and failure by assuming
# that 'host_c1' always succeeds, and 'host_c2'
# always fails
@@ -55,6 +57,8 @@ def stub_set_host_enabled(context, host, enabled):
def stub_set_host_maintenance(context, host, mode):
+ if host == "notimplemented":
+ raise NotImplementedError()
# We'll simulate success and failure by assuming
# that 'host_c1' always succeeds, and 'host_c2'
# always fails
@@ -64,6 +68,8 @@ def stub_set_host_maintenance(context, host, mode):
def stub_host_power_action(context, host, action):
+ if host == "notimplemented":
+ raise NotImplementedError()
return action
@@ -153,6 +159,23 @@ class HostTestCase(test.TestCase):
self._test_host_update('host_c1', 'maintenance_mode',
'disable', 'off_maintenance')
+ def _test_host_update_notimpl(self, key, val):
+ def stub_service_get_all_notimpl(self, req):
+ return [{'host': 'notimplemented', 'topic': None,
+ 'availability_zone': None}]
+ self.stubs.Set(db, 'service_get_all',
+ stub_service_get_all_notimpl)
+ body = {key: val}
+ self.assertRaises(webob.exc.HTTPNotImplemented,
+ self.controller.update,
+ self.req, 'notimplemented', body=body)
+
+ def test_disable_host_notimpl(self):
+ self._test_host_update_notimpl('status', 'disable')
+
+ def test_enable_maintenance_notimpl(self):
+ self._test_host_update_notimpl('maintenance_mode', 'enable')
+
def test_host_startup(self):
result = self.controller.startup(self.req, "host_c1")
self.assertEqual(result["power_action"], "startup")
@@ -165,6 +188,19 @@ class HostTestCase(test.TestCase):
result = self.controller.reboot(self.req, "host_c1")
self.assertEqual(result["power_action"], "reboot")
+ def _test_host_power_action_notimpl(self, method):
+ self.assertRaises(webob.exc.HTTPNotImplemented,
+ method, self.req, "notimplemented")
+
+ def test_host_startup_notimpl(self):
+ self._test_host_power_action_notimpl(self.controller.startup)
+
+ def test_host_shutdown_notimpl(self):
+ self._test_host_power_action_notimpl(self.controller.shutdown)
+
+ def test_host_reboot_notimpl(self):
+ self._test_host_power_action_notimpl(self.controller.reboot)
+
def test_bad_status_value(self):
bad_body = {"status": "bad"}
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py
index 3b0539159..d1b9904c4 100644
--- a/nova/virt/hyperv/driver.py
+++ b/nova/virt/hyperv/driver.py
@@ -131,10 +131,6 @@ class HyperVDriver(driver.ComputeDriver):
def host_power_action(self, host, action):
return self._hostops.host_power_action(host, action)
- def set_host_enabled(self, host, enabled):
- """Sets the specified host's ability to accept new instances."""
- pass
-
def snapshot(self, context, instance, name):
self._snapshotops.snapshot(context, instance, name)
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 750ffb0a2..c3b6c4244 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -2819,19 +2819,6 @@ class LibvirtDriver(driver.ComputeDriver):
If 'refresh' is True, run update the stats first."""
return self.host_state.get_host_stats(refresh=refresh)
- def host_power_action(self, host, action):
- """Reboots, shuts down or powers up the host."""
- raise NotImplementedError()
-
- def host_maintenance_mode(self, host, mode):
- """Start/Stop host maintenance window. On start, it triggers
- guest VMs evacuation."""
- raise NotImplementedError()
-
- def set_host_enabled(self, host, enabled):
- """Sets the specified host's ability to accept new instances."""
- pass
-
def get_host_uptime(self, host):
"""Returns the result of calling "uptime"."""
#NOTE(dprince): host seems to be ignored for this call and in
diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py
index ee97416ec..f6aa91e85 100644
--- a/nova/virt/vmwareapi/driver.py
+++ b/nova/virt/vmwareapi/driver.py
@@ -199,19 +199,6 @@ class VMWareESXDriver(driver.ComputeDriver):
"""This method is supported only by libvirt."""
return
- def host_power_action(self, host, action):
- """Reboots, shuts down or powers up the host."""
- raise NotImplementedError()
-
- def host_maintenance_mode(self, host, mode):
- """Start/Stop host maintenance window. On start, it triggers
- guest VMs evacuation."""
- raise NotImplementedError()
-
- def set_host_enabled(self, host, enabled):
- """Sets the specified host's ability to accept new instances."""
- raise NotImplementedError()
-
def plug_vifs(self, instance, network_info):
"""Plug VIFs into networks."""
self._vmops.plug_vifs(instance, network_info)