From: Salvatore Orlando Date: Mon, 8 Aug 2011 12:02:09 +0000 (+0100) Subject: Fixing detail action for port collection X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=100f47e9e69361d2859867be6db04be39416d448;p=openstack-build%2Fneutron-build.git Fixing detail action for port collection Adding PortIsDown exception Adding unit tests for detail actions and PortIsDown PEP8 FIXES --- diff --git a/quantum/api/attachments.py b/quantum/api/attachments.py index 14f4d9405..acb5bcab3 100644 --- a/quantum/api/attachments.py +++ b/quantum/api/attachments.py @@ -72,6 +72,8 @@ class Controller(common.QuantumController): return faults.Fault(faults.PortNotFound(e)) except exception.PortInUse as e: return faults.Fault(faults.PortInUse(e)) + except exception.PortIsDown as e: + return faults.Fault(faults.PortIsDown(e)) except exception.AlreadyAttached as e: return faults.Fault(faults.AlreadyAttached(e)) diff --git a/quantum/api/faults.py b/quantum/api/faults.py index ef4d50bc6..57c06c035 100644 --- a/quantum/api/faults.py +++ b/quantum/api/faults.py @@ -34,6 +34,7 @@ class Fault(webob.exc.HTTPException): 430: "portNotFound", 431: "requestedStateInvalid", 432: "portInUse", + 433: "portIsDown", 440: "alreadyAttached", 470: "serviceUnavailable", 471: "pluginFault"} @@ -133,6 +134,19 @@ class PortInUse(webob.exc.HTTPClientError): explanation = ('A resource is currently attached to the logical port') +class PortIsDown(webob.exc.HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This indicates that the server could not plug an attachment into a port + as the port is administratively down + code: 433, title: PortIsDown + """ + code = 433 + title = 'Port is Down' + explanation = ('The specified logical port is administratively down') + + class AlreadyAttached(webob.exc.HTTPClientError): """ subclass of :class:`~HTTPClientError` diff --git a/quantum/api/networks.py b/quantum/api/networks.py index 9ad99b0d1..58d105155 100644 --- a/quantum/api/networks.py +++ b/quantum/api/networks.py @@ -39,7 +39,8 @@ class Controller(common.QuantumController): "port": ["id", "state"], "attachment": ["id"] }, - "plurals": {"networks": "network"} + "plurals": {"networks": "network", + "ports": "port"} }, } diff --git a/quantum/api/ports.py b/quantum/api/ports.py index 3b95e2d93..0568e1575 100644 --- a/quantum/api/ports.py +++ b/quantum/api/ports.py @@ -51,14 +51,23 @@ class Controller(common.QuantumController): port_details=False): """ Returns a list of ports. """ try: - ports = self._plugin.get_all_ports(tenant_id, network_id) + port_list = self._plugin.get_all_ports(tenant_id, network_id) builder = ports_view.get_view_builder(request) + + # Load extra data for ports if required. + if port_details: + port_list_detail = \ + [self._plugin.get_port_details( + tenant_id, network_id, port['port-id']) + for port in port_list] + port_list = port_list_detail + result = [builder.build(port, port_details)['port'] - for port in ports] + for port in port_list] return dict(ports=result) except exception.NetworkNotFound as e: return faults.Fault(faults.NetworkNotFound(e)) - + def _item(self, request, tenant_id, network_id, port_id, att_details=False): """ Returns a specific port. """ @@ -88,11 +97,11 @@ class Controller(common.QuantumController): port_id = kwargs.get('id') if port_id: # show details for a given network - return self._item(request, tenant_id, + return self._item(request, tenant_id, network_id, port_id, att_details=True) else: # show details for all port - return self._items(request, tenant_id, + return self._items(request, tenant_id, network_id, port_details=True) def create(self, request, tenant_id, network_id): diff --git a/quantum/api/views/ports.py b/quantum/api/views/ports.py index 747ce1442..f7b75e41a 100644 --- a/quantum/api/views/ports.py +++ b/quantum/api/views/ports.py @@ -32,8 +32,8 @@ class ViewBuilder(object): def build(self, port_data, port_details=False, att_details=False): """Generic method used to generate a port entity.""" port = dict(port=dict(id=port_data['port-id'])) - if port_details: + if port_details: port['port']['state'] = port_data['port-state'] if att_details and port_data['attachment-id']: - port['attachment'] = dict(id=port_data['attachment-id']) + port['port']['attachment'] = dict(id=port_data['attachment-id']) return port diff --git a/quantum/common/exceptions.py b/quantum/common/exceptions.py index 4daf31762..796de201b 100644 --- a/quantum/common/exceptions.py +++ b/quantum/common/exceptions.py @@ -102,6 +102,12 @@ class PortInUse(QuantumException): "is plugged into the logical port.") +class PortIsDown(QuantumException): + message = _("Unable to perform operation on port %(port_id)s " \ + "for network %(net_id)s. The port is currently " \ + "administratively down.") + + class AlreadyAttached(QuantumException): message = _("Unable to plug the attachment %(att_id)s into port " \ "%(port_id)s for network %(net_id)s. The attachment is " \ diff --git a/quantum/common/wsgi.py b/quantum/common/wsgi.py index c6d66fa71..7f831aa17 100644 --- a/quantum/common/wsgi.py +++ b/quantum/common/wsgi.py @@ -129,13 +129,13 @@ class Request(webob.Request): format = parts[1] if format in ['json', 'xml']: return 'application/{0}'.format(parts[1]) - + #Then look up content header type_from_header = self.get_content_type() if type_from_header: return type_from_header ctypes = ['application/json', 'application/xml'] - + #Finally search in Accept-* headers bm = self.accept.best_match(ctypes) return bm or 'application/json' diff --git a/quantum/plugins/SamplePlugin.py b/quantum/plugins/SamplePlugin.py index 34573d2fe..62feffebc 100644 --- a/quantum/plugins/SamplePlugin.py +++ b/quantum/plugins/SamplePlugin.py @@ -405,10 +405,13 @@ class FakePlugin(object): specified Virtual Network. """ LOG.debug("FakePlugin.plug_interface() called") + port = self._get_port(tenant_id, net_id, port_id) + # Verify port state + if port['state'] == 'DOWN': + raise exc.PortIsDown(net_id=net_id, port_id=port_id) # Validate attachment self._validate_attachment(tenant_id, net_id, port_id, remote_interface_id) - port = self._get_port(tenant_id, net_id, port_id) if port['interface_id']: raise exc.PortInUse(net_id=net_id, port_id=port_id, att_id=port['interface_id']) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index d7095515b..dc778b0e7 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -101,6 +101,25 @@ class APITest(unittest.TestCase): self.assertEqual(len(network_data['networks']), 2) LOG.debug("_test_list_networks - format:%s - END", format) + def _test_list_networks_detail(self, format): + LOG.debug("_test_list_networks_detail - format:%s - START", format) + content_type = "application/%s" % format + self._create_network(format, "net_1") + self._create_network(format, "net_2") + list_network_req = testlib.network_list_detail_request(self.tenant_id, + format) + list_network_res = list_network_req.get_response(self.api) + self.assertEqual(list_network_res.status_int, 200) + network_data = self._net_serializer.deserialize( + list_network_res.body, content_type) + # Check network count: should return 2 + self.assertEqual(len(network_data['networks']), 2) + # Check contents - id & name for each network + for network in network_data['networks']: + self.assertTrue('id' in network and 'name' in network) + self.assertTrue(network['id'] and network['name']) + LOG.debug("_test_list_networks_detail - format:%s - END", format) + def _test_show_network(self, format): LOG.debug("_test_show_network - format:%s - START", format) content_type = "application/%s" % format @@ -117,6 +136,29 @@ class APITest(unittest.TestCase): network_data['network']) LOG.debug("_test_show_network - format:%s - END", format) + def _test_show_network_detail(self, format): + LOG.debug("_test_show_network_detail - format:%s - START", format) + content_type = "application/%s" % format + # Create a network and a port + network_id = self._create_network(format) + port_id = self._create_port(network_id, "ACTIVE", format) + show_network_req = testlib.show_network_detail_request( + self.tenant_id, network_id, format) + show_network_res = show_network_req.get_response(self.api) + self.assertEqual(show_network_res.status_int, 200) + network_data = self._net_serializer.deserialize( + show_network_res.body, content_type) + self.assertEqual({'id': network_id, + 'name': self.network_name, + 'ports': [ + { + 'id': port_id, + 'state': 'ACTIVE' + } + ]}, + network_data['network']) + LOG.debug("_test_show_network_detail - format:%s - END", format) + def _test_show_network_not_found(self, format): LOG.debug("_test_show_network_not_found - format:%s - START", format) show_network_req = testlib.show_network_request(self.tenant_id, @@ -240,6 +282,27 @@ class APITest(unittest.TestCase): self.assertEqual(len(port_data['ports']), 2) LOG.debug("_test_list_ports - format:%s - END", format) + def _test_list_ports_detail(self, format): + LOG.debug("_test_list_ports_detail - format:%s - START", format) + content_type = "application/%s" % format + port_state = "ACTIVE" + network_id = self._create_network(format) + self._create_port(network_id, port_state, format) + self._create_port(network_id, port_state, format) + list_port_req = testlib.port_list_detail_request(self.tenant_id, + network_id, format) + list_port_res = list_port_req.get_response(self.api) + self.assertEqual(list_port_res.status_int, 200) + port_data = self._port_serializer.deserialize( + list_port_res.body, content_type) + # Check port count: should return 2 + self.assertEqual(len(port_data['ports']), 2) + # Check contents - id & name for each network + for port in port_data['ports']: + self.assertTrue('id' in port and 'state' in port) + self.assertTrue(port['id'] and port['state']) + LOG.debug("_test_list_ports_detail - format:%s - END", format) + def _test_show_port(self, format): LOG.debug("_test_show_port - format:%s - START", format) content_type = "application/%s" % format @@ -257,6 +320,44 @@ class APITest(unittest.TestCase): port_data['port']) LOG.debug("_test_show_port - format:%s - END", format) + def _test_show_port_detail(self, format): + LOG.debug("_test_show_port - format:%s - START", format) + content_type = "application/%s" % format + port_state = "ACTIVE" + network_id = self._create_network(format) + port_id = self._create_port(network_id, port_state, format) + + # Part 1 - no attachment + show_port_req = testlib.show_port_detail_request(self.tenant_id, + network_id, port_id, format) + show_port_res = show_port_req.get_response(self.api) + self.assertEqual(show_port_res.status_int, 200) + port_data = self._port_serializer.deserialize( + show_port_res.body, content_type) + self.assertEqual({'id': port_id, 'state': port_state}, + port_data['port']) + + # Part 2 - plug attachment into port + interface_id = "test_interface" + put_attachment_req = testlib.put_attachment_request(self.tenant_id, + network_id, + port_id, + interface_id, + format) + put_attachment_res = put_attachment_req.get_response(self.api) + self.assertEqual(put_attachment_res.status_int, 204) + show_port_req = testlib.show_port_detail_request(self.tenant_id, + network_id, port_id, format) + show_port_res = show_port_req.get_response(self.api) + self.assertEqual(show_port_res.status_int, 200) + port_data = self._port_serializer.deserialize( + show_port_res.body, content_type) + self.assertEqual({'id': port_id, 'state': port_state, + 'attachment': {'id': interface_id}}, + port_data['port']) + + LOG.debug("_test_show_port_detail - format:%s - END", format) + def _test_show_port_networknotfound(self, format): LOG.debug("_test_show_port_networknotfound - format:%s - START", format) @@ -580,6 +681,22 @@ class APITest(unittest.TestCase): LOG.debug("_test_put_attachment_portnotfound - format:%s - END", format) + def _test_put_attachment_portisdown(self, format): + LOG.debug("_test_put_attachment_portisdown - format:%s - START", + format) + port_state = "DOWN" + network_id = self._create_network(format) + interface_id = "test_interface" + port_id = self._create_port(network_id, port_state, format) + put_attachment_req = testlib.put_attachment_request(self.tenant_id, + network_id, + port_id, + interface_id, + format) + put_attachment_res = put_attachment_req.get_response(self.api) + self.assertEqual(put_attachment_res.status_int, 433) + LOG.debug("_test_put_attachment_portisdown - format:%s - END", format) + def _test_delete_attachment(self, format): LOG.debug("_test_delete_attachment - format:%s - START", format) port_state = "ACTIVE" @@ -669,6 +786,12 @@ class APITest(unittest.TestCase): def test_list_networks_xml(self): self._test_list_networks('xml') + def test_list_networks_detail_json(self): + self._test_list_networks_detail('json') + + def test_list_networks_detail_xml(self): + self._test_list_networks_detail('xml') + def test_create_network_json(self): self._test_create_network('json') @@ -693,6 +816,12 @@ class APITest(unittest.TestCase): def test_show_network_xml(self): self._test_show_network('xml') + def test_show_network_detail_json(self): + self._test_show_network_detail('json') + + def test_show_network_detail_xml(self): + self._test_show_network_detail('xml') + def test_delete_network_json(self): self._test_delete_network('json') @@ -729,12 +858,24 @@ class APITest(unittest.TestCase): def test_list_ports_xml(self): self._test_list_ports('xml') + def test_list_ports_detail_json(self): + self._test_list_ports_detail('json') + + def test_list_ports_detail_xml(self): + self._test_list_ports_detail('xml') + def test_show_port_json(self): self._test_show_port('json') def test_show_port_xml(self): self._test_show_port('xml') + def test_show_port_detail_json(self): + self._test_show_port_detail('json') + + def test_show_port_detail_xml(self): + self._test_show_port_detail('xml') + def test_show_port_networknotfound_json(self): self._test_show_port_networknotfound('json') @@ -855,6 +996,12 @@ class APITest(unittest.TestCase): def test_put_attachment_portnotfound_json(self): self._test_put_attachment_portnotfound('json') + def test_put_attachment_portisdown_xml(self): + self._test_put_attachment_portisdown('xml') + + def test_put_attachment_portisdown_json(self): + self._test_put_attachment_portisdown('json') + def test_delete_attachment_xml(self): self._test_delete_attachment('xml') diff --git a/tests/unit/testlib_api.py b/tests/unit/testlib_api.py index 074267e13..cd3cce006 100644 --- a/tests/unit/testlib_api.py +++ b/tests/unit/testlib_api.py @@ -12,21 +12,40 @@ def create_request(path, body, content_type, method='GET'): return req -def network_list_request(tenant_id, format='xml'): +def _network_list_request(tenant_id, format='xml', detail=False): method = 'GET' - path = "/tenants/%(tenant_id)s/networks.%(format)s" % locals() + detail_str = detail and '/detail' or '' + path = "/tenants/%(tenant_id)s/networks" \ + "%(detail_str)s.%(format)s" % locals() content_type = "application/%s" % format return create_request(path, None, content_type, method) -def show_network_request(tenant_id, network_id, format='xml'): +def network_list_request(tenant_id, format='xml'): + return _network_list_request(tenant_id, format) + + +def network_list_detail_request(tenant_id, format='xml'): + return _network_list_request(tenant_id, format, detail=True) + + +def _show_network_request(tenant_id, network_id, format='xml', detail=False): method = 'GET' + detail_str = detail and '/detail' or '' path = "/tenants/%(tenant_id)s/networks" \ - "/%(network_id)s.%(format)s" % locals() + "/%(network_id)s%(detail_str)s.%(format)s" % locals() content_type = "application/%s" % format return create_request(path, None, content_type, method) +def show_network_request(tenant_id, network_id, format='xml'): + return _show_network_request(tenant_id, network_id, format) + + +def show_network_detail_request(tenant_id, network_id, format='xml'): + return _show_network_request(tenant_id, network_id, format, detail=True) + + def new_network_request(tenant_id, network_name='new_name', format='xml', custom_req_body=None): method = 'POST' @@ -56,22 +75,43 @@ def network_delete_request(tenant_id, network_id, format='xml'): return create_request(path, None, content_type, method) -def port_list_request(tenant_id, network_id, format='xml'): +def _port_list_request(tenant_id, network_id, format='xml', detail=False): method = 'GET' + detail_str = detail and '/detail' or '' path = "/tenants/%(tenant_id)s/networks/" \ - "%(network_id)s/ports.%(format)s" % locals() + "%(network_id)s/ports%(detail_str)s.%(format)s" % locals() content_type = "application/%s" % format return create_request(path, None, content_type, method) -def show_port_request(tenant_id, network_id, port_id, format='xml'): +def port_list_request(tenant_id, network_id, format='xml'): + return _port_list_request(tenant_id, network_id, format) + + +def port_list_detail_request(tenant_id, network_id, format='xml'): + return _port_list_request(tenant_id, network_id, + format, detail=True) + + +def _show_port_request(tenant_id, network_id, port_id, + format='xml', detail=False): method = 'GET' + detail_str = detail and '/detail' or '' path = "/tenants/%(tenant_id)s/networks/%(network_id)s" \ - "/ports/%(port_id)s.%(format)s" % locals() + "/ports/%(port_id)s%(detail_str)s.%(format)s" % locals() content_type = "application/%s" % format return create_request(path, None, content_type, method) +def show_port_request(tenant_id, network_id, port_id, format='xml'): + return _show_port_request(tenant_id, network_id, port_id, format) + + +def show_port_detail_request(tenant_id, network_id, port_id, format='xml'): + return _show_port_request(tenant_id, network_id, port_id, + format, detail=True) + + def new_port_request(tenant_id, network_id, port_state, format='xml', custom_req_body=None): method = 'POST'