diff --git a/nova/api/openstack/v2/contrib/security_groups.py b/nova/api/openstack/v2/contrib/security_groups.py index 50f129bd618..3c4f0e47367 100644 --- a/nova/api/openstack/v2/contrib/security_groups.py +++ b/nova/api/openstack/v2/contrib/security_groups.py @@ -24,6 +24,7 @@ from nova.api.openstack import common from nova.api.openstack.v2 import extensions from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil from nova import compute from nova import db from nova import exception @@ -355,6 +356,85 @@ def delete(self, req, id): return webob.Response(status_int=202) +def make_rule(elem): + elem.set('id') + elem.set('parent_group_id') + + proto = xmlutil.SubTemplateElement(elem, 'ip_protocol') + proto.text = 'ip_protocol' + + from_port = xmlutil.SubTemplateElement(elem, 'from_port') + from_port.text = 'from_port' + + to_port = xmlutil.SubTemplateElement(elem, 'to_port') + to_port.text = 'to_port' + + group = xmlutil.SubTemplateElement(elem, 'group', selector='group') + name = xmlutil.SubTemplateElement(group, 'name') + name.text = 'name' + tenant_id = xmlutil.SubTemplateElement(group, 'tenant_id') + tenant_id.text = 'tenant_id' + + ip_range = xmlutil.SubTemplateElement(elem, 'ip_range', + selector='ip_range') + cidr = xmlutil.SubTemplateElement(ip_range, 'cidr') + cidr.text = 'cidr' + + +def make_sg(elem): + elem.set('id') + elem.set('tenant_id') + elem.set('name') + + desc = xmlutil.SubTemplateElement(elem, 'description') + desc.text = 'description' + + rules = xmlutil.SubTemplateElement(elem, 'rules') + rule = xmlutil.SubTemplateElement(rules, 'rule', selector='rules') + make_rule(rule) + + +sg_nsmap = {None: wsgi.XMLNS_V11} + + +class SecurityGroupRuleTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('security_group_rule', + selector='security_group_rule') + make_rule(root) + return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap) + + +class SecurityGroupTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('security_group', + selector='security_group') + make_sg(root) + return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap) + + +class SecurityGroupsTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('security_groups') + elem = xmlutil.SubTemplateElement(root, 'security_group', + selector='security_groups') + make_sg(elem) + return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap) + + +class SecurityGroupXMLSerializer(xmlutil.XMLTemplateSerializer): + def index(self): + return SecurityGroupsTemplate() + + def default(self): + return SecurityGroupTemplate() + + +class SecurityGroupRulesXMLSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return SecurityGroupRuleTemplate() + + class Security_groups(extensions.ExtensionDescriptor): """Security group support""" @@ -439,10 +519,8 @@ def get_actions(self): def get_resources(self): resources = [] - metadata = _get_metadata() body_serializers = { - 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, - xmlns=wsgi.XMLNS_V11), + 'application/xml': SecurityGroupXMLSerializer(), } serializer = wsgi.ResponseSerializer(body_serializers, None) @@ -458,6 +536,11 @@ def get_resources(self): resources.append(res) + body_serializers = { + 'application/xml': SecurityGroupRulesXMLSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers, None) + body_deserializers = { 'application/xml': SecurityGroupRulesXMLDeserializer(), } @@ -538,14 +621,3 @@ def _extract_security_group_rule(self, node): sg_rule['cidr'] = self.extract_text(cidr_node) return sg_rule - - -def _get_metadata(): - metadata = { - "attributes": { - "security_group": ["id", "tenant_id", "name"], - "rule": ["id", "parent_group_id"], - "security_group_rule": ["id", "parent_group_id"], - } - } - return metadata diff --git a/nova/api/openstack/v2/contrib/simple_tenant_usage.py b/nova/api/openstack/v2/contrib/simple_tenant_usage.py index e896371ed07..4204c53bff7 100644 --- a/nova/api/openstack/v2/contrib/simple_tenant_usage.py +++ b/nova/api/openstack/v2/contrib/simple_tenant_usage.py @@ -23,6 +23,8 @@ from nova.api.openstack.v2 import extensions from nova.api.openstack.v2 import views +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil from nova.compute import api from nova.db.sqlalchemy.session import get_session from nova import exception @@ -211,6 +213,47 @@ def show(self, req, id): return {'tenant_usage': usage} +def make_usage(elem): + for subelem_tag in ('tenant_id', 'total_local_gb_usage', + 'total_vcpus_usage', 'total_memory_mb_usage', + 'total_hours', 'start', 'stop'): + subelem = xmlutil.SubTemplateElement(elem, subelem_tag) + subelem.text = subelem_tag + + server_usages = xmlutil.SubTemplateElement(elem, 'server_usages') + server_usage = xmlutil.SubTemplateElement(server_usages, 'server_usage', + selector='server_usages') + for subelem_tag in ('name', 'hours', 'memory_mb', 'local_gb', 'vcpus', + 'tenant_id', 'flavor', 'started_at', 'ended_at', + 'state', 'uptime'): + subelem = xmlutil.SubTemplateElement(server_usage, subelem_tag) + subelem.text = subelem_tag + + +class SimpleTenantUsageTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('tenant_usage', selector='tenant_usage') + make_usage(root) + return xmlutil.MasterTemplate(root, 1) + + +class SimpleTenantUsagesTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('tenant_usages') + elem = xmlutil.SubTemplateElement(root, 'tenant_usage', + selector='tenant_usages') + make_usage(elem) + return xmlutil.MasterTemplate(root, 1) + + +class SimpleTenantUsageSerializer(xmlutil.XMLTemplateSerializer): + def index(self): + return SimpleTenantUsagesTemplate() + + def show(self): + return SimpleTenantUsageTemplate() + + class Simple_tenant_usage(extensions.ExtensionDescriptor): """Simple tenant usage extension""" @@ -222,8 +265,14 @@ class Simple_tenant_usage(extensions.ExtensionDescriptor): def get_resources(self): resources = [] + body_serializers = { + 'application/xml': SimpleTenantUsageSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + res = extensions.ResourceExtension('os-simple-tenant-usage', - SimpleTenantUsageController()) + SimpleTenantUsageController(), + serializer=serializer) resources.append(res) return resources diff --git a/nova/api/openstack/v2/contrib/virtual_interfaces.py b/nova/api/openstack/v2/contrib/virtual_interfaces.py index 1975a8dc0ce..211b8c9be3e 100644 --- a/nova/api/openstack/v2/contrib/virtual_interfaces.py +++ b/nova/api/openstack/v2/contrib/virtual_interfaces.py @@ -18,6 +18,7 @@ from nova.api.openstack import common from nova.api.openstack.v2 import extensions from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil from nova import log as logging from nova import network @@ -33,13 +34,6 @@ def _translate_vif_summary_view(_context, vif): return d -def _get_metadata(): - metadata = { - "attributes": { - 'virtual_interface': ["id", "mac_address"]}} - return metadata - - class ServerVirtualInterfaceController(object): """The instance VIF API controller for the Openstack API. """ @@ -63,6 +57,24 @@ def index(self, req, server_id): entity_maker=_translate_vif_summary_view) +vif_nsmap = {None: wsgi.XMLNS_V11} + + +class VirtualInterfaceTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('virtual_interfaces') + elem = xmlutil.SubTemplateElement(root, 'virtual_interface', + selector='virtual_interfaces') + elem.set('id') + elem.set('mac_address') + return xmlutil.MasterTemplate(root, 1, nsmap=vif_nsmap) + + +class VirtualInterfaceSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return VirtualInterfaceTemplate() + + class Virtual_interfaces(extensions.ExtensionDescriptor): """Virtual interface support""" @@ -74,10 +86,9 @@ class Virtual_interfaces(extensions.ExtensionDescriptor): def get_resources(self): resources = [] - metadata = _get_metadata() body_serializers = { - 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, - xmlns=wsgi.XMLNS_V11)} + 'application/xml': VirtualInterfaceSerializer() + } serializer = wsgi.ResponseSerializer(body_serializers, None) res = extensions.ResourceExtension( 'os-virtual-interfaces', diff --git a/nova/api/openstack/v2/contrib/volumes.py b/nova/api/openstack/v2/contrib/volumes.py index 5d86282f213..75d8af803e9 100644 --- a/nova/api/openstack/v2/contrib/volumes.py +++ b/nova/api/openstack/v2/contrib/volumes.py @@ -21,6 +21,8 @@ from nova.api.openstack import common from nova.api.openstack.v2 import extensions from nova.api.openstack.v2 import servers +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil from nova import compute from nova import db from nova import exception @@ -86,21 +88,6 @@ def _translate_volume_summary_view(context, vol): class VolumeController(object): """The Volumes API controller for the OpenStack API.""" - _serialization_metadata = { - 'application/xml': { - "attributes": { - "volume": [ - "id", - "status", - "size", - "availabilityZone", - "createdAt", - "displayName", - "displayDescription", - "volumeType", - "metadata", - ]}}} - def __init__(self): self.volume_api = volume.API() super(VolumeController, self).__init__() @@ -180,6 +167,51 @@ def create(self, req, body): return {'volume': retval} +def make_volume(elem): + elem.set('id') + elem.set('status') + elem.set('size') + elem.set('availabilityZone') + elem.set('createdAt') + elem.set('displayName') + elem.set('displayDescription') + elem.set('volumeType') + + attachments = xmlutil.SubTemplateElement(elem, 'attachments') + attachment = xmlutil.SubTemplateElement(attachments, 'attachment', + selector='attachments') + make_attachment(attachment) + + metadata = xmlutil.make_flat_dict('metadata') + elem.append(metadata) + + +class VolumeTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volume', selector='volume') + make_volume(root) + return xmlutil.MasterTemplate(root, 1) + + +class VolumesTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volumes') + elem = xmlutil.SubTemplateElement(root, 'volume', selector='volumes') + make_volume(elem) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return VolumeTemplate() + + def index(self): + return VolumesTemplate() + + def detail(self): + return VolumesTemplate() + + def _translate_attachment_detail_view(_context, vol): """Maps keys for attachment details view.""" @@ -216,14 +248,6 @@ class VolumeAttachmentController(object): """ - _serialization_metadata = { - 'application/xml': { - 'attributes': { - 'volumeAttachment': ['id', - 'serverId', - 'volumeId', - 'device']}}} - def __init__(self): self.compute_api = compute.API() self.volume_api = volume.API() @@ -331,6 +355,38 @@ def _items(self, req, server_id, entity_maker): return {'volumeAttachments': res} +def make_attachment(elem): + elem.set('id') + elem.set('serverId') + elem.set('volumeId') + elem.set('device') + + +class VolumeAttachmentTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volumeAttachment', + selector='volumeAttachment') + make_attachment(root) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeAttachmentsTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volumeAttachments') + elem = xmlutil.SubTemplateElement(root, 'volumeAttachment', + selector='volumeAttachments') + make_attachment(elem) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeAttachmentSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return VolumeAttachmentTemplate() + + def index(self): + return VolumeAttachmentsTemplate() + + class BootFromVolumeController(servers.Controller): """The boot from volume API controller for the Openstack API.""" @@ -349,22 +405,48 @@ class Volumes(extensions.ExtensionDescriptor): def get_resources(self): resources = [] + body_serializers = { + 'application/xml': VolumeSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + # NOTE(justinsb): No way to provide singular name ('volume') # Does this matter? res = extensions.ResourceExtension('os-volumes', VolumeController(), + serializer=serializer, collection_actions={'detail': 'GET'}) resources.append(res) + body_serializers = { + 'application/xml': VolumeAttachmentSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + res = extensions.ResourceExtension('os-volume_attachments', VolumeAttachmentController(), + serializer=serializer, parent=dict( member_name='server', collection_name='servers')) resources.append(res) + headers_serializer = servers.HeadersSerializer() + body_serializers = { + 'application/xml': servers.ServerXMLSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers, + headers_serializer) + + body_deserializers = { + 'application/xml': servers.ServerXMLDeserializer(), + } + deserializer = wsgi.RequestDeserializer(body_deserializers) + res = extensions.ResourceExtension('os-volumes_boot', - BootFromVolumeController()) + BootFromVolumeController(), + serializer=serializer, + deserializer=deserializer) resources.append(res) return resources diff --git a/nova/api/openstack/v2/contrib/volumetypes.py b/nova/api/openstack/v2/contrib/volumetypes.py index ca3866c99aa..24ac87d014e 100644 --- a/nova/api/openstack/v2/contrib/volumetypes.py +++ b/nova/api/openstack/v2/contrib/volumetypes.py @@ -21,6 +21,7 @@ from nova.api.openstack.v2 import extensions from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil from nova import db from nova import exception from nova.volume import volume_types @@ -89,6 +90,37 @@ def _handle_quota_error(self, error): raise error +def make_voltype(elem): + elem.set('id') + elem.set('name') + extra_specs = xmlutil.make_flat_dict('extra_specs', selector='extra_specs') + elem.append(extra_specs) + + +class VolumeTypeTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volume_type', selector='volume_type') + make_voltype(root) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeTypesTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volume_types') + sel = lambda obj, do_raise=False: obj.values() + elem = xmlutil.SubTemplateElement(root, 'volume_type', selector=sel) + make_voltype(elem) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeTypesSerializer(xmlutil.XMLTemplateSerializer): + def index(self): + return VolumeTypesTemplate() + + def default(self): + return VolumeTypeTemplate() + + class VolumeTypeExtraSpecsController(object): """ The volume type extra specs API controller for the Openstack API """ @@ -160,6 +192,40 @@ def _handle_quota_error(self, error): raise error +class VolumeTypeExtraSpecsTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.make_flat_dict('extra_specs', selector='extra_specs') + return xmlutil.MasterTemplate(root, 1) + + +class VolumeTypeExtraSpecTemplate(xmlutil.TemplateBuilder): + def construct(self): + tagname = xmlutil.Selector('key') + + def extraspec_sel(obj, do_raise=False): + # Have to extract the key and value for later use... + key, value = obj.items()[0] + return dict(key=key, value=value) + + root = xmlutil.TemplateElement(tagname, selector=extraspec_sel) + root.text = 'value' + return xmlutil.MasterTemplate(root, 1) + + +class VolumeTypeExtraSpecsSerializer(xmlutil.XMLTemplateSerializer): + def index(self): + return VolumeTypeExtraSpecsTemplate() + + def create(self): + return VolumeTypeExtraSpecsTemplate() + + def update(self): + return VolumeTypeExtraSpecTemplate() + + def show(self): + return VolumeTypeExtraSpecTemplate() + + class Volumetypes(extensions.ExtensionDescriptor): """Volume types support""" @@ -170,13 +236,26 @@ class Volumetypes(extensions.ExtensionDescriptor): def get_resources(self): resources = [] + + body_serializers = { + 'application/xml': VolumeTypesSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + res = extensions.ResourceExtension( 'os-volume-types', - VolumeTypesController()) + VolumeTypesController(), + serializer=serializer) resources.append(res) + body_serializers = { + 'application/xml': VolumeTypeExtraSpecsSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + res = extensions.ResourceExtension('extra_specs', VolumeTypeExtraSpecsController(), + serializer=serializer, parent=dict( member_name='vol_type', collection_name='os-volume-types')) diff --git a/nova/tests/api/openstack/v2/contrib/test_security_groups.py b/nova/tests/api/openstack/v2/contrib/test_security_groups.py index 9055e1bca69..b4ef608d85c 100644 --- a/nova/tests/api/openstack/v2/contrib/test_security_groups.py +++ b/nova/tests/api/openstack/v2/contrib/test_security_groups.py @@ -16,10 +16,12 @@ import unittest +from lxml import etree import mox import webob from nova.api.openstack.v2.contrib import security_groups +from nova.api.openstack import wsgi import nova.db from nova import exception from nova import utils @@ -845,3 +847,167 @@ def test_create_no_name_request(self): }, } self.assertEquals(request['body'], expected) + + +class TestSecurityGroupXMLSerializer(unittest.TestCase): + def setUp(self): + self.namespace = wsgi.XMLNS_V11 + tmp = security_groups.SecurityGroupRulesXMLSerializer() + self.rule_serializer = tmp + self.group_serializer = security_groups.SecurityGroupXMLSerializer() + + def _tag(self, elem): + tagname = elem.tag + self.assertEqual(tagname[0], '{') + tmp = tagname.partition('}') + namespace = tmp[0][1:] + self.assertEqual(namespace, self.namespace) + return tmp[2] + + def _verify_security_group_rule(self, raw_rule, tree): + self.assertEqual(raw_rule['id'], tree.get('id')) + self.assertEqual(raw_rule['parent_group_id'], + tree.get('parent_group_id')) + + seen = set() + expected = set(['ip_protocol', 'from_port', 'to_port', + 'group', 'group/name', 'group/tenant_id', + 'ip_range', 'ip_range/cidr']) + + for child in tree: + child_tag = self._tag(child) + self.assertTrue(child_tag in raw_rule) + seen.add(child_tag) + if child_tag in ('group', 'ip_range'): + for gr_child in child: + gr_child_tag = self._tag(gr_child) + self.assertTrue(gr_child_tag in raw_rule[child_tag]) + seen.add('%s/%s' % (child_tag, gr_child_tag)) + self.assertEqual(gr_child.text, + raw_rule[child_tag][gr_child_tag]) + else: + self.assertEqual(child.text, raw_rule[child_tag]) + self.assertEqual(seen, expected) + + def _verify_security_group(self, raw_group, tree): + rules = raw_group['rules'] + self.assertEqual('security_group', self._tag(tree)) + self.assertEqual(raw_group['id'], tree.get('id')) + self.assertEqual(raw_group['tenant_id'], tree.get('tenant_id')) + self.assertEqual(raw_group['name'], tree.get('name')) + self.assertEqual(2, len(tree)) + for child in tree: + child_tag = self._tag(child) + if child_tag == 'rules': + self.assertEqual(2, len(child)) + for idx, gr_child in enumerate(child): + self.assertEqual(self._tag(gr_child), 'rule') + self._verify_security_group_rule(rules[idx], gr_child) + else: + self.assertEqual('description', child_tag) + self.assertEqual(raw_group['description'], child.text) + + def test_rule_serializer(self): + raw_rule = dict( + id='123', + parent_group_id='456', + ip_protocol='tcp', + from_port='789', + to_port='987', + group=dict(name='group', tenant_id='tenant'), + ip_range=dict(cidr='10.0.0.0/8')) + rule = dict(security_group_rule=raw_rule) + text = self.rule_serializer.serialize(rule) + + print text + tree = etree.fromstring(text) + + self.assertEqual('security_group_rule', self._tag(tree)) + self._verify_security_group_rule(raw_rule, tree) + + def test_group_serializer(self): + rules = [dict( + id='123', + parent_group_id='456', + ip_protocol='tcp', + from_port='789', + to_port='987', + group=dict(name='group1', tenant_id='tenant1'), + ip_range=dict(cidr='10.55.44.0/24')), + dict( + id='654', + parent_group_id='321', + ip_protocol='udp', + from_port='234', + to_port='567', + group=dict(name='group2', tenant_id='tenant2'), + ip_range=dict(cidr='10.44.55.0/24'))] + raw_group = dict( + id='890', + description='description', + name='name', + tenant_id='tenant', + rules=rules) + sg_group = dict(security_group=raw_group) + text = self.group_serializer.serialize(sg_group) + + print text + tree = etree.fromstring(text) + + self._verify_security_group(raw_group, tree) + + def test_groups_serializer(self): + rules = [dict( + id='123', + parent_group_id='1234', + ip_protocol='tcp', + from_port='12345', + to_port='123456', + group=dict(name='group1', tenant_id='tenant1'), + ip_range=dict(cidr='10.123.0.0/24')), + dict( + id='234', + parent_group_id='2345', + ip_protocol='udp', + from_port='23456', + to_port='234567', + group=dict(name='group2', tenant_id='tenant2'), + ip_range=dict(cidr='10.234.0.0/24')), + dict( + id='345', + parent_group_id='3456', + ip_protocol='tcp', + from_port='34567', + to_port='345678', + group=dict(name='group3', tenant_id='tenant3'), + ip_range=dict(cidr='10.345.0.0/24')), + dict( + id='456', + parent_group_id='4567', + ip_protocol='udp', + from_port='45678', + to_port='456789', + group=dict(name='group4', tenant_id='tenant4'), + ip_range=dict(cidr='10.456.0.0/24'))] + groups = [dict( + id='567', + description='description1', + name='name1', + tenant_id='tenant1', + rules=rules[0:2]), + dict( + id='678', + description='description2', + name='name2', + tenant_id='tenant2', + rules=rules[2:4])] + sg_groups = dict(security_groups=groups) + text = self.group_serializer.serialize(sg_groups, 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('security_groups', self._tag(tree)) + self.assertEqual(len(groups), len(tree)) + for idx, child in enumerate(tree): + self._verify_security_group(groups[idx], child) diff --git a/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py b/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py index 56d8de7d29f..5d36518a6c8 100644 --- a/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py +++ b/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py @@ -17,8 +17,11 @@ import datetime import json + +from lxml import etree import webob +from nova.api.openstack.v2.contrib import simple_tenant_usage from nova.compute import api from nova import context from nova import flags @@ -170,3 +173,168 @@ def test_verify_show_cant_view_other_tenant(self): res = req.get_response(fakes.wsgi_app( fake_auth_context=self.alt_user_context)) self.assertEqual(res.status_int, 403) + + +class SimpleTenantUsageSerializerTest(test.TestCase): + def setUp(self): + super(SimpleTenantUsageSerializerTest, self).setUp() + self.serializer = simple_tenant_usage.SimpleTenantUsageSerializer() + + def _verify_server_usage(self, raw_usage, tree): + self.assertEqual('server_usage', tree.tag) + + # Figure out what fields we expect + not_seen = set(raw_usage.keys()) + + for child in tree: + self.assertTrue(child.tag in not_seen) + not_seen.remove(child.tag) + self.assertEqual(str(raw_usage[child.tag]), child.text) + + self.assertEqual(len(not_seen), 0) + + def _verify_tenant_usage(self, raw_usage, tree): + self.assertEqual('tenant_usage', tree.tag) + + # Figure out what fields we expect + not_seen = set(raw_usage.keys()) + + for child in tree: + self.assertTrue(child.tag in not_seen) + not_seen.remove(child.tag) + if child.tag == 'server_usages': + for idx, gr_child in enumerate(child): + self._verify_server_usage(raw_usage['server_usages'][idx], + gr_child) + else: + self.assertEqual(str(raw_usage[child.tag]), child.text) + + self.assertEqual(len(not_seen), 0) + + def test_serializer_show(self): + today = datetime.datetime.now() + yesterday = today - datetime.timedelta(days=1) + raw_usage = dict( + tenant_id='tenant', + total_local_gb_usage=789, + total_vcpus_usage=456, + total_memory_mb_usage=123, + total_hours=24, + start=yesterday, + stop=today, + server_usages=[dict( + name='test', + hours=24, + memory_mb=1024, + local_gb=50, + vcpus=1, + tenant_id='tenant', + flavor='m1.small', + started_at=yesterday, + ended_at=today, + state='terminated', + uptime=86400), + dict( + name='test2', + hours=12, + memory_mb=512, + local_gb=25, + vcpus=2, + tenant_id='tenant', + flavor='m1.tiny', + started_at=yesterday, + ended_at=today, + state='terminated', + uptime=43200), + ], + ) + tenant_usage = dict(tenant_usage=raw_usage) + text = self.serializer.serialize(tenant_usage, 'show') + + print text + tree = etree.fromstring(text) + + self._verify_tenant_usage(raw_usage, tree) + + def test_serializer_index(self): + today = datetime.datetime.now() + yesterday = today - datetime.timedelta(days=1) + raw_usages = [dict( + tenant_id='tenant1', + total_local_gb_usage=1024, + total_vcpus_usage=23, + total_memory_mb_usage=512, + total_hours=24, + start=yesterday, + stop=today, + server_usages=[dict( + name='test1', + hours=24, + memory_mb=1024, + local_gb=50, + vcpus=2, + tenant_id='tenant1', + flavor='m1.small', + started_at=yesterday, + ended_at=today, + state='terminated', + uptime=86400), + dict( + name='test2', + hours=42, + memory_mb=4201, + local_gb=25, + vcpus=1, + tenant_id='tenant1', + flavor='m1.tiny', + started_at=today, + ended_at=yesterday, + state='terminated', + uptime=43200), + ], + ), + dict( + tenant_id='tenant2', + total_local_gb_usage=512, + total_vcpus_usage=32, + total_memory_mb_usage=1024, + total_hours=42, + start=today, + stop=yesterday, + server_usages=[dict( + name='test3', + hours=24, + memory_mb=1024, + local_gb=50, + vcpus=2, + tenant_id='tenant2', + flavor='m1.small', + started_at=yesterday, + ended_at=today, + state='terminated', + uptime=86400), + dict( + name='test2', + hours=42, + memory_mb=4201, + local_gb=25, + vcpus=1, + tenant_id='tenant4', + flavor='m1.tiny', + started_at=today, + ended_at=yesterday, + state='terminated', + uptime=43200), + ], + ), + ] + tenant_usages = dict(tenant_usages=raw_usages) + text = self.serializer.serialize(tenant_usages, 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('tenant_usages', tree.tag) + self.assertEqual(len(raw_usages), len(tree)) + for idx, child in enumerate(tree): + self._verify_tenant_usage(raw_usages[idx], child) diff --git a/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py index 3e03fed340f..47f085b00a7 100644 --- a/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py +++ b/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py @@ -15,10 +15,11 @@ import json +from lxml import etree import webob -from nova.api.openstack.v2.contrib.virtual_interfaces import \ - ServerVirtualInterfaceController +from nova.api.openstack.v2.contrib import virtual_interfaces +from nova.api.openstack import wsgi from nova import network from nova import test from nova.tests.api.openstack import fakes @@ -35,7 +36,7 @@ class ServerVirtualInterfaceTest(test.TestCase): def setUp(self): super(ServerVirtualInterfaceTest, self).setUp() - self.controller = ServerVirtualInterfaceController() + self.controller = virtual_interfaces.ServerVirtualInterfaceController() self.stubs.Set(network.api.API, "get_vifs_by_instance", get_vifs_by_instance) @@ -54,3 +55,39 @@ def test_get_virtual_interfaces_list(self): {'id': '11111111-1111-1111-1111-11111111111111111', 'mac_address': '11-11-11-11-11-11'}]} self.assertEqual(res_dict, response) + + +class ServerVirtualInterfaceSerializerTest(test.TestCase): + def setUp(self): + super(ServerVirtualInterfaceSerializerTest, self).setUp() + self.namespace = wsgi.XMLNS_V11 + self.serializer = virtual_interfaces.VirtualInterfaceSerializer() + + def _tag(self, elem): + tagname = elem.tag + self.assertEqual(tagname[0], '{') + tmp = tagname.partition('}') + namespace = tmp[0][1:] + self.assertEqual(namespace, self.namespace) + return tmp[2] + + def test_serializer(self): + raw_vifs = [dict( + id='uuid1', + mac_address='aa:bb:cc:dd:ee:ff'), + dict( + id='uuid2', + mac_address='bb:aa:dd:cc:ff:ee')] + vifs = dict(virtual_interfaces=raw_vifs) + text = self.serializer.serialize(vifs) + + print text + tree = etree.fromstring(text) + + self.assertEqual('virtual_interfaces', self._tag(tree)) + self.assertEqual(len(raw_vifs), len(tree)) + for idx, child in enumerate(tree): + self.assertEqual('virtual_interface', self._tag(child)) + self.assertEqual(raw_vifs[idx]['id'], child.get('id')) + self.assertEqual(raw_vifs[idx]['mac_address'], + child.get('mac_address')) diff --git a/nova/tests/api/openstack/v2/contrib/test_volume_types.py b/nova/tests/api/openstack/v2/contrib/test_volume_types.py index db7b4ee71df..4343a6d0402 100644 --- a/nova/tests/api/openstack/v2/contrib/test_volume_types.py +++ b/nova/tests/api/openstack/v2/contrib/test_volume_types.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from lxml import etree import webob from nova.api.openstack.v2.contrib import volumetypes @@ -162,3 +163,47 @@ def test_create_empty_body(self): req = fakes.HTTPRequest.blank('/v2/123/os-volume-types') self.assertRaises(webob.exc.HTTPUnprocessableEntity, self.controller.create, req, '') + + +class VolumeTypesSerializerTest(test.TestCase): + def setUp(self): + super(VolumeTypesSerializerTest, self).setUp() + self.serializer = volumetypes.VolumeTypesSerializer() + + def _verify_volume_type(self, vtype, tree): + self.assertEqual('volume_type', tree.tag) + self.assertEqual(vtype['name'], tree.get('name')) + self.assertEqual(str(vtype['id']), tree.get('id')) + self.assertEqual(1, len(tree)) + extra_specs = tree[0] + self.assertEqual('extra_specs', extra_specs.tag) + seen = set(vtype['extra_specs'].keys()) + for child in extra_specs: + self.assertTrue(child.tag in seen) + self.assertEqual(vtype['extra_specs'][child.tag], child.text) + seen.remove(child.tag) + self.assertEqual(len(seen), 0) + + def test_index_serializer(self): + # Just getting some input data + vtypes = return_volume_types_get_all_types(None) + text = self.serializer.serialize(vtypes, 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volume_types', tree.tag) + self.assertEqual(len(vtypes), len(tree)) + for child in tree: + name = child.get('name') + self.assertTrue(name in vtypes) + self._verify_volume_type(vtypes[name], child) + + def test_voltype_serializer(self): + vtype = stub_volume_type(1) + text = self.serializer.serialize(dict(volume_type=vtype)) + + print text + tree = etree.fromstring(text) + + self._verify_volume_type(vtype, tree) diff --git a/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py b/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py index 9947ff9c1bc..41b50118f83 100644 --- a/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py +++ b/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py @@ -17,6 +17,7 @@ # License for the specific language governing permissions and limitations # under the License. +from lxml import etree import webob from nova.api.openstack.v2.contrib import volumetypes @@ -161,3 +162,38 @@ def test_update_item_body_uri_mismatch(self): req = fakes.HTTPRequest.blank(self.api_path + '/bad') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, req, 1, 'bad', body) + + +class VolumeTypeExtraSpecsSerializerTest(test.TestCase): + def setUp(self): + super(VolumeTypeExtraSpecsSerializerTest, self).setUp() + self.serializer = volumetypes.VolumeTypeExtraSpecsSerializer() + + def test_index_create_serializer(self): + # Just getting some input data + extra_specs = stub_volume_type_extra_specs() + text = self.serializer.serialize(dict(extra_specs=extra_specs), + 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('extra_specs', tree.tag) + self.assertEqual(len(extra_specs), len(tree)) + seen = set(extra_specs.keys()) + for child in tree: + self.assertTrue(child.tag in seen) + self.assertEqual(extra_specs[child.tag], child.text) + seen.remove(child.tag) + self.assertEqual(len(seen), 0) + + def test_update_show_serializer(self): + exemplar = dict(key1='value1') + text = self.serializer.serialize(exemplar, 'update') + + print text + tree = etree.fromstring(text) + + self.assertEqual('key1', tree.tag) + self.assertEqual('value1', tree.text) + self.assertEqual(0, len(tree)) diff --git a/nova/tests/api/openstack/v2/contrib/test_volumes.py b/nova/tests/api/openstack/v2/contrib/test_volumes.py index 2842b51514b..f0e7a03b728 100644 --- a/nova/tests/api/openstack/v2/contrib/test_volumes.py +++ b/nova/tests/api/openstack/v2/contrib/test_volumes.py @@ -16,9 +16,11 @@ import datetime import json +from lxml import etree import webob import nova +from nova.api.openstack.v2.contrib import volumes from nova.compute import instance_types from nova import flags from nova import test @@ -79,7 +81,7 @@ def test_create_root_volume(self): req.body = json.dumps(body) req.headers['content-type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) + self.assertEqual(res.status_int, 202) server = json.loads(res.body)['server'] self.assertEqual(FAKE_UUID, server['id']) self.assertEqual(FLAGS.password_length, len(server['adminPass'])) @@ -87,3 +89,148 @@ def test_create_root_volume(self): self.assertEqual(_block_device_mapping_seen[0]['volume_id'], 1) self.assertEqual(_block_device_mapping_seen[0]['device_name'], '/dev/vda') + + +class VolumeSerializerTest(test.TestCase): + def _verify_volume_attachment(self, attach, tree): + for attr in ('id', 'volumeId', 'serverId', 'device'): + self.assertEqual(str(attach[attr]), tree.get(attr)) + + def _verify_volume(self, vol, tree): + self.assertEqual(tree.tag, 'volume') + + for attr in ('id', 'status', 'size', 'availabilityZone', 'createdAt', + 'displayName', 'displayDescription', 'volumeType'): + self.assertEqual(str(vol[attr]), tree.get(attr)) + + for child in tree: + self.assertTrue(child.tag in ('attachments', 'metadata')) + if child.tag == 'attachments': + self.assertEqual(1, len(child)) + self.assertEqual('attachment', child[0].tag) + self._verify_volume_attachment(vol['attachments'][0], child[0]) + elif child.tag == 'metadata': + not_seen = set(vol['metadata'].keys()) + for gr_child in child: + self.assertTrue(gr_child.tag in not_seen) + self.assertEqual(str(vol['metadata'][gr_child.tag]), + gr_child.text) + not_seen.remove(gr_child.tag) + self.assertEqual(0, len(not_seen)) + + def test_attach_show_create_serializer(self): + serializer = volumes.VolumeAttachmentSerializer() + raw_attach = dict( + id='vol_id', + volumeId='vol_id', + serverId='instance_uuid', + device='/foo') + text = serializer.serialize(dict(volumeAttachment=raw_attach), 'show') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volumeAttachment', tree.tag) + self._verify_volume_attachment(raw_attach, tree) + + def test_attach_index_serializer(self): + serializer = volumes.VolumeAttachmentSerializer() + raw_attaches = [dict( + id='vol_id1', + volumeId='vol_id1', + serverId='instance1_uuid', + device='/foo1'), + dict( + id='vol_id2', + volumeId='vol_id2', + serverId='instance2_uuid', + device='/foo2')] + text = serializer.serialize(dict(volumeAttachments=raw_attaches), + 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volumeAttachments', tree.tag) + self.assertEqual(len(raw_attaches), len(tree)) + for idx, child in enumerate(tree): + self.assertEqual('volumeAttachment', child.tag) + self._verify_volume_attachment(raw_attaches[idx], child) + + def test_volume_show_create_serializer(self): + serializer = volumes.VolumeSerializer() + raw_volume = dict( + id='vol_id', + status='vol_status', + size=1024, + availabilityZone='vol_availability', + createdAt=datetime.datetime.now(), + attachments=[dict( + id='vol_id', + volumeId='vol_id', + serverId='instance_uuid', + device='/foo')], + displayName='vol_name', + displayDescription='vol_desc', + volumeType='vol_type', + metadata=dict( + foo='bar', + baz='quux', + ), + ) + text = serializer.serialize(dict(volume=raw_volume), 'show') + + print text + tree = etree.fromstring(text) + + self._verify_volume(raw_volume, tree) + + def test_volume_index_detail_serializer(self): + serializer = volumes.VolumeSerializer() + raw_volumes = [dict( + id='vol1_id', + status='vol1_status', + size=1024, + availabilityZone='vol1_availability', + createdAt=datetime.datetime.now(), + attachments=[dict( + id='vol1_id', + volumeId='vol1_id', + serverId='instance_uuid', + device='/foo1')], + displayName='vol1_name', + displayDescription='vol1_desc', + volumeType='vol1_type', + metadata=dict( + foo='vol1_foo', + bar='vol1_bar', + ), + ), + dict( + id='vol2_id', + status='vol2_status', + size=1024, + availabilityZone='vol2_availability', + createdAt=datetime.datetime.now(), + attachments=[dict( + id='vol2_id', + volumeId='vol2_id', + serverId='instance_uuid', + device='/foo2')], + displayName='vol2_name', + displayDescription='vol2_desc', + volumeType='vol2_type', + metadata=dict( + foo='vol2_foo', + bar='vol2_bar', + ), + )] + text = serializer.serialize(dict(volumes=raw_volumes), 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volumes', tree.tag) + self.assertEqual(len(raw_volumes), len(tree)) + for idx, child in enumerate(tree): + self._verify_volume(raw_volumes[idx], child)