From 6b83e1cd31f5e138af20fbd5c118d55da092eb35 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 7 Jul 2011 15:24:12 +0000 Subject: Added API and supporting code for rebooting or shutting down XenServer hosts. --- nova/api/openstack/contrib/hosts.py | 26 ++++++++++++++++++ nova/compute/api.py | 6 ++++ nova/compute/manager.py | 6 ++++ nova/tests/test_hosts.py | 32 +++++++++++++++++++--- nova/virt/driver.py | 4 +++ nova/virt/fake.py | 4 +++ nova/virt/hyperv.py | 4 +++ nova/virt/libvirt/connection.py | 4 +++ nova/virt/vmwareapi_conn.py | 4 +++ nova/virt/xenapi/vmops.py | 21 ++++++++++++-- nova/virt/xenapi_conn.py | 4 +++ .../xenserver/xenapi/etc/xapi.d/plugins/xenhost | 30 +++++++++++++++++++- 12 files changed, 137 insertions(+), 8 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 @@ -880,6 +880,12 @@ class ComputeManager(manager.SchedulerDependentManager): """Sets the specified host's ability to accept new instances.""" 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.""" 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""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index 292bbce12..0cf7de0ce 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -103,6 +103,33 @@ def set_host_enabled(self, arg_dict): return {"status": status} +@jsonify +def set_power_state(self, arg_dict): + """Reboots or powers off this host. Ideally, we would also like to be + able to power *on* a host, but right now this is not technically + feasible. + """ + power_state = arg_dict.get("power_state") + if power_state is None: + raise pluginlib.PluginError( + _("Missing 'power_state' argument to set_power_state")) + # Host must be disabled first +# result = _run_command("xe host-disable") +# if result: +# raise pluginlib.PluginError(result) +# # All running VMs must be shutdown +# result = _run_command("xe vm-shutdown --multiple power-state=running") +# if result: +# raise pluginlib.PluginError(result) +# cmds = {"reboot": "xe host-reboot", "on": "xe host-power-on", +# "off": "xe host-shutdown"} +# result = _run_command(cmds[power_state]) +# # Should be empty string +# if result: +# raise pluginlib.PluginError(result) + return {"power_state": power_state} + + @jsonify def host_data(self, arg_dict): """Runs the commands on the xenstore host to return the current status @@ -217,4 +244,5 @@ def cleanup(dct): if __name__ == "__main__": XenAPIPlugin.dispatch( {"host_data": host_data, - "set_host_enabled": set_host_enabled}) + "set_host_enabled": set_host_enabled, + "set_power_state": set_power_state}) -- cgit From 60a9763382ccd77735a75b6047c821477eab684e Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 7 Jul 2011 15:36:39 +0000 Subject: pep8 fixes --- nova/tests/test_hosts.py | 16 ++++++++-------- nova/virt/xenapi/vmops.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py index 5a52e36e2..417737638 100644 --- a/nova/tests/test_hosts.py +++ b/nova/tests/test_hosts.py @@ -103,23 +103,23 @@ class HostTestCase(test.TestCase): 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") + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + self.req, "host_c2", body=en_body) 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") + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + self.req, "host_c1", body=bad_body) def test_bad_status_value(self): bad_body = {"status": "bad"} - result = self.controller.update(self.req, "host_c1", body=bad_body) - self.assertEqual(str(result.wrapped_exc)[:15], "400 Bad Request") + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + self.req, "host_c1", body=bad_body) def test_bad_update_key(self): bad_body = {"crazy": "bad"} - result = self.controller.update(self.req, "host_c1", body=bad_body) - self.assertEqual(str(result.wrapped_exc)[:15], "400 Bad Request") + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + self.req, "host_c1", body=bad_body) def test_bad_host(self): self.assertRaises(exception.HostNotFound, self.controller.update, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ec90ba9fe..aec802eff 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -941,7 +941,7 @@ class VMOps(object): resp = json.loads(xenapi_resp) except TypeError as e: # Already logged; return the message - return xenapi_resp.details[-1] + return xenapi_resp.details[-1] return resp["status"] def set_power_state(self, host, power_state): @@ -952,7 +952,7 @@ class VMOps(object): resp = json.loads(xenapi_resp) except TypeError as e: # Already logged; return the message - return xenapi_resp.details[-1] + return xenapi_resp.details[-1] return resp["power_state"] def _call_xenhost(self, method, arg_dict): -- cgit From 85795ff1f8b6a0ff3de634828208d6debd91692f Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Mon, 1 Aug 2011 21:06:47 +0000 Subject: Added option for rebooting or shutting down a host. --- nova/api/openstack/contrib/hosts.py | 15 +++++++++++++ nova/compute/api.py | 5 +++++ nova/compute/manager.py | 8 ++++++- nova/tests/test_hosts.py | 9 ++++++++ nova/virt/driver.py | 4 ++++ nova/virt/fake.py | 4 ++++ nova/virt/hyperv.py | 4 ++++ nova/virt/libvirt/connection.py | 4 ++++ nova/virt/vmwareapi_conn.py | 4 ++++ nova/virt/xenapi/vmops.py | 8 +++++++ nova/virt/xenapi_conn.py | 4 ++++ .../xenserver/xenapi/etc/xapi.d/plugins/xenhost | 25 +++++++++++++++++++++- 12 files changed, 92 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index 55e57e1a4..b6a4bdb77 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 == "powerstate": + if val in ("reboot", "shutdown"): + return self._set_powerstate(req, id, val) + else: + explanation = _("Invalid powerstate: '%s'") % raw_val + raise webob.exc.HTTPBadRequest(explanation=explanation) else: explanation = _("Invalid update setting: '%s'") % raw_key raise webob.exc.HTTPBadRequest(explanation=explanation) @@ -91,6 +97,15 @@ class HostController(object): enabled=enabled) return {"host": host, "status": result} + def _set_powerstate(self, req, host, state): + """Reboots or shuts down the host.""" + context = req.environ['nova.context'] + LOG.audit(_("Changing powerstate of host %(host)s to %(state)s.") + % locals()) + result = self.compute_api.set_host_powerstate(context, host=host, + state=state) + return {"host": host, "powerstate": result} + class Hosts(extensions.ExtensionDescriptor): def get_name(self): diff --git a/nova/compute/api.py b/nova/compute/api.py index 8f7b3c3ef..bd17fdf31 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -998,6 +998,11 @@ class API(base.Base): return self._call_compute_message("set_host_enabled", context, instance_id=None, host=host, params={"enabled": enabled}) + def set_host_powerstate(self, context, host, state): + """Reboots or shuts down the host.""" + return self._call_compute_message("set_host_powerstate", context, + instance_id=None, host=host, params={"state": 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 a2d84cd76..d0f9a81f4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1,4 +1,4 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 +: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. @@ -927,6 +927,12 @@ class ComputeManager(manager.SchedulerDependentManager): instance_id, result)) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) + def set_host_powerstate(self, context, instance_id=None, host=None, + state=None): + """Reboots or shuts down the host.""" + return self.driver.set_host_powerstate(host, state) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def set_host_enabled(self, context, instance_id=None, host=None, enabled=None): diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py index 548f81f8b..ad057f429 100644 --- a/nova/tests/test_hosts.py +++ b/nova/tests/test_hosts.py @@ -48,6 +48,13 @@ def stub_set_host_enabled(context, host, enabled): return status +def stub_set_host_powerstate(context, host, state): + # We'll simulate success and failure by assuming + # that 'host_c1' always succeeds, and 'host_c2' + # always fails + return state if host == "host_c1" else "running" + + class FakeRequest(object): environ = {"nova.context": context.get_admin_context()} @@ -62,6 +69,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_host_powerstate', + stub_set_host_powerstate) def test_list_hosts(self): """Verify that the compute hosts are returned.""" diff --git a/nova/virt/driver.py b/nova/virt/driver.py index b219fb2cb..bbb17480d 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -251,6 +251,10 @@ class ComputeDriver(object): """Poll for rescued instances""" raise NotImplementedError() + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + raise NotImplementedError() + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 26bc421c0..6dc6552d7 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -515,6 +515,10 @@ class FakeConnection(driver.ComputeDriver): """Return fake Host Status of ram, disk, network.""" return self.host_status + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index c26fe108b..119def38b 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -498,6 +498,10 @@ class HyperVConnection(driver.ComputeDriver): """See xenapi_conn.py implementation.""" pass + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 17c328a83..ae1a16d44 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1583,6 +1583,10 @@ class LibvirtConnection(driver.ComputeDriver): """See xenapi_conn.py implementation.""" pass + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index ce57847b2..af547821f 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -190,6 +190,10 @@ class VMWareESXConnection(driver.ComputeDriver): """This method is supported only by libvirt.""" return + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7e02e1def..8e57042f9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1023,6 +1023,14 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + args = {"state": json.dumps(state)} + methods = {"reboot": "host_reboot", "shutdown": "host_shutdown"} + json_resp = self._call_xenhost(methods[state], args) + resp = json.loads(json_resp) + return resp["powerstate"] + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" args = {"enabled": json.dumps(enabled)} diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index cc18ed83c..5ac837a17 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -334,6 +334,10 @@ class XenAPIConnection(driver.ComputeDriver): True, run the update first.""" return self.HostState.get_host_stats(refresh=refresh) + def set_host_powerstate(self, host, state): + """Reboots or shuts down the host.""" + return self._vmops.set_host_powerstate(host, state) + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" return self._vmops.set_host_enabled(host, enabled) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index 292bbce12..5a5122b4a 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -103,6 +103,27 @@ def set_host_enabled(self, arg_dict): return {"status": status} +def _powerstate(state): + host_uuid = _get_host_uuid() + cmd = "xe host-disable uuid=%(host_uuid)s" % locals() + _run_command(cmd) + cmd = "xe host-%(state)s uuid=%(host_uuid)s" % locals() + _run_command(cmd) + return {"powerstate": state} + + +@jsonify +def host_reboot(self, arg_dict): + """Reboots the host.""" + return _powerstate("reboot") + + +@jsonify +def host_shutdown(self, arg_dict): + """Reboots the host.""" + return _powerstate("shutdown") + + @jsonify def host_data(self, arg_dict): """Runs the commands on the xenstore host to return the current status @@ -217,4 +238,6 @@ def cleanup(dct): if __name__ == "__main__": XenAPIPlugin.dispatch( {"host_data": host_data, - "set_host_enabled": set_host_enabled}) + "set_host_enabled": set_host_enabled, + "host_shutdown": host_shutdown, + "host_reboot": host_reboot}) -- cgit From 07d89c29389fe8f2b9f3a398ab99566d151e8e92 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 2 Aug 2011 02:19:31 +0000 Subject: Added host shutdown/reboot conditioning. --- .../xenserver/xenapi/etc/xapi.d/plugins/xenhost | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index 5a5122b4a..873b4c58d 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -105,10 +105,20 @@ def set_host_enabled(self, arg_dict): def _powerstate(state): host_uuid = _get_host_uuid() - cmd = "xe host-disable uuid=%(host_uuid)s" % locals() - _run_command(cmd) - cmd = "xe host-%(state)s uuid=%(host_uuid)s" % locals() - _run_command(cmd) + # Host must be disabled first + result = _run_command("xe host-disable") + if result: + raise pluginlib.PluginError(result) + # All running VMs must be shutdown + result = _run_command("xe vm-shutdown --multiple power-state=running") + if result: + raise pluginlib.PluginError(result) + cmds = {"reboot": "xe host-reboot", "startup": "xe host-power-on", + "shutdown": "xe host-shutdown"} + result = _run_command(cmds[state]) + # Should be empty string + if result: + raise pluginlib.PluginError(result) return {"powerstate": state} @@ -124,6 +134,12 @@ def host_shutdown(self, arg_dict): return _powerstate("shutdown") +@jsonify +def host_start(self, arg_dict): + """Starts the host.""" + return _powerstate("startup") + + @jsonify def host_data(self, arg_dict): """Runs the commands on the xenstore host to return the current status @@ -240,4 +256,5 @@ if __name__ == "__main__": {"host_data": host_data, "set_host_enabled": set_host_enabled, "host_shutdown": host_shutdown, - "host_reboot": host_reboot}) + "host_reboot": host_reboot, + "host_start": host_start}) -- cgit From f06dee2b82bd658a57736d94974f431976085400 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 2 Aug 2011 19:02:40 +0000 Subject: Fixed several typos --- nova/api/openstack/contrib/hosts.py | 5 ++--- nova/compute/manager.py | 2 +- nova/tests/test_hosts.py | 2 +- tools/pip-requires | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index 94fba910c..2d9d494b9 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -85,7 +85,7 @@ class HostController(object): # 'startup' option to start up a host, but this is not # technically feasible now, as we run the host on the # XenServer box. - msg = _("Host startup on XenServer is not supported.")) + msg = _("Host startup on XenServer is not supported.") raise webob.exc.HTTPBadRequest(explanation=msg) elif val in ("reboot", "shutdown"): return self._set_powerstate(req, id, val) @@ -106,8 +106,7 @@ class HostController(object): return {"host": host, "status": result} def _set_powerstate(self, req, host, state): - """Reboots or shuts down the host. - """ + """Reboots or shuts down the host.""" context = req.environ['nova.context'] result = self.compute_api.set_host_powerstate(context, host=host, state=state) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d0f9a81f4..cd05f3f24 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1,4 +1,4 @@ -: tabstop=4 shiftwidth=4 softtabstop=4 +#: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py index ad057f429..9f54d5ec7 100644 --- a/nova/tests/test_hosts.py +++ b/nova/tests/test_hosts.py @@ -52,7 +52,7 @@ def stub_set_host_powerstate(context, host, state): # We'll simulate success and failure by assuming # that 'host_c1' always succeeds, and 'host_c2' # always fails - return state if host == "host_c1" else "running" + return state if host == "host_c1" else "running" class FakeRequest(object): diff --git a/tools/pip-requires b/tools/pip-requires index dec93c351..1e8f7e1d2 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -33,3 +33,4 @@ coverage nosexcover GitPython paramiko +xattr -- cgit From 0079cc3536811baf9ed6fa0cedbd5863c602644b Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 2 Aug 2011 19:50:54 +0000 Subject: Removed duplicate xattr from pip-requires --- tools/pip-requires | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/pip-requires b/tools/pip-requires index e6ef3996c..23e707034 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -33,4 +33,3 @@ coverage nosexcover GitPython paramiko -xattr -- cgit From 4c07cac5b0e79f3911fbcc392c3f9e7f07333968 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 2 Aug 2011 20:39:14 +0000 Subject: Fixed a missing space. --- nova/compute/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index b83c4831c..5ea7a1c01 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -995,7 +995,7 @@ class API(base.Base): return self._call_compute_message("set_host_enabled", context, instance_id=None, host=host, params={"enabled": enabled}) - def set_host_powerstate(self, context, host, state): + def set_host_powerstate(self, context, host, state): """Reboots or shuts down the host.""" return self._call_compute_message("set_host_powerstate", context, instance_id=None, host=host, params={"state": state}) -- cgit From 1d3d1d5fb552f2dc80c39ad15d89d59bfc7f873a Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 2 Aug 2011 21:11:12 +0000 Subject: Minor test fixes --- nova/api/openstack/contrib/hosts.py | 4 ++-- nova/tests/test_hosts.py | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index e9f82c75b..c90a889d5 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -78,7 +78,7 @@ class HostController(object): else: explanation = _("Invalid status: '%s'") % raw_val raise webob.exc.HTTPBadRequest(explanation=explanation) - elif key == "powerstate": + elif key == "power_state": if val == "startup": # The only valid values for 'state' are 'reboot' or # 'shutdown'. For completeness' sake there is the @@ -113,7 +113,7 @@ class HostController(object): context = req.environ['nova.context'] result = self.compute_api.set_host_powerstate(context, host=host, state=state) - return {"host": host, "powerstate": result} + return {"host": host, "power_state": result} class Hosts(extensions.ExtensionDescriptor): diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py index 1c7c21a1a..d8f90a109 100644 --- a/nova/tests/test_hosts.py +++ b/nova/tests/test_hosts.py @@ -49,10 +49,7 @@ def stub_set_host_enabled(context, host, enabled): def stub_set_host_powerstate(context, host, state): - # We'll simulate success and failure by assuming - # that 'host_c1' always succeeds, and 'host_c2' - # always fails - return state if host == "host_c1" else "running" + return state class FakeRequest(object): @@ -96,12 +93,14 @@ 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): + def test_host_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") + # Test invalid power_state + en_body = {"power_state": "invalid"} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - self.req, "host_c2", body=en_body) + self.req, "host_c1", body=en_body) def test_bad_power_state_value(self): bad_body = {"power_state": "bad"} -- cgit From 14e8257af4624fa5b056a1b0e94d1b584e080ce9 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 2 Aug 2011 22:48:47 +0000 Subject: Added check for --allow-admin-api to the host API extension code. --- nova/api/openstack/contrib/hosts.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index c90a889d5..78ba66771 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -133,6 +133,11 @@ class Hosts(extensions.ExtensionDescriptor): return "2011-06-29T00:00:00+00:00" def get_resources(self): - resources = [extensions.ResourceExtension('os-hosts', HostController(), - collection_actions={'update': 'PUT'}, member_actions={})] + resources = [] + # If we are not in an admin env, don't add the resource. Regular users + # shouldn't have access to the host. + if FLAGS.allow_admin_api: + resources = [extensions.ResourceExtension('os-hosts', + HostController(), collection_actions={'update': 'PUT'}, + member_actions={})] return resources -- cgit From a0ec6a6aa5ebdde1d099c5f6c03cf1dbd28441fa Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 3 Aug 2011 00:52:15 +0000 Subject: Removed duplicate methods created by previous merge. --- nova/compute/manager.py | 8 +------ nova/virt/driver.py | 4 ---- nova/virt/fake.py | 4 ---- nova/virt/hyperv.py | 4 ---- nova/virt/libvirt/connection.py | 4 ---- nova/virt/vmwareapi_conn.py | 4 ---- nova/virt/xenapi/vmops.py | 11 --------- nova/virt/xenapi_conn.py | 4 ---- .../xenserver/xenapi/etc/xapi.d/plugins/xenhost | 27 ---------------------- 9 files changed, 1 insertion(+), 69 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 37b920074..3c89042c8 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1,4 +1,4 @@ -#: tabstop=4 shiftwidth=4 softtabstop=4 +# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. @@ -939,12 +939,6 @@ class ComputeManager(manager.SchedulerDependentManager): """Sets the specified host's ability to accept new instances.""" return self.driver.set_host_enabled(host, enabled) - @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) - 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(notifier=notifier, publisher_id=publisher_id()) def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for an instance on this host.""" diff --git a/nova/virt/driver.py b/nova/virt/driver.py index b32ed7c54..bbb17480d 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -259,10 +259,6 @@ class ComputeDriver(object): """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() - def plug_vifs(self, instance, network_info): """Plugs in VIFs to networks.""" raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 135b88ab8..888dca220 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -518,7 +518,3 @@ 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 553f85265..119def38b 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -505,7 +505,3 @@ 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 150478edf..46f3eef4b 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1586,7 +1586,3 @@ 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 6f68fd726..af547821f 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -198,10 +198,6 @@ 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 - def plug_vifs(self, instance, network_info): """Plugs in VIFs to networks.""" self._vmops.plug_vifs(instance, network_info) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 194c33d54..a90ae0128 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1042,17 +1042,6 @@ class VMOps(object): 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 diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 468e12696..5962b4ff8 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -339,10 +339,6 @@ 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""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index 5169aeb12..c29d57717 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -142,33 +142,6 @@ def host_start(self, arg_dict): return _powerstate("startup") -@jsonify -def set_power_state(self, arg_dict): - """Reboots or powers off this host. Ideally, we would also like to be - able to power *on* a host, but right now this is not technically - feasible. - """ - power_state = arg_dict.get("power_state") - if power_state is None: - raise pluginlib.PluginError( - _("Missing 'power_state' argument to set_power_state")) - # Host must be disabled first -# result = _run_command("xe host-disable") -# if result: -# raise pluginlib.PluginError(result) -# # All running VMs must be shutdown -# result = _run_command("xe vm-shutdown --multiple power-state=running") -# if result: -# raise pluginlib.PluginError(result) -# cmds = {"reboot": "xe host-reboot", "on": "xe host-power-on", -# "off": "xe host-shutdown"} -# result = _run_command(cmds[power_state]) -# # Should be empty string -# if result: -# raise pluginlib.PluginError(result) - return {"power_state": power_state} - - @jsonify def host_data(self, arg_dict): """Runs the commands on the xenstore host to return the current status -- cgit From 7b69ef4fe1e4aabcf44789455b96492b168ad6f5 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 3 Aug 2011 01:32:08 +0000 Subject: Removed trailing whitespace that somehow made it into trunk. --- nova/api/openstack/create_instance_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index a2d18d37e..333994fcc 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -92,7 +92,7 @@ class CreateInstanceHelper(object): image_href = self.controller._image_ref_from_req_data(body) # If the image href was generated by nova api, strip image_href # down to an id and use the default glance connection params - + if str(image_href).startswith(req.application_url): image_href = image_href.split('/').pop() try: -- cgit From 75b110aa451382cce94f10a392597b40df97839c Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 4 Aug 2011 20:49:21 +0000 Subject: Changed all references to 'power state' to 'power action' as requested by review. --- nova/api/openstack/contrib/hosts.py | 43 ++++++++++++---------- nova/compute/api.py | 6 +-- nova/compute/manager.py | 6 +-- nova/tests/test_hosts.py | 31 ++++++++-------- nova/virt/driver.py | 2 +- nova/virt/fake.py | 2 +- nova/virt/hyperv.py | 2 +- nova/virt/libvirt/connection.py | 2 +- nova/virt/vmwareapi_conn.py | 2 +- nova/virt/xenapi/vmops.py | 8 ++-- nova/virt/xenapi_conn.py | 4 +- .../xenserver/xenapi/etc/xapi.d/plugins/xenhost | 12 +++--- 12 files changed, 61 insertions(+), 59 deletions(-) diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index 78ba66771..09adbe2f4 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -70,7 +70,7 @@ class HostController(object): key = raw_key.lower().strip() val = raw_val.lower().strip() # NOTE: (dabo) Right now only 'status' can be set, but other - # actions may follow. + # settings may follow. if key == "status": if val[:6] in ("enable", "disabl"): return self._set_enabled_status(req, id, @@ -78,20 +78,6 @@ class HostController(object): else: explanation = _("Invalid status: '%s'") % raw_val raise webob.exc.HTTPBadRequest(explanation=explanation) - elif key == "power_state": - if val == "startup": - # The only valid values for 'state' are 'reboot' or - # 'shutdown'. For completeness' sake there is the - # 'startup' option to start up a host, but this is not - # technically feasible now, as we run the host on the - # XenServer box. - msg = _("Host startup on XenServer is not supported.") - raise webob.exc.HTTPBadRequest(explanation=msg) - elif val in ("reboot", "shutdown"): - return self._set_powerstate(req, id, val) - else: - explanation = _("Invalid powerstate: '%s'") % raw_val - raise webob.exc.HTTPBadRequest(explanation=explanation) else: explanation = _("Invalid update setting: '%s'") % raw_key raise webob.exc.HTTPBadRequest(explanation=explanation) @@ -108,12 +94,28 @@ class HostController(object): raise webob.exc.HTTPBadRequest(explanation=result) return {"host": host, "status": result} - def _set_powerstate(self, req, host, state): + def _host_power_action(self, req, host, action): """Reboots or shuts down the host.""" context = req.environ['nova.context'] - result = self.compute_api.set_host_powerstate(context, host=host, - state=state) - return {"host": host, "power_state": result} + result = self.compute_api.host_power_action(context, host=host, + action=action) + return {"host": host, "power_action": result} + + def startup(self, req, id): + """The only valid values for 'action' are 'reboot' or + 'shutdown'. For completeness' sake there is the + 'startup' option to start up a host, but this is not + technically feasible now, as we run the host on the + XenServer box. + """ + msg = _("Host startup on XenServer is not supported.") + raise webob.exc.HTTPBadRequest(explanation=msg) + + def shutdown(self, req, id): + return self._host_power_action(req, host=id, action="shutdown") + + def reboot(self, req, id): + return self._host_power_action(req, host=id, action="reboot") class Hosts(extensions.ExtensionDescriptor): @@ -139,5 +141,6 @@ class Hosts(extensions.ExtensionDescriptor): if FLAGS.allow_admin_api: resources = [extensions.ResourceExtension('os-hosts', HostController(), collection_actions={'update': 'PUT'}, - member_actions={})] + member_actions={"startup": "GET", "shutdown": "GET", + "reboot": "GET"})] return resources diff --git a/nova/compute/api.py b/nova/compute/api.py index 3e0cd7cfa..5f85ca908 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -995,10 +995,10 @@ class API(base.Base): return self._call_compute_message("set_host_enabled", context, instance_id=None, host=host, params={"enabled": enabled}) - def set_host_powerstate(self, context, host, state): + def host_power_action(self, context, host, action): """Reboots or shuts down the host.""" - return self._call_compute_message("set_host_powerstate", context, - instance_id=None, host=host, params={"state": state}) + return self._call_compute_message("host_power_action", context, + instance_id=None, host=host, params={"action": action}) @scheduler_api.reroute_compute("diagnostics") def get_diagnostics(self, context, instance_id): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 220b09554..c2c12a9a2 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -955,10 +955,10 @@ class ComputeManager(manager.SchedulerDependentManager): result)) @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) - def set_host_powerstate(self, context, instance_id=None, host=None, - state=None): + def host_power_action(self, context, instance_id=None, host=None, + action=None): """Reboots or shuts down the host.""" - return self.driver.set_host_powerstate(host, state) + return self.driver.host_power_action(host, action) @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def set_host_enabled(self, context, instance_id=None, host=None, diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py index d8f90a109..cd22571e6 100644 --- a/nova/tests/test_hosts.py +++ b/nova/tests/test_hosts.py @@ -48,8 +48,8 @@ def stub_set_host_enabled(context, host, enabled): return status -def stub_set_host_powerstate(context, host, state): - return state +def stub_host_power_action(context, host, action): + return action class FakeRequest(object): @@ -66,8 +66,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_host_powerstate', - stub_set_host_powerstate) + self.stubs.Set(self.controller.compute_api, 'host_power_action', + stub_host_power_action) def test_list_hosts(self): """Verify that the compute hosts are returned.""" @@ -93,19 +93,18 @@ class HostTestCase(test.TestCase): result_c2 = self.controller.update(self.req, "host_c2", body=en_body) self.assertEqual(result_c2["status"], "disabled") - def test_host_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") - # Test invalid power_state - en_body = {"power_state": "invalid"} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - self.req, "host_c1", body=en_body) + def test_host_startup(self): + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.startup, + self.req, "host_c1") - def test_bad_power_state_value(self): - bad_body = {"power_state": "bad"} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - self.req, "host_c1", body=bad_body) + def test_host_shutdown(self): + result = self.controller.shutdown(self.req, "host_c1") + print "RES", result + self.assertEqual(result["power_action"], "shutdown") + + def test_host_reboot(self): + result = self.controller.reboot(self.req, "host_c1") + self.assertEqual(result["power_action"], "reboot") def test_bad_status_value(self): bad_body = {"status": "bad"} diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 30f14459a..052c6607e 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -282,7 +282,7 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 7b13f4fbe..db51c258b 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -512,7 +512,7 @@ class FakeConnection(driver.ComputeDriver): """Return fake Host Status of ram, disk, network.""" return self.host_status - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" pass diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 00c4d360b..f0efeb581 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -499,7 +499,7 @@ class HyperVConnection(driver.ComputeDriver): """See xenapi_conn.py implementation.""" pass - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" pass diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index f0b2014ac..14e02c7c4 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1558,7 +1558,7 @@ class LibvirtConnection(driver.ComputeDriver): """See xenapi_conn.py implementation.""" pass - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" pass diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 90d083f10..5937d9585 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -191,7 +191,7 @@ class VMWareESXConnection(driver.ComputeDriver): """This method is supported only by libvirt.""" return - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" pass diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index dfd85dd2c..509abd767 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1024,13 +1024,13 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" - args = {"state": json.dumps(state)} + args = {"action": json.dumps(action)} methods = {"reboot": "host_reboot", "shutdown": "host_shutdown"} - json_resp = self._call_xenhost(methods[state], args) + json_resp = self._call_xenhost(methods[action], args) resp = json.loads(json_resp) - return resp["powerstate"] + return resp["power_action"] def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index fa5b78a2a..3452343c6 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -332,9 +332,9 @@ class XenAPIConnection(driver.ComputeDriver): True, run the update first.""" return self.HostState.get_host_stats(refresh=refresh) - def set_host_powerstate(self, host, state): + def host_power_action(self, host, action): """Reboots or shuts down the host.""" - return self._vmops.set_host_powerstate(host, state) + return self._vmops.host_power_action(host, action) def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index c29d57717..f6a9ac8d8 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -103,7 +103,7 @@ def set_host_enabled(self, arg_dict): return {"status": status} -def _powerstate(state): +def _power_action(action): host_uuid = _get_host_uuid() # Host must be disabled first result = _run_command("xe host-disable") @@ -115,23 +115,23 @@ def _powerstate(state): raise pluginlib.PluginError(result) cmds = {"reboot": "xe host-reboot", "startup": "xe host-power-on", "shutdown": "xe host-shutdown"} - result = _run_command(cmds[state]) + result = _run_command(cmds[action]) # Should be empty string if result: raise pluginlib.PluginError(result) - return {"powerstate": state} + return {"power_action": action} @jsonify def host_reboot(self, arg_dict): """Reboots the host.""" - return _powerstate("reboot") + return _power_action("reboot") @jsonify def host_shutdown(self, arg_dict): """Reboots the host.""" - return _powerstate("shutdown") + return _power_action("shutdown") @jsonify @@ -139,7 +139,7 @@ def host_start(self, arg_dict): """Starts the host. NOTE: Currently not feasible, since the host runs on the same machine as Xen. """ - return _powerstate("startup") + return _power_action("startup") @jsonify -- cgit From dcac4bc6c7be9832704e37cca7ce815e083974f5 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 4 Aug 2011 20:55:56 +0000 Subject: Added admin-only decorator --- nova/api/openstack/contrib/admin_only.py | 30 ++++++++++++++++++++++++++++++ nova/api/openstack/contrib/hosts.py | 14 ++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 nova/api/openstack/contrib/admin_only.py diff --git a/nova/api/openstack/contrib/admin_only.py b/nova/api/openstack/contrib/admin_only.py new file mode 100644 index 000000000..e821c9e1f --- /dev/null +++ b/nova/api/openstack/contrib/admin_only.py @@ -0,0 +1,30 @@ +# Copyright (c) 2011 Openstack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Decorator for limiting extensions that should be admin-only.""" + +from functools import wraps +from nova import flags +FLAGS = flags.FLAGS + + +def admin_only(fnc): + @wraps(fnc) + def _wrapped(self, *args, **kwargs): + if FLAGS.allow_admin_api: + return fnc(self, *args, **kwargs) + return [] + _wrapped.func_name = fnc.func_name + return _wrapped diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index 09adbe2f4..cdf8760d5 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -24,6 +24,7 @@ from nova import log as logging from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import faults +from nova.api.openstack.contrib import admin_only from nova.scheduler import api as scheduler_api @@ -134,13 +135,10 @@ class Hosts(extensions.ExtensionDescriptor): def get_updated(self): return "2011-06-29T00:00:00+00:00" + @admin_only.admin_only def get_resources(self): - resources = [] - # If we are not in an admin env, don't add the resource. Regular users - # shouldn't have access to the host. - if FLAGS.allow_admin_api: - resources = [extensions.ResourceExtension('os-hosts', - HostController(), collection_actions={'update': 'PUT'}, - member_actions={"startup": "GET", "shutdown": "GET", - "reboot": "GET"})] + resources = [extensions.ResourceExtension('os-hosts', + HostController(), collection_actions={'update': 'PUT'}, + member_actions={"startup": "GET", "shutdown": "GET", + "reboot": "GET"})] return resources -- cgit From b5ff9bc2add98444773a26ce37e1ceb82e9531ae Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 4 Aug 2011 21:10:22 +0000 Subject: Removed test show() method --- nova/api/openstack/contrib/hosts.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index ddb611905..7290360ad 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -65,13 +65,6 @@ class HostController(object): def index(self, req): return {'hosts': _list_hosts(req)} - def show(self, req, id): - """Check the query vars for values to be returned from the host config - settings. Return a dict with the query var as the key and the config - setting as the value. - """ - return {"PARAMS": req.params.keys()} - @check_host def update(self, req, id, body): for raw_key, raw_val in body.iteritems(): -- cgit