]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Fixing detail action for port collection
authorSalvatore Orlando <salvatore.orlando@eu.citrix.com>
Mon, 8 Aug 2011 12:02:09 +0000 (13:02 +0100)
committerSalvatore Orlando <salvatore.orlando@eu.citrix.com>
Mon, 8 Aug 2011 12:02:09 +0000 (13:02 +0100)
Adding PortIsDown exception
Adding unit tests for detail actions and PortIsDown
PEP8 FIXES

quantum/api/attachments.py
quantum/api/faults.py
quantum/api/networks.py
quantum/api/ports.py
quantum/api/views/ports.py
quantum/common/exceptions.py
quantum/common/wsgi.py
quantum/plugins/SamplePlugin.py
tests/unit/test_api.py
tests/unit/testlib_api.py

index 14f4d940586d7e9c7ae3fd8052e39ca43e645955..acb5bcab30bd5c246f58976291df3d4a163544bd 100644 (file)
@@ -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))
 
index ef4d50bc6b27c59279257afb22927ebdb64bbe17..57c06c0359a2c12ae4c03405f3ad47b9c3e98318 100644 (file)
@@ -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`
index 9ad99b0d1b6ac85eaf71c122c64c93f2ec7d2028..58d105155df88cf5455b388e881cbed01ee989bc 100644 (file)
@@ -39,7 +39,8 @@ class Controller(common.QuantumController):
                 "port": ["id", "state"],
                 "attachment": ["id"]
             },
-            "plurals": {"networks": "network"}
+            "plurals": {"networks": "network",
+                        "ports": "port"}
         },
     }
 
index 3b95e2d93cc2902da2d4f334343124ab455a646a..0568e1575ee5db24559945072d876309e23c696b 100644 (file)
@@ -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):
index 747ce1442e4ea1fcfd0919dcbe98a52eef9695ce..f7b75e41aea44dd818f567f55893acfe14de7e06 100644 (file)
@@ -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
index 4daf31762b9138d697dc02086e58422f9c1db8f6..796de201ba1e76a4f1a631c2727d41a64099cb2a 100644 (file)
@@ -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 " \
index c6d66fa71da71518fe32822686a32690b9c78c49..7f831aa1721d9edece40c8b044d8662fc21ee211 100644 (file)
@@ -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'
index 34573d2fe5e3ff283e8dbddeec6dd7456ee7a59b..62feffebc2e30fd73dc8367daf0802c3db0a7a25 100644 (file)
@@ -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'])
index d7095515b904e2b86bd5ba5c022fe6e99e7afbdb..dc778b0e7002e532323b1bef8479fd778e318082 100644 (file)
@@ -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')
 
index 074267e138c9bebf1905dbf9c0029b4cda49a766..cd3cce0063b520eb5dd9844f64f2ba029ba5bb1c 100644 (file)
@@ -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'