From 0834f3d64b2cc37407c24a9b717e218d758adf79 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 4 Jul 2011 15:14:36 -0400 Subject: properly displays addresses in each network, not just public/private; adding addresses attribute to server entities --- nova/api/openstack/__init__.py | 11 ++--- nova/api/openstack/ips.py | 79 ++++++++++++++++++++++++++++------- nova/api/openstack/servers.py | 31 ++++++++------ nova/api/openstack/views/addresses.py | 38 +++++++++++++---- nova/api/openstack/views/servers.py | 13 +++++- 5 files changed, 129 insertions(+), 43 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index f24017df0..3e4d87ee0 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -144,9 +144,6 @@ class APIRouterV10(APIRouter): def _setup_routes(self, mapper): super(APIRouterV10, self)._setup_routes(mapper, '1.0') - mapper.resource("image", "images", - controller=images.create_resource('1.0'), - collection={'detail': 'GET'}) mapper.resource("shared_ip_group", "shared_ip_groups", collection={'detail': 'GET'}, @@ -157,8 +154,8 @@ class APIRouterV10(APIRouter): parent_resource=dict(member_name='server', collection_name='servers')) - mapper.resource("ip", "ips", controller=ips.create_resource(), - collection=dict(public='GET', private='GET'), + mapper.resource("ip", "ips", controller=ips.create_resource('1.0'), + member=dict(public='GET', private='GET'), parent_resource=dict(member_name='server', collection_name='servers')) @@ -177,3 +174,7 @@ class APIRouterV11(APIRouter): controller=server_metadata.create_resource(), parent_resource=dict(member_name='server', collection_name='servers')) + + mapper.resource("ip", "ips", controller=ips.create_resource('1.1'), + parent_resource=dict(member_name='server', + collection_name='servers')) diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py index 71646b6d3..b83e2f9b3 100644 --- a/nova/api/openstack/ips.py +++ b/nova/api/openstack/ips.py @@ -23,6 +23,7 @@ import nova from nova.api.openstack import faults import nova.api.openstack.views.addresses from nova.api.openstack import wsgi +from nova import db class Controller(object): @@ -30,7 +31,6 @@ class Controller(object): def __init__(self): self.compute_api = nova.compute.API() - self.builder = nova.api.openstack.views.addresses.ViewBuilderV10() def _get_instance(self, req, server_id): try: @@ -40,29 +40,78 @@ class Controller(object): return faults.Fault(exc.HTTPNotFound()) return instance + def create(self, req, server_id, body): + return faults.Fault(exc.HTTPNotImplemented()) + + def delete(self, req, server_id, id): + return faults.Fault(exc.HTTPNotImplemented()) + + +class ControllerV10(Controller): + def index(self, req, server_id): instance = self._get_instance(req, server_id) - return {'addresses': self.builder.build(instance)} + builder = nova.api.openstack.views.addresses.ViewBuilderV10() + return {'addresses': builder.build(instance)} - def public(self, req, server_id): + def show(self, req, server_id, id): instance = self._get_instance(req, server_id) - return {'public': self.builder.build_public_parts(instance)} + builder = self._get_view_builder(req) + if id == 'private': + view = builder.build_private_parts(instance) + elif id == 'public': + view = builder.build_public_parts(instance) + else: + msg = _("Only private and public networks available") + return faults.Fault(exc.HTTPNotFound(explanation=msg)) - def private(self, req, server_id): - instance = self._get_instance(req, server_id) - return {'private': self.builder.build_private_parts(instance)} + return {id: view} + + def _get_view_builder(self, req): + return nova.api.openstack.views.addresses.ViewBuilderV10() + + +class ControllerV11(Controller): + + def index(self, req, server_id): + context = req.environ['nova.context'] + interfaces = self._get_virtual_interfaces(context, server_id) + networks = self._get_view_builder(req).build(interfaces) + return {'addresses': networks} def show(self, req, server_id, id): - return faults.Fault(exc.HTTPNotImplemented()) + context = req.environ['nova.context'] + interfaces = self._get_virtual_interfaces(context, server_id) + network = self._get_view_builder(req).build_network(interfaces, id) - def create(self, req, server_id, body): - return faults.Fault(exc.HTTPNotImplemented()) + if network is None: + msg = _("Instance is not a member of specified network") + return faults.Fault(exc.HTTPNotFound(explanation=msg)) - def delete(self, req, server_id, id): - return faults.Fault(exc.HTTPNotImplemented()) + return network + + def _get_virtual_interfaces(self, context, server_id): + try: + return db.api.virtual_interface_get_by_instance(context, server_id) + except exception.InstanceNotFound: + msg = _("Instance does not exist") + raise exc.HTTPNotFound(explanation=msg) + + def _get_view_builder(self, req): + return nova.api.openstack.views.addresses.ViewBuilderV11() + + +def create_resource(version): + controller = { + '1.0': ControllerV10, + '1.1': ControllerV11, + }[version]() + xmlns = { + '1.0': wsgi.XMLNS_V10, + '1.1': wsgi.XMLNS_V11, + }[version] -def create_resource(): metadata = { 'list_collections': { 'public': {'item_name': 'ip', 'item_key': 'addr'}, @@ -72,7 +121,7 @@ def create_resource(): serializers = { 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, - xmlns=wsgi.XMLNS_V10), + xmlns=xmlns), } - return wsgi.Resource(Controller(), serializers=serializers) + return wsgi.Resource(controller, serializers=serializers) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index fc1ab8d46..3c29c2606 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -19,6 +19,7 @@ import traceback from webob import exc from nova import compute +from nova import db from nova import exception from nova import flags from nova import log as logging @@ -62,7 +63,7 @@ class Controller(object): return exc.HTTPBadRequest(explanation=str(err)) return servers - def _get_view_builder(self, req): + def _build_view(self, req, instance, is_detail=False): raise NotImplementedError() def _limit_items(self, items, req): @@ -88,8 +89,7 @@ class Controller(object): fixed_ip=fixed_ip, recurse_zones=recurse_zones) limited_list = self._limit_items(instance_list, req) - builder = self._get_view_builder(req) - servers = [builder.build(inst, is_detail)['server'] + servers = [self._build_view(req, inst, is_detail)['server'] for inst in limited_list] return dict(servers=servers) @@ -99,8 +99,7 @@ class Controller(object): try: instance = self.compute_api.routing_get( req.environ['nova.context'], id) - builder = self._get_view_builder(req) - return builder.build(instance, is_detail=True) + return self._build_view(req, instance, is_detail=True) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) @@ -130,8 +129,7 @@ class Controller(object): for key in ['instance_type', 'image_ref']: inst[key] = extra_values[key] - builder = self._get_view_builder(req) - server = builder.build(inst, is_detail=True) + server = self._build_view(req, inst, is_detail=True) server['server']['adminPass'] = extra_values['password'] return server @@ -418,10 +416,10 @@ class ControllerV10(Controller): def _flavor_id_from_req_data(self, data): return data['server']['flavorId'] - def _get_view_builder(self, req): - addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV10() - return nova.api.openstack.views.servers.ViewBuilderV10( - addresses_builder) + def _build_view(self, req, instance, is_detail=False): + addresses = nova.api.openstack.views.addresses.ViewBuilderV10() + builder = nova.api.openstack.views.servers.ViewBuilderV10(addresses) + return builder.build(instance, is_detail=is_detail) def _limit_items(self, items, req): return common.limited(items, req) @@ -481,16 +479,23 @@ class ControllerV11(Controller): href = data['server']['flavorRef'] return common.get_id_from_href(href) - def _get_view_builder(self, req): + def _build_view(self, req, instance, is_detail=False): base_url = req.application_url flavor_builder = nova.api.openstack.views.flavors.ViewBuilderV11( base_url) image_builder = nova.api.openstack.views.images.ViewBuilderV11( base_url) addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV11() - return nova.api.openstack.views.servers.ViewBuilderV11( + builder = nova.api.openstack.views.servers.ViewBuilderV11( addresses_builder, flavor_builder, image_builder, base_url) + context = req.environ['nova.context'] + interfaces = db.api.virtual_interface_get_by_instance(context, + instance['id']) + instance['virtual_interfaces'] = interfaces + + return builder.build(instance, is_detail=is_detail) + def _action_change_password(self, input_dict, req, id): context = req.environ['nova.context'] if (not 'changePassword' in input_dict diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index b59eb4751..c7464a7c5 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -20,13 +20,14 @@ from nova.api.openstack import common class ViewBuilder(object): - ''' Models a server addresses response as a python dictionary.''' + """Models a server addresses response as a python dictionary.""" def build(self, inst): raise NotImplementedError() class ViewBuilderV10(ViewBuilder): + def build(self, inst): private_ips = self.build_private_parts(inst) public_ips = self.build_public_parts(inst) @@ -40,11 +41,30 @@ class ViewBuilderV10(ViewBuilder): class ViewBuilderV11(ViewBuilder): - def build(self, inst): - # TODO(tr3buchet) - this shouldn't be hard coded to 4... - private_ips = utils.get_from_path(inst, 'fixed_ips/address') - private_ips = [dict(version=4, addr=a) for a in private_ips] - public_ips = utils.get_from_path(inst, - 'fixed_ips/floating_ips/address') - public_ips = [dict(version=4, addr=a) for a in public_ips] - return dict(public=public_ips, private=private_ips) + + def build(self, interfaces): + networks = {} + for interface in interfaces: + network_label = interface['network']['label'] + if network_label not in networks: + networks[network_label] = [] + + for fixed_ip in interface['fixed_ips']: + ip = {'addr': fixed_ip['address'], 'version': 4} + networks[network_label].append(ip) + return networks + + def build_network(self, interfaces, network_label): + for interface in interfaces: + if interface['network']['label'] == network_label: + ips = self._extract_fixed_ips(interface) + return {network_label: ips} + return None + + def _extract_fixed_ips(self, interface): + fixed_ips = [] + for fixed_ip in interface['fixed_ips']: + ip = {'addr': fixed_ip['address'], 'version': 4} + fixed_ips.append(ip) + return fixed_ips + diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index cbfa5aae7..691cc48ca 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -77,7 +77,6 @@ class ViewBuilder(object): inst_dict = { 'id': inst['id'], 'name': inst['display_name'], - 'addresses': self.addresses_builder.build(inst), 'status': power_mapping[inst.get('state')]} ctxt = nova.context.get_admin_context() @@ -98,10 +97,15 @@ class ViewBuilder(object): self._build_image(inst_dict, inst) self._build_flavor(inst_dict, inst) + self._build_addresses(inst_dict, inst) inst_dict['uuid'] = inst['uuid'] return dict(server=inst_dict) + def _build_addresses(self, response, inst): + """Return the addresses sub-resource of a server.""" + raise NotImplementedError() + def _build_image(self, response, inst): """Return the image sub-resource of a server.""" raise NotImplementedError() @@ -128,6 +132,9 @@ class ViewBuilderV10(ViewBuilder): if 'instance_type' in dict(inst): response['flavorId'] = inst['instance_type']['flavorid'] + def _build_addresses(self, response, inst): + response['addresses'] = self.addresses_builder.build(inst) + class ViewBuilderV11(ViewBuilder): """Model an Openstack API V1.0 server response.""" @@ -151,6 +158,10 @@ class ViewBuilderV11(ViewBuilder): flavor_ref = self.flavor_builder.generate_href(flavor_id) response["flavorRef"] = flavor_ref + def _build_addresses(self, response, inst): + interfaces = inst['virtual_interfaces'] + response['addresses'] = self.addresses_builder.build(interfaces) + def _build_extra(self, response, inst): self._build_links(response, inst) -- cgit From 880121c8498530d9c0e9a38e983c4d4518c1189e Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 13 Jul 2011 11:10:40 -0400 Subject: adding test; casting instance to dict to prevent sqlalchemy errors --- nova/api/openstack/__init__.py | 13 ++++--------- nova/api/openstack/ips.py | 2 +- nova/api/openstack/servers.py | 5 +++-- 3 files changed, 8 insertions(+), 12 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 3e4d87ee0..e87d7c754 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -125,6 +125,10 @@ class APIRouter(base_wsgi.Router): collection={'detail': 'GET'}, member=self.server_members) + mapper.resource("ip", "ips", controller=ips.create_resource(version), + parent_resource=dict(member_name='server', + collection_name='servers')) + mapper.resource("image", "images", controller=images.create_resource(version), collection={'detail': 'GET'}) @@ -154,11 +158,6 @@ class APIRouterV10(APIRouter): parent_resource=dict(member_name='server', collection_name='servers')) - mapper.resource("ip", "ips", controller=ips.create_resource('1.0'), - member=dict(public='GET', private='GET'), - parent_resource=dict(member_name='server', - collection_name='servers')) - class APIRouterV11(APIRouter): """Define routes specific to OpenStack API V1.1.""" @@ -174,7 +173,3 @@ class APIRouterV11(APIRouter): controller=server_metadata.create_resource(), parent_resource=dict(member_name='server', collection_name='servers')) - - mapper.resource("ip", "ips", controller=ips.create_resource('1.1'), - parent_resource=dict(member_name='server', - collection_name='servers')) diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py index b83e2f9b3..59e3c084c 100644 --- a/nova/api/openstack/ips.py +++ b/nova/api/openstack/ips.py @@ -93,7 +93,7 @@ class ControllerV11(Controller): def _get_virtual_interfaces(self, context, server_id): try: return db.api.virtual_interface_get_by_instance(context, server_id) - except exception.InstanceNotFound: + except nova.exception.InstanceNotFound: msg = _("Instance does not exist") raise exc.HTTPNotFound(explanation=msg) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3c29c2606..2cb27472a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -492,9 +492,10 @@ class ControllerV11(Controller): context = req.environ['nova.context'] interfaces = db.api.virtual_interface_get_by_instance(context, instance['id']) - instance['virtual_interfaces'] = interfaces + _instance = dict(instance) + _instance['virtual_interfaces'] = interfaces - return builder.build(instance, is_detail=is_detail) + return builder.build(_instance, is_detail=is_detail) def _action_change_password(self, input_dict, req, id): context = req.environ['nova.context'] -- cgit From 132a47611b2fdbbb1e6c70a33bfd092854ea6e98 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 13 Jul 2011 14:02:44 -0400 Subject: updating testing; simplifying instance-level code --- nova/api/openstack/servers.py | 8 +------- nova/api/openstack/views/addresses.py | 1 - nova/api/openstack/views/servers.py | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 2cb27472a..5230f4d5c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -489,13 +489,7 @@ class ControllerV11(Controller): builder = nova.api.openstack.views.servers.ViewBuilderV11( addresses_builder, flavor_builder, image_builder, base_url) - context = req.environ['nova.context'] - interfaces = db.api.virtual_interface_get_by_instance(context, - instance['id']) - _instance = dict(instance) - _instance['virtual_interfaces'] = interfaces - - return builder.build(_instance, is_detail=is_detail) + return builder.build(instance, is_detail=is_detail) def _action_change_password(self, input_dict, req, id): context = req.environ['nova.context'] diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index c7464a7c5..b127ac38c 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -67,4 +67,3 @@ class ViewBuilderV11(ViewBuilder): ip = {'addr': fixed_ip['address'], 'version': 4} fixed_ips.append(ip) return fixed_ips - diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 691cc48ca..a957aa58e 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -159,7 +159,7 @@ class ViewBuilderV11(ViewBuilder): response["flavorRef"] = flavor_ref def _build_addresses(self, response, inst): - interfaces = inst['virtual_interfaces'] + interfaces = inst.get('virtual_interfaces', []) response['addresses'] = self.addresses_builder.build(interfaces) def _build_extra(self, response, inst): -- cgit From 27b8d75f9b666ce08472270b38685d8e36a612d8 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 14 Jul 2011 13:25:05 -0400 Subject: fixing bad merge --- nova/api/openstack/ips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py index f08d8023e..1ebfdb831 100644 --- a/nova/api/openstack/ips.py +++ b/nova/api/openstack/ips.py @@ -125,4 +125,4 @@ def create_resource(version): } serializer = wsgi.ResponseSerializer(body_serializers) - return wsgi.Resource(Controller(), serializer=serializer) + return wsgi.Resource(controller, serializer=serializer) -- cgit From 38233d72aff36fbdb0fd49755458b7b5100366e1 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 14 Jul 2011 14:23:23 -0400 Subject: exposing floating ips --- nova/api/openstack/views/addresses.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index b127ac38c..a242efa45 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -46,24 +46,26 @@ class ViewBuilderV11(ViewBuilder): networks = {} for interface in interfaces: network_label = interface['network']['label'] + if network_label not in networks: networks[network_label] = [] - for fixed_ip in interface['fixed_ips']: - ip = {'addr': fixed_ip['address'], 'version': 4} - networks[network_label].append(ip) + networks[network_label].extend(self._extract_ipv4(interface)) + return networks def build_network(self, interfaces, network_label): for interface in interfaces: if interface['network']['label'] == network_label: - ips = self._extract_fixed_ips(interface) - return {network_label: ips} + ips = self._extract_ipv4(interface) + return {network_label: list(ips)} return None - def _extract_fixed_ips(self, interface): - fixed_ips = [] + def _extract_ipv4(self, interface): for fixed_ip in interface['fixed_ips']: - ip = {'addr': fixed_ip['address'], 'version': 4} - fixed_ips.append(ip) - return fixed_ips + yield self._build_ip_entity(fixed_ip['address'], 4) + for floating_ip in fixed_ip.get('floating_ips', []): + yield self._build_ip_entity(floating_ip['address'], 4) + + def _build_ip_entity(self, address, version): + return {'addr': address, 'version': version} -- cgit