Merge "Add additional information to servers output."
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
"compute_extension:createserverext": [],
|
||||
"compute_extension:deferred_delete": [],
|
||||
"compute_extension:disk_config": [],
|
||||
"compute_extension:extended_server_attributes": [["rule:admin_api"]],
|
||||
"compute_extension:extended_status": [["rule:admin_api"]],
|
||||
"compute_extension:flavorextradata": [],
|
||||
"compute_extension:flavorextraspecs": [],
|
||||
|
129
nova/api/openstack/compute/contrib/extended_server_attributes.py
Normal file
129
nova/api/openstack/compute/contrib/extended_server_attributes.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# Copyright 2012 Openstack, LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""The Extended Server Attributes API extension."""
|
||||
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger("nova.api.openstack.compute.contrib."
|
||||
"extended_server_attributes")
|
||||
authorize = extensions.soft_extension_authorizer('compute',
|
||||
'extended_server_attributes')
|
||||
|
||||
|
||||
class ExtendedServerAttributesController(wsgi.Controller):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ExtendedServerAttributesController, self).__init__(*args,
|
||||
**kwargs)
|
||||
self.compute_api = compute.API()
|
||||
|
||||
def _get_instances(self, context, instance_uuids):
|
||||
filters = {'uuid': instance_uuids}
|
||||
instances = self.compute_api.get_all(context, filters)
|
||||
return dict((instance['uuid'], instance) for instance in instances)
|
||||
|
||||
def _extend_server(self, server, instance):
|
||||
for attr in ['host', 'name']:
|
||||
if attr == 'name':
|
||||
key = "%s:instance_%s" % (Extended_server_attributes.alias,
|
||||
attr)
|
||||
else:
|
||||
key = "%s:%s" % (Extended_server_attributes.alias, attr)
|
||||
server[key] = instance[attr]
|
||||
|
||||
@wsgi.extends
|
||||
def show(self, req, resp_obj, id):
|
||||
context = req.environ['nova.context']
|
||||
if authorize(context):
|
||||
# Attach our slave template to the response object
|
||||
resp_obj.attach(xml=ExtendedServerAttributesTemplate())
|
||||
|
||||
try:
|
||||
instance = self.compute_api.get(context, id)
|
||||
except exception.NotFound:
|
||||
explanation = _("Server not found.")
|
||||
raise exc.HTTPNotFound(explanation=explanation)
|
||||
|
||||
self._extend_server(resp_obj.obj['server'], instance)
|
||||
|
||||
@wsgi.extends
|
||||
def detail(self, req, resp_obj):
|
||||
context = req.environ['nova.context']
|
||||
if authorize(context):
|
||||
# Attach our slave template to the response object
|
||||
resp_obj.attach(xml=ExtendedServerAttributesTemplate())
|
||||
|
||||
servers = list(resp_obj.obj['servers'])
|
||||
instance_uuids = [server['id'] for server in servers]
|
||||
instances = self._get_instances(context, instance_uuids)
|
||||
|
||||
for server_object in servers:
|
||||
try:
|
||||
instance_data = instances[server_object['id']]
|
||||
except KeyError:
|
||||
# Ignore missing instance data
|
||||
continue
|
||||
|
||||
self._extend_server(server_object, instance_data)
|
||||
|
||||
|
||||
class Extended_server_attributes(extensions.ExtensionDescriptor):
|
||||
"""Extended Server Attributes support."""
|
||||
|
||||
name = "ExtendedServerAttributes"
|
||||
alias = "OS-EXT-SRV-ATTR"
|
||||
namespace = "http://docs.openstack.org/compute/ext/" \
|
||||
"extended_status/api/v1.1"
|
||||
updated = "2011-11-03T00:00:00+00:00"
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = ExtendedServerAttributesController()
|
||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||
return [extension]
|
||||
|
||||
|
||||
def make_server(elem):
|
||||
elem.set('{%s}instance_name' % Extended_server_attributes.namespace,
|
||||
'%s:instance_name' % Extended_server_attributes.alias)
|
||||
elem.set('{%s}host' % Extended_server_attributes.namespace,
|
||||
'%s:host' % Extended_server_attributes.alias)
|
||||
|
||||
|
||||
class ExtendedServerAttributeTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('server', selector='server')
|
||||
make_server(root)
|
||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
||||
Extended_server_attributes.alias: \
|
||||
Extended_server_attributes.namespace})
|
||||
|
||||
|
||||
class ExtendedServerAttributesTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('servers')
|
||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
||||
make_server(elem)
|
||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
||||
Extended_server_attributes.alias: \
|
||||
Extended_server_attributes.namespace})
|
@@ -0,0 +1,95 @@
|
||||
# Copyright 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.
|
||||
|
||||
import json
|
||||
|
||||
import webob
|
||||
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
UUID1 = '00000000-0000-0000-0000-000000000001'
|
||||
UUID2 = '00000000-0000-0000-0000-000000000002'
|
||||
UUID3 = '00000000-0000-0000-0000-000000000003'
|
||||
|
||||
|
||||
def fake_compute_get(*args, **kwargs):
|
||||
return fakes.stub_instance(1, uuid=UUID3, host="host-fake")
|
||||
|
||||
|
||||
def fake_compute_get_all(*args, **kwargs):
|
||||
return [
|
||||
fakes.stub_instance(1, uuid=UUID1, host="host-1"),
|
||||
fakes.stub_instance(2, uuid=UUID2, host="host-2")
|
||||
]
|
||||
|
||||
|
||||
class ExtendedServerAttributesTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ExtendedServerAttributesTest, self).setUp()
|
||||
fakes.stub_out_nw_api(self.stubs)
|
||||
self.stubs.Set(compute.api.API, 'get', fake_compute_get)
|
||||
self.stubs.Set(compute.api.API, 'get_all', fake_compute_get_all)
|
||||
|
||||
def _make_request(self, url):
|
||||
req = webob.Request.blank(url)
|
||||
req.headers['Accept'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
return res
|
||||
|
||||
def assertServerAttributes(self, server, host, instance_name):
|
||||
self.assertEqual(server.get('OS-EXT-SRV-ATTR:host'), host)
|
||||
self.assertEqual(server.get('OS-EXT-SRV-ATTR:instance_name'),
|
||||
instance_name)
|
||||
|
||||
def test_show(self):
|
||||
url = '/v2/fake/servers/%s' % UUID3
|
||||
res = self._make_request(url)
|
||||
body = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertServerAttributes(body['server'],
|
||||
host='host-fake',
|
||||
instance_name='instance-1')
|
||||
|
||||
def test_detail(self):
|
||||
url = '/v2/fake/servers/detail'
|
||||
res = self._make_request(url)
|
||||
body = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
for i, server in enumerate(body['servers']):
|
||||
self.assertServerAttributes(server,
|
||||
host='host-%s' % (i + 1),
|
||||
instance_name='instance-%s' % (i + 1))
|
||||
|
||||
def test_no_instance_passthrough_404(self):
|
||||
|
||||
def fake_compute_get(*args, **kwargs):
|
||||
raise exception.InstanceNotFound()
|
||||
|
||||
self.stubs.Set(compute.api.API, 'get', fake_compute_get)
|
||||
url = '/v2/fake/servers/70f6db34-de8d-4fbd-aafb-4065bdfa6115'
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
@@ -161,6 +161,7 @@ class ExtensionControllerTest(ExtensionTestCase):
|
||||
"DeferredDelete",
|
||||
"DiskConfig",
|
||||
"ExtendedStatus",
|
||||
"ExtendedServerAttributes",
|
||||
"FlavorExtraSpecs",
|
||||
"FlavorExtraData",
|
||||
"FlavorManage",
|
||||
|
@@ -88,6 +88,7 @@
|
||||
"compute_extension:createserverext": [],
|
||||
"compute_extension:deferred_delete": [],
|
||||
"compute_extension:disk_config": [],
|
||||
"compute_extension:extended_server_attributes": [],
|
||||
"compute_extension:extended_status": [],
|
||||
"compute_extension:flavorextradata": [],
|
||||
"compute_extension:flavorextraspecs": [],
|
||||
|
Reference in New Issue
Block a user