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 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'nova/api') 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): -- 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 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'nova/api') 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): -- 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 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'nova/api') 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) -- 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 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') 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): -- 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(-) (limited to 'nova/api') 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 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(-) (limited to 'nova/api') 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 f34a6fb9efd8d950555431f5e7268dbde8ae78c8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 3 Aug 2011 19:17:08 -0400 Subject: Remove instances of the "diaper pattern" Anywhere "except:" occurs, I tried to replace it with an explicit except on known error types. If none were known, Except was used. In the process I've been able to unearth a few evasive bugs and clean up some adjacent code. --- nova/api/direct.py | 5 ++--- nova/api/ec2/__init__.py | 4 ++-- nova/api/ec2/apirequest.py | 2 +- nova/api/openstack/common.py | 27 +++++++++++---------------- nova/api/openstack/servers.py | 22 +++++++++++----------- 5 files changed, 27 insertions(+), 33 deletions(-) (limited to 'nova/api') diff --git a/nova/api/direct.py b/nova/api/direct.py index 993815fc7..139c46d63 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -48,7 +48,6 @@ import nova.api.openstack.wsgi # Global storage for registering modules. ROUTES = {} - def register_service(path, handle): """Register a service handle at a given path. @@ -296,8 +295,8 @@ class ServiceWrapper(object): 'application/json': nova.api.openstack.wsgi.JSONDictSerializer(), }[content_type] return serializer.serialize(result) - except: - raise exception.Error("returned non-serializable type: %s" + except Exception, e: + raise exception.Error(_("Returned non-serializable type: %s") % result) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index af232edda..804e54ef9 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -147,7 +147,7 @@ class Authenticate(wsgi.Middleware): try: signature = req.params['Signature'] access = req.params['AWSAccessKeyId'] - except: + except KeyError, e: raise webob.exc.HTTPBadRequest() # Make a copy of args for authentication and signature verification. @@ -211,7 +211,7 @@ class Requestify(wsgi.Middleware): for non_arg in non_args: # Remove, but raise KeyError if omitted args.pop(non_arg) - except: + except KeyError, e: raise webob.exc.HTTPBadRequest() LOG.debug(_('action: %s'), action) diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index 7d78c5cfa..9a3e55925 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -104,7 +104,7 @@ class APIRequest(object): for key in data.keys(): val = data[key] el.appendChild(self._render_data(xml, key, val)) - except: + except Exception: LOG.debug(data) raise diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index efa4ab385..1304379a8 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -16,7 +16,7 @@ # under the License. import re -from urlparse import urlparse +import urlparse from xml.dom import minidom import webob @@ -137,8 +137,8 @@ def get_id_from_href(href): if re.match(r'\d+$', str(href)): return int(href) try: - return int(urlparse(href).path.split('/')[-1]) - except: + return int(urlparse.urlsplit(href).path.split('/')[-1]) + except ValueError, e: LOG.debug(_("Error extracting id from href: %s") % href) raise ValueError(_('could not parse id from href')) @@ -153,22 +153,17 @@ def remove_version_from_href(href): Returns: 'http://www.nova.com' """ - try: - #removes the first instance that matches /v#.#/ - new_href = re.sub(r'[/][v][0-9]+\.[0-9]+[/]', '/', href, count=1) + parsed_url = urlparse.urlsplit(href) + new_path = re.sub(r'^/v[0-9]+\.[0-9]+(/|$)', r'\1', parsed_url.path, count=1) - #if no version was found, try finding /v#.# at the end of the string - if new_href == href: - new_href = re.sub(r'[/][v][0-9]+\.[0-9]+$', '', href, count=1) - except: - LOG.debug(_("Error removing version from href: %s") % href) - msg = _('could not parse version from href') + if new_path == parsed_url.path: + msg = _('href %s does not contain version') % href + LOG.debug(msg) raise ValueError(msg) - if new_href == href: - msg = _('href does not contain version') - raise ValueError(msg) - return new_href + parsed_url = list(parsed_url) + parsed_url[2] = new_path + return urlparse.urlunsplit(parsed_url) def get_version_from_href(href): diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 002b47edb..493845e8c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -290,7 +290,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.lock(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::lock %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -306,7 +306,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.unlock(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::unlock %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -321,7 +321,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.get_lock(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::get_lock %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -336,7 +336,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.reset_network(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::reset_network %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -351,7 +351,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.inject_network_info(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::inject_network_info %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -363,7 +363,7 @@ class Controller(object): ctxt = req.environ['nova.context'] try: self.compute_api.pause(ctxt, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::pause %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -375,7 +375,7 @@ class Controller(object): ctxt = req.environ['nova.context'] try: self.compute_api.unpause(ctxt, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("Compute.api::unpause %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -387,7 +387,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.suspend(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("compute.api::suspend %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -399,7 +399,7 @@ class Controller(object): context = req.environ['nova.context'] try: self.compute_api.resume(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("compute.api::resume %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -420,7 +420,7 @@ class Controller(object): context = req.environ["nova.context"] try: self.compute_api.rescue(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("compute.api::rescue %s"), readable) raise exc.HTTPUnprocessableEntity() @@ -432,7 +432,7 @@ class Controller(object): context = req.environ["nova.context"] try: self.compute_api.unrescue(context, id) - except: + except Exception: readable = traceback.format_exc() LOG.exception(_("compute.api::unrescue %s"), readable) raise exc.HTTPUnprocessableEntity() -- cgit From f22c19c6f78451074c33fe8da855755574cb6b49 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 4 Aug 2011 15:51:41 -0400 Subject: Split serverXMLDeserializers into v1.0 and v1.1 --- nova/api/openstack/create_instance_helper.py | 48 ++++++++++++++++++++++++++++ nova/api/openstack/servers.py | 7 +++- 2 files changed, 54 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 2a8e7fd7e..832c890b6 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -304,6 +304,54 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): metadata_deserializer = common.MetadataXMLDeserializer() + def create(self, string): + """Deserialize an xml-formatted server create request""" + dom = minidom.parseString(string) + server = self._extract_server(dom) + return {'body': {'server': server}} + + def _extract_server(self, node): + """Marshal the server attribute of a parsed request""" + server = {} + server_node = self.find_first_child_named(node, 'server') + + attributes = ["name", "imageId", "flavorId", "adminPass"] + for attr in attributes: + if server_node.getAttribute(attr): + server[attr] = server_node.getAttribute(attr) + + metadata_node = self.find_first_child_named(server_node, "metadata") + server["metadata"] = self.metadata_deserializer.extract_metadata( + metadata_node) + + server["personality"] = self._extract_personality(server_node) + + return server + + def _extract_personality(self, server_node): + """Marshal the personality attribute of a parsed request""" + node = self.find_first_child_named(server_node, "personality") + personality = [] + if node is not None: + for file_node in self.find_children_named(node, "file"): + item = {} + if file_node.hasAttribute("path"): + item["path"] = file_node.getAttribute("path") + item["contents"] = self.extract_text(file_node) + personality.append(item) + return personality + + +class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): + """ + Deserializer to handle xml-formatted server create requests. + + Handles standard server attributes as well as optional metadata + and personality attributes + """ + + metadata_deserializer = common.MetadataXMLDeserializer() + def action(self, string): dom = minidom.parseString(string) action_node = dom.childNodes[0] diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index d17714371..3acc4510e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -891,8 +891,13 @@ def create_resource(version='1.0'): 'application/xml': xml_serializer, } + xml_deserializer = { + '1.0': helper.ServerXMLDeserializer(), + '1.1': helper.ServerXMLDeserializerV11(), + }[version] + body_deserializers = { - 'application/xml': helper.ServerXMLDeserializer(), + 'application/xml': xml_deserializer, } serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer) -- cgit From 9ce80fc74b3ea4513369b795d1e6891d6dfa8e03 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 4 Aug 2011 16:20:37 -0400 Subject: Updated create image server action to respect 1.1 --- nova/api/openstack/create_instance_helper.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 832c890b6..eec861428 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -380,8 +380,10 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): if value: data[attribute] = value metadata_node = self.find_first_child_named(node, 'metadata') - metadata = self.metadata_deserializer.extract_metadata(metadata_node) - data['metadata'] = metadata + if metadata_node is not None: + metadata = self.metadata_deserializer.extract_metadata( + metadata_node) + data['metadata'] = metadata return data def create(self, string): @@ -395,29 +397,32 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): server = {} server_node = self.find_first_child_named(node, 'server') - attributes = ["name", "imageId", "flavorId", "imageRef", - "flavorRef", "adminPass"] + attributes = ["name", "imageRef", "flavorRef", "adminPass"] for attr in attributes: if server_node.getAttribute(attr): server[attr] = server_node.getAttribute(attr) metadata_node = self.find_first_child_named(server_node, "metadata") - server["metadata"] = self.metadata_deserializer.extract_metadata( - metadata_node) + if metadata_node is not None: + server["metadata"] = self.extract_metadata(metadata_node) - server["personality"] = self._extract_personality(server_node) + personality = self._extract_personality(server_node) + if personality is not None: + server["personality"] = personality return server def _extract_personality(self, server_node): """Marshal the personality attribute of a parsed request""" node = self.find_first_child_named(server_node, "personality") - personality = [] if node is not None: + personality = [] for file_node in self.find_children_named(node, "file"): item = {} if file_node.hasAttribute("path"): item["path"] = file_node.getAttribute("path") item["contents"] = self.extract_text(file_node) personality.append(item) - return personality + return personality + else: + return None -- cgit From 0f515d6d31b2c95ed7f1e3ca8d9d67f98fda9fbe Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 4 Aug 2011 16:26:31 -0400 Subject: Added XML serialization for server actions --- nova/api/openstack/create_instance_helper.py | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index eec861428..894d47beb 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -360,6 +360,12 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): action_deserializer = { 'createImage': self._action_create_image, 'createBackup': self._action_create_backup, + 'changePassword': self._action_change_password, + 'reboot': self._action_reboot, + 'rebuild': self._action_rebuild, + 'resize': self._action_resize, + 'confirmResize': self._action_confirm_resize, + 'revertResize': self._action_revert_resize, }.get(action_name, self.default) action_data = action_deserializer(action_node) @@ -373,6 +379,46 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): attributes = ('name', 'backup_type', 'rotation') return self._deserialize_image_action(node, attributes) + def _action_change_password(self, node): + if not node.hasAttribute("adminPass"): + raise AttributeError("No adminPass was specified in request") + return {"adminPass": node.getAttribute("adminPass")} + + def _action_reboot(self, node): + if not node.hasAttribute("type"): + raise AttributeError("No reboot type was specified in request") + return {"type": node.getAttribute("type")} + + def _action_rebuild(self, node): + rebuild = {} + if node.hasAttribute("name"): + rebuild['name'] = node.getAttribute("name") + + metadata_node = self.find_first_child_named(node, "metadata") + if metadata_node is not None: + rebuild["metadata"] = self.extract_metadata(metadata_node) + + personality = self._extract_personality(node) + if personality is not None: + rebuild["personality"] = personality + + if not node.hasAttribute("imageRef"): + raise AttributeError("No imageRef was specified in request") + rebuild["imageRef"] = node.getAttribute("imageRef") + + return rebuild + + def _action_resize(self, node): + if not node.hasAttribute("flavorRef"): + raise AttributeError("No flavorRef was specified in request") + return {"flavorRef": node.getAttribute("flavorRef")} + + def _action_confirm_resize(self, node): + return None + + def _action_revert_resize(self, node): + return None + def _deserialize_image_action(self, node, allowed_attributes): data = {} for attribute in allowed_attributes: -- cgit From 5b463f5d14c62f66250d5edc3edbd2ded704e0da Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 4 Aug 2011 16:38:55 -0400 Subject: Added missing tests for server actions Updated reboot to verify the reboot type is HARD or SOFT Fixed case of having an empty flavorref on resize --- nova/api/openstack/servers.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3acc4510e..fa4d11e24 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -268,9 +268,13 @@ class Controller(object): def _action_reboot(self, input_dict, req, id): if 'reboot' in input_dict and 'type' in input_dict['reboot']: reboot_type = input_dict['reboot']['type'] + if (not reboot_type == 'HARD') and (not reboot_type == 'SOFT'): + msg = _("Argument 'type' for reboot is not HARD or SOFT") + LOG.exception(msg) + raise exc.HTTPBadRequest() else: LOG.exception(_("Missing argument 'type' for reboot")) - raise exc.HTTPUnprocessableEntity() + raise exc.HTTPBadRequest() try: # TODO(gundlach): pass reboot_type, support soft reboot in # virt driver @@ -646,6 +650,9 @@ class ControllerV11(Controller): """ Resizes a given instance to the flavor size requested """ try: flavor_ref = input_dict["resize"]["flavorRef"] + if not flavor_ref: + msg = _("Resize request has invalid 'flavorRef' attribute.") + raise exc.HTTPBadRequest(explanation=msg) except (KeyError, TypeError): msg = _("Resize requests require 'flavorRef' attribute.") raise exc.HTTPBadRequest(explanation=msg) -- 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 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'nova/api') 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 -- 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 (limited to 'nova/api') 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 637dfc0f44cbd5bf0c76d80d708a241e562403ac Mon Sep 17 00:00:00 2001 From: Gabe Westmaas Date: Fri, 5 Aug 2011 01:55:53 +0000 Subject: Added explanations to exceptions and cleaned up reboot types --- nova/api/openstack/servers.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index fa4d11e24..9a55af8ea 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -267,14 +267,16 @@ class Controller(object): def _action_reboot(self, input_dict, req, id): if 'reboot' in input_dict and 'type' in input_dict['reboot']: - reboot_type = input_dict['reboot']['type'] - if (not reboot_type == 'HARD') and (not reboot_type == 'SOFT'): + valid_reboot_types = ['HARD', 'SOFT'] + reboot_type = input_dict['reboot']['type'].upper() + if not valid_reboot_types.count(reboot_type): msg = _("Argument 'type' for reboot is not HARD or SOFT") LOG.exception(msg) - raise exc.HTTPBadRequest() + raise exc.HTTPBadRequest(explanation=msg) else: - LOG.exception(_("Missing argument 'type' for reboot")) - raise exc.HTTPBadRequest() + msg = _("Missing argument 'type' for reboot") + LOG.exception(msg) + raise exc.HTTPBadRequest(explanation=msg) try: # TODO(gundlach): pass reboot_type, support soft reboot in # virt driver -- cgit From ab1ba7cbcffc92c2c82c468fb0a2a81f93db3f85 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 5 Aug 2011 06:01:55 -0700 Subject: fixed up zones controller to properly work with 1.1 --- nova/api/openstack/zones.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index f7fd87bcd..a2bf267ed 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -166,7 +166,7 @@ class Controller(object): return self.helper._get_server_admin_password_old_style(server) -class ControllerV11(object): +class ControllerV11(Controller): """Controller for 1.1 Zone resources.""" def _get_server_admin_password(self, server): -- cgit From e3433605d77492a58916d2e131eb0701baf849fa Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 5 Aug 2011 07:19:35 -0700 Subject: pep8 violations sneaking into trunk? --- nova/api/direct.py | 1 + nova/api/openstack/common.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/direct.py b/nova/api/direct.py index 139c46d63..fdd2943d2 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -48,6 +48,7 @@ import nova.api.openstack.wsgi # Global storage for registering modules. ROUTES = {} + def register_service(path, handle): """Register a service handle at a given path. diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 715b9e4a4..75e862630 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -154,7 +154,8 @@ def remove_version_from_href(href): """ parsed_url = urlparse.urlsplit(href) - new_path = re.sub(r'^/v[0-9]+\.[0-9]+(/|$)', r'\1', parsed_url.path, count=1) + new_path = re.sub(r'^/v[0-9]+\.[0-9]+(/|$)', r'\1', parsed_url.path, + count=1) if new_path == parsed_url.path: msg = _('href %s does not contain version') % href -- cgit From 9602a558b6be6e6812626b986c0f9557a3862fe6 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 5 Aug 2011 11:59:14 -0400 Subject: glance image service pagination --- nova/api/openstack/images.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index c76738d30..b9bc83fde 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -143,7 +143,7 @@ class ControllerV10(Controller): """ context = req.environ['nova.context'] filters = self._get_filters(req) - images = self._image_service.index(context, filters) + images = self._image_service.index(context, filters=filters) images = common.limited(images, req) builder = self.get_builder(req).build return dict(images=[builder(image, detail=False) for image in images]) @@ -156,7 +156,7 @@ class ControllerV10(Controller): """ context = req.environ['nova.context'] filters = self._get_filters(req) - images = self._image_service.detail(context, filters) + images = self._image_service.detail(context, filters=filters) images = common.limited(images, req) builder = self.get_builder(req).build return dict(images=[builder(image, detail=True) for image in images]) -- cgit From f03c926a7d28ee35789048ea53c36cd452ed3571 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 5 Aug 2011 14:28:22 -0500 Subject: Allow actions queries by UUID and PEP8 fixes. --- nova/api/direct.py | 1 + nova/api/openstack/common.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/direct.py b/nova/api/direct.py index 139c46d63..fdd2943d2 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -48,6 +48,7 @@ import nova.api.openstack.wsgi # Global storage for registering modules. ROUTES = {} + def register_service(path, handle): """Register a service handle at a given path. diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 715b9e4a4..4548c2c75 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -154,7 +154,8 @@ def remove_version_from_href(href): """ parsed_url = urlparse.urlsplit(href) - new_path = re.sub(r'^/v[0-9]+\.[0-9]+(/|$)', r'\1', parsed_url.path, count=1) + new_path = re.sub(r'^/v[0-9]+\.[0-9]+(/|$)', r'\1', parsed_url.path, + count=1) if new_path == parsed_url.path: msg = _('href %s does not contain version') % href -- cgit From 9633e9877c7836c18c30b51c8494abfb025e64ca Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 5 Aug 2011 22:14:15 +0000 Subject: Adding flag around image-create for v1.0 --- nova/api/openstack/__init__.py | 3 +++ nova/api/openstack/images.py | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index d6a98c2cd..4d49df2ad 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -50,6 +50,9 @@ FLAGS = flags.FLAGS flags.DEFINE_bool('allow_admin_api', False, 'When True, this API service will accept admin operations.') +flags.DEFINE_bool('allow_instance_snapshots', + True, + 'When True, this API service will permit instance snapshot operations.') class FaultWrapper(base_wsgi.Middleware): diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index b9bc83fde..7b738e1f3 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -108,6 +108,12 @@ class ControllerV10(Controller): def create(self, req, body): """Snapshot a server instance and save the image.""" + if not FLAGS.allow_instance_snapshots: + LOG.warn(_('Rejecting snapshot request, snapshots currently' + ' disabled')) + msg = _("Instance Snapshots are not permitted at this time.") + raise webob.exc.HTTPBadRequest(explanation=msg) + try: image = body["image"] except (KeyError, TypeError): -- cgit From c49e99a7fc590c2dde6125843d904895ca8861a3 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 5 Aug 2011 22:29:28 +0000 Subject: Disable flag for V1 Openstack API --- nova/api/openstack/servers.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 1051ba571..391c7d644 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -691,6 +691,12 @@ class ControllerV11(Controller): def _action_create_image(self, input_dict, req, instance_id): """Snapshot a server instance.""" + if not FLAGS.allow_instance_snapshots: + LOG.warn(_('Rejecting snapshot request, snapshots currently' + ' disabled')) + msg = _("Instance Snapshots are not permitted at this time.") + raise webob.exc.HTTPBadRequest(explanation=msg) + entity = input_dict.get("createImage", {}) try: -- cgit From bdabdd50845279cbca11f510dd5da6a5aa110528 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 5 Aug 2011 22:56:08 +0000 Subject: Using decorator for snapshots enabled check --- nova/api/openstack/common.py | 14 ++++++++++++++ nova/api/openstack/images.py | 7 +------ nova/api/openstack/servers.py | 7 +------ 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 4548c2c75..ec9368140 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import functools import re import urlparse from xml.dom import minidom @@ -280,3 +281,16 @@ class MetadataXMLSerializer(wsgi.XMLDictSerializer): def default(self, *args, **kwargs): return '' + + +def check_snapshots_enabled(f): + @functools.wraps(f) + def inner(*args, **kwargs): + if not FLAGS.allow_instance_snapshots: + LOG.warn(_('Rejecting snapshot request, snapshots currently' + ' disabled')) + msg = _("Instance Snapshots are not permitted at this time.") + raise webob.exc.HTTPBadRequest(explanation=msg) + return f(*args, **kwargs) + return inner + diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 7b738e1f3..0aabb9e56 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -106,14 +106,9 @@ class Controller(object): class ControllerV10(Controller): """Version 1.0 specific controller logic.""" + @common.check_snapshots_enabled def create(self, req, body): """Snapshot a server instance and save the image.""" - if not FLAGS.allow_instance_snapshots: - LOG.warn(_('Rejecting snapshot request, snapshots currently' - ' disabled')) - msg = _("Instance Snapshots are not permitted at this time.") - raise webob.exc.HTTPBadRequest(explanation=msg) - try: image = body["image"] except (KeyError, TypeError): diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 391c7d644..4d6518598 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -689,14 +689,9 @@ class ControllerV11(Controller): return webob.Response(status_int=202) + @common.check_snapshots_enabled def _action_create_image(self, input_dict, req, instance_id): """Snapshot a server instance.""" - if not FLAGS.allow_instance_snapshots: - LOG.warn(_('Rejecting snapshot request, snapshots currently' - ' disabled')) - msg = _("Instance Snapshots are not permitted at this time.") - raise webob.exc.HTTPBadRequest(explanation=msg) - entity = input_dict.get("createImage", {}) try: -- cgit From b15535a20b7f717aa23f5bc6d695e574bb86c407 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 5 Aug 2011 23:26:08 +0000 Subject: Adding check to stub method --- nova/api/openstack/common.py | 2 +- nova/api/openstack/servers.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index ec9368140..375304930 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -289,7 +289,7 @@ def check_snapshots_enabled(f): if not FLAGS.allow_instance_snapshots: LOG.warn(_('Rejecting snapshot request, snapshots currently' ' disabled')) - msg = _("Instance Snapshots are not permitted at this time.") + msg = _("Instance snapshots are not permitted at this time.") raise webob.exc.HTTPBadRequest(explanation=msg) return f(*args, **kwargs) return inner diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 4d6518598..f1a27a98c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -240,6 +240,7 @@ class Controller(object): resp.headers['Location'] = image_ref return resp + @common.check_snapshots_enabled def _action_create_image(self, input_dict, req, id): return exc.HTTPNotImplemented() -- cgit From 7a5bb39ef11d630df26f2fcfbf249f0c34e9fa55 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 5 Aug 2011 18:51:17 -0500 Subject: Pep8 fix --- nova/api/openstack/common.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 375304930..5226cdf9a 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -293,4 +293,3 @@ def check_snapshots_enabled(f): raise webob.exc.HTTPBadRequest(explanation=msg) return f(*args, **kwargs) return inner - -- cgit From 8c75de3188fdbec6456fcf7071b6b08b9d1a0d40 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Sun, 7 Aug 2011 16:40:07 -0400 Subject: Set image progress to 100 if the image is active. --- nova/api/openstack/views/images.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 873ce212a..8539fbcbf 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -77,7 +77,9 @@ class ViewBuilder(object): "status": image_obj.get("status"), }) - if image["status"] == "SAVING": + if image["status"] == "ACTIVE": + image["progress"] = 100 + else: image["progress"] = 0 return image -- cgit From e4ee8b54d0e840050357902b78f7e48013be9096 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 8 Aug 2011 10:12:01 -0400 Subject: upper() is even better. --- nova/api/openstack/views/images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 8539fbcbf..912303d14 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -77,7 +77,7 @@ class ViewBuilder(object): "status": image_obj.get("status"), }) - if image["status"] == "ACTIVE": + if image["status"].upper() == "ACTIVE": image["progress"] = 100 else: image["progress"] = 0 -- cgit From b1a503053cb8cbeb1a4ab18e650b49cc4da15e23 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Mon, 8 Aug 2011 14:19:53 +0000 Subject: Moved the restriction on host startup to the xenapi layer.: --- nova/api/openstack/contrib/hosts.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index cdf8760d5..d5bd3166b 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -96,21 +96,17 @@ class HostController(object): return {"host": host, "status": result} def _host_power_action(self, req, host, action): - """Reboots or shuts down the host.""" + """Reboots, shuts down or powers up the host.""" context = req.environ['nova.context'] - result = self.compute_api.host_power_action(context, host=host, - action=action) + try: + result = self.compute_api.host_power_action(context, host=host, + action=action) + except NotImplementedError as e: + raise webob.exc.HTTPBadRequest(explanation=e.msg) 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) + return self._host_power_action(req, host=id, action="startup") def shutdown(self, req, id): return self._host_power_action(req, host=id, action="shutdown") -- cgit From 973032959ea4b1300cb68f767885dbd3226bebd9 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Mon, 8 Aug 2011 14:42:18 +0000 Subject: Fixed some typos from the last refactoring --- nova/api/openstack/contrib/hosts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py index d5bd3166b..ecaa365b7 100644 --- a/nova/api/openstack/contrib/hosts.py +++ b/nova/api/openstack/contrib/hosts.py @@ -133,7 +133,7 @@ class Hosts(extensions.ExtensionDescriptor): @admin_only.admin_only def get_resources(self): - resources = [extensions.ResourceExtension('os-hosts', + resources = [extensions.ResourceExtension('os-hosts', HostController(), collection_actions={'update': 'PUT'}, member_actions={"startup": "GET", "shutdown": "GET", "reboot": "GET"})] -- cgit