1
# Copyright (c) 2011 Openstack, LLC.
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5
# not use this file except in compliance with the License. You may obtain
6
# a copy of the License at
8
# http://www.apache.org/licenses/LICENSE-2.0
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
# License for the specific language governing permissions and limitations
16
"""The hosts admin extension."""
19
from xml.dom import minidom
20
from xml.parsers import expat
22
from nova.api.openstack import wsgi
23
from nova.api.openstack import xmlutil
24
from nova.api.openstack.v2 import extensions
25
from nova import compute
26
from nova import exception
27
from nova import flags
28
from nova import log as logging
29
from nova.scheduler import api as scheduler_api
32
LOG = logging.getLogger("nova.api.openstack.v2.contrib.hosts")
36
class HostIndexTemplate(xmlutil.TemplateBuilder):
38
def shimmer(obj, do_raise=False):
39
# A bare list is passed in; we need to wrap it in a dict
40
return dict(hosts=obj)
42
root = xmlutil.TemplateElement('hosts', selector=shimmer)
43
elem = xmlutil.SubTemplateElement(root, 'host', selector='hosts')
47
return xmlutil.MasterTemplate(root, 1)
50
class HostUpdateTemplate(xmlutil.TemplateBuilder):
52
root = xmlutil.TemplateElement('host')
56
return xmlutil.MasterTemplate(root, 1)
59
class HostActionTemplate(xmlutil.TemplateBuilder):
61
root = xmlutil.TemplateElement('host')
63
root.set('power_action')
65
return xmlutil.MasterTemplate(root, 1)
68
class HostDeserializer(wsgi.XMLDeserializer):
69
def default(self, string):
71
node = minidom.parseString(string)
72
except expat.ExpatError:
73
msg = _("cannot understand XML")
74
raise exception.MalformedRequestBody(reason=msg)
77
for child in node.childNodes[0].childNodes:
78
updates[child.tagName] = self.extract_text(child)
80
return dict(body=updates)
83
def _list_hosts(req, service=None):
84
"""Returns a summary list of hosts, optionally filtering
87
context = req.environ['nova.context']
88
hosts = scheduler_api.get_host_list(context)
90
hosts = [host for host in hosts
91
if host["service"] == service]
96
"""Makes sure that the host exists."""
97
def wrapped(self, req, id, service=None, *args, **kwargs):
98
listed_hosts = _list_hosts(req, service)
99
hosts = [h["host_name"] for h in listed_hosts]
101
return fn(self, req, id, *args, **kwargs)
103
raise exception.HostNotFound(host=id)
107
class HostController(object):
108
"""The Hosts API controller for the OpenStack API."""
110
self.compute_api = compute.API()
111
super(HostController, self).__init__()
113
@wsgi.serializers(xml=HostIndexTemplate)
114
def index(self, req):
115
return {'hosts': _list_hosts(req)}
117
@wsgi.serializers(xml=HostUpdateTemplate)
118
@wsgi.deserializers(xml=HostDeserializer)
120
def update(self, req, id, body):
121
for raw_key, raw_val in body.iteritems():
122
key = raw_key.lower().strip()
123
val = raw_val.lower().strip()
124
# NOTE: (dabo) Right now only 'status' can be set, but other
125
# settings may follow.
127
if val[:6] in ("enable", "disabl"):
128
return self._set_enabled_status(req, id,
129
enabled=(val.startswith("enable")))
131
explanation = _("Invalid status: '%s'") % raw_val
132
raise webob.exc.HTTPBadRequest(explanation=explanation)
134
explanation = _("Invalid update setting: '%s'") % raw_key
135
raise webob.exc.HTTPBadRequest(explanation=explanation)
137
def _set_enabled_status(self, req, host, enabled):
138
"""Sets the specified host's ability to accept new instances."""
139
context = req.environ['nova.context']
140
state = "enabled" if enabled else "disabled"
141
LOG.audit(_("Setting host %(host)s to %(state)s.") % locals())
142
result = self.compute_api.set_host_enabled(context, host=host,
144
if result not in ("enabled", "disabled"):
145
# An error message was returned
146
raise webob.exc.HTTPBadRequest(explanation=result)
147
return {"host": host, "status": result}
149
def _host_power_action(self, req, host, action):
150
"""Reboots, shuts down or powers up the host."""
151
context = req.environ['nova.context']
153
result = self.compute_api.host_power_action(context, host=host,
155
except NotImplementedError as e:
156
raise webob.exc.HTTPBadRequest(explanation=e.msg)
157
return {"host": host, "power_action": result}
159
@wsgi.serializers(xml=HostActionTemplate)
160
def startup(self, req, id):
161
return self._host_power_action(req, host=id, action="startup")
163
@wsgi.serializers(xml=HostActionTemplate)
164
def shutdown(self, req, id):
165
return self._host_power_action(req, host=id, action="shutdown")
167
@wsgi.serializers(xml=HostActionTemplate)
168
def reboot(self, req, id):
169
return self._host_power_action(req, host=id, action="reboot")
172
class Hosts(extensions.ExtensionDescriptor):
173
"""Admin-only host administration"""
177
namespace = "http://docs.openstack.org/compute/ext/hosts/api/v1.1"
178
updated = "2011-06-29T00:00:00+00:00"
181
def get_resources(self):
182
resources = [extensions.ResourceExtension('os-hosts',
184
collection_actions={'update': 'PUT'},
185
member_actions={"startup": "GET", "shutdown": "GET",