]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Improve Nicira plugin test coverage
authorSalvatore Orlando <salv.orlando@gmail.com>
Sat, 18 May 2013 18:19:50 +0000 (20:19 +0200)
committerSalvatore Orlando <salv.orlando@gmail.com>
Sat, 29 Jun 2013 09:01:03 +0000 (11:01 +0200)
Blueprint nvp-test-coverage

This patch improves the Nicira plugin test coverage by:
- adding a suite of test cases for nvplib
- removing unused exception handling code
- removing 'vestigial' code in nvp plugin modules

This patches also fixes a few minor glitches with the plugin
and the fake api client.

Change-Id: Ia319f556a815e5061b867a97d834123786218fec

14 files changed:
quantum/plugins/nicira/QuantumPlugin.py
quantum/plugins/nicira/nicira_db.py
quantum/plugins/nicira/nvp_plugin_version.py [deleted file]
quantum/plugins/nicira/nvplib.py
quantum/tests/unit/nicira/etc/fake_get_lqueue.json [new file with mode: 0644]
quantum/tests/unit/nicira/etc/fake_get_lrouter_lport.json
quantum/tests/unit/nicira/etc/fake_get_lswitch_lport.json
quantum/tests/unit/nicira/etc/fake_get_lswitch_lport_status.json
quantum/tests/unit/nicira/etc/fake_get_security_profile.json [new file with mode: 0644]
quantum/tests/unit/nicira/etc/fake_put_lrouter_lport_att.json
quantum/tests/unit/nicira/fake_nvpapiclient.py
quantum/tests/unit/nicira/test_networkgw.py
quantum/tests/unit/nicira/test_nicira_plugin.py
quantum/tests/unit/nicira/test_nvplib.py

index 5fbea4b4b2e67d7068b7d489ba4d0745ce6081e7..afb43b2fa9cf4322ceee49353188c23c70d5c570 100644 (file)
@@ -66,7 +66,6 @@ from quantum.plugins.nicira import nicira_db
 from quantum.plugins.nicira import nicira_networkgw_db as networkgw_db
 from quantum.plugins.nicira import nicira_qos_db as qos_db
 from quantum.plugins.nicira import nvp_cluster
-from quantum.plugins.nicira.nvp_plugin_version import PLUGIN_VERSION
 from quantum.plugins.nicira import NvpApiClient
 from quantum.plugins.nicira import nvplib
 
@@ -154,13 +153,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     # Map nova zones to cluster for easy retrieval
     novazone_cluster_map = {}
 
-    port_security_enabled_update = "update_port:port_security_enabled"
-
-    def __init__(self, loglevel=None):
-        if loglevel:
-            logging.basicConfig(level=loglevel)
-            nvplib.LOG.setLevel(loglevel)
-            NvpApiClient.LOG.setLevel(loglevel)
+    def __init__(self):
 
         # Routines for managing logical ports in NVP
         self._port_drivers = {
@@ -181,8 +174,6 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                        self._nvp_delete_router_port,
                        l3_db.DEVICE_OWNER_FLOATINGIP:
                        self._nvp_delete_fip_port,
-                       l3_db.DEVICE_OWNER_ROUTER_INTF:
-                       self._nvp_delete_port,
                        networkgw_db.DEVICE_OWNER_NET_GW_INTF:
                        self._nvp_delete_port,
                        'default': self._nvp_delete_port}
@@ -278,16 +269,17 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                           "port id %(port_id)s on router %(router_id)s") %
                 {'port_id': port_data.get('id'), 'router_id': router_id})
         self._update_router_port_attachment(cluster, context, router_id,
-                                            port_data, attachment_type,
-                                            attachment, attachment_vlan,
-                                            lrouter_port['uuid'])
+                                            port_data, lrouter_port['uuid'],
+                                            attachment_type, attachment,
+                                            attachment_vlan)
         return lrouter_port
 
     def _update_router_port_attachment(self, cluster, context,
                                        router_id, port_data,
-                                       attachment_type, attachment,
-                                       attachment_vlan=None,
-                                       nvp_router_port_id=None):
+                                       nvp_router_port_id,
+                                       attachment_type,
+                                       attachment,
+                                       attachment_vlan=None):
         if not nvp_router_port_id:
             nvp_router_port_id = self._find_router_gw_port(context, port_data)
         try:
@@ -470,25 +462,18 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 err_msg=(_("It is not allowed to create router interface "
                            "ports on external networks as '%s'") %
                          port_data['network_id']))
-        try:
-            selected_lswitch = self._nvp_find_lswitch_for_port(context,
-                                                               port_data)
-            # Do not apply port security here!
-            lport = self._nvp_create_port_helper(self.cluster,
-                                                 selected_lswitch['uuid'],
-                                                 port_data,
-                                                 False)
-            nicira_db.add_quantum_nvp_port_mapping(
-                context.session, port_data['id'], lport['uuid'])
-            LOG.debug(_("_nvp_create_port completed for port %(name)s on "
-                        "network %(network_id)s. The new port id is %(id)s."),
-                      port_data)
-        except Exception:
-            # failed to create port in NVP delete port from quantum_db
-            LOG.exception(_("An exception occured while plugging "
-                            "the interface"))
-            super(NvpPluginV2, self).delete_port(context, port_data["id"])
-            raise
+        selected_lswitch = self._nvp_find_lswitch_for_port(context,
+                                                           port_data)
+        # Do not apply port security here!
+        lport = self._nvp_create_port_helper(self.cluster,
+                                             selected_lswitch['uuid'],
+                                             port_data,
+                                             False)
+        nicira_db.add_quantum_nvp_port_mapping(
+            context.session, port_data['id'], lport['uuid'])
+        LOG.debug(_("_nvp_create_port completed for port %(name)s on "
+                    "network %(network_id)s. The new port id is %(id)s."),
+                  port_data)
 
     def _find_router_gw_port(self, context, port_data):
         router_id = port_data['device_id']
@@ -533,10 +518,10 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             # Update attachment
             self._update_router_port_attachment(
                 self.cluster, context, router_id, port_data,
+                lr_port['uuid'],
                 "L3GatewayAttachment",
                 ext_network[pnet.PHYSICAL_NETWORK],
-                ext_network[pnet.SEGMENTATION_ID],
-                lr_port['uuid'])
+                ext_network[pnet.SEGMENTATION_ID])
         # Set the SNAT rule for each subnet (only first IP)
         for cidr in self._find_router_subnets_cidrs(context, router_id):
             cidr_prefix = int(cidr.split('/')[1])
@@ -579,9 +564,9 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             # Reset attachment
             self._update_router_port_attachment(
                 self.cluster, context, router_id, port_data,
+                lr_port['uuid'],
                 "L3GatewayAttachment",
-                self.cluster.default_l3_gw_service_uuid,
-                nvp_router_port_id=lr_port['uuid'])
+                self.cluster.default_l3_gw_service_uuid)
 
         except NvpApiClient.ResourceNotFound:
             raise nvp_exc.NvpPluginException(
@@ -609,31 +594,23 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                       port_data['network_id'])
             # No need to actually update the DB state - the default is down
             return port_data
-        try:
-            selected_lswitch = self._nvp_find_lswitch_for_port(context,
-                                                               port_data)
-            lport = self._nvp_create_port_helper(self.cluster,
-                                                 selected_lswitch['uuid'],
-                                                 port_data,
-                                                 True)
-            nicira_db.add_quantum_nvp_port_mapping(
-                context.session, port_data['id'], lport['uuid'])
-            nvplib.plug_l2_gw_service(
-                self.cluster,
-                port_data['network_id'],
-                lport['uuid'],
-                port_data['device_id'],
-                int(port_data.get('gw:segmentation_id') or 0))
-            LOG.debug(_("_nvp_create_port completed for port %(name)s "
-                        "on network %(network_id)s. The new port id "
-                        "is %(id)s."), port_data)
-        except NvpApiClient.NvpApiException:
-            # failed to create port in NVP delete port from quantum_db
-            msg = (_("An exception occured while plugging the gateway "
-                     "interface into network:%s") % port_data['network_id'])
-            LOG.exception(msg)
-            super(NvpPluginV2, self).delete_port(context, port_data["id"])
-            raise q_exc.QuantumException(message=msg)
+        selected_lswitch = self._nvp_find_lswitch_for_port(context,
+                                                           port_data)
+        lport = self._nvp_create_port_helper(self.cluster,
+                                             selected_lswitch['uuid'],
+                                             port_data,
+                                             True)
+        nicira_db.add_quantum_nvp_port_mapping(
+            context.session, port_data['id'], lport['uuid'])
+        nvplib.plug_l2_gw_service(
+            self.cluster,
+            port_data['network_id'],
+            lport['uuid'],
+            port_data['device_id'],
+            int(port_data.get('gw:segmentation_id') or 0))
+        LOG.debug(_("_nvp_create_port completed for port %(name)s "
+                    "on network %(network_id)s. The new port id "
+                    "is %(id)s."), port_data)
 
     def _nvp_create_fip_port(self, context, port_data):
         # As we do not create ports for floating IPs in NVP,
@@ -1402,7 +1379,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     # Find the NVP port corresponding to quantum port_id
                     # Do not query by nvp id as the port might be on
                     # an extended switch and we do not store the extended
-                    # swiwtch uuid
+                    # switch uuid
                     results = nvplib.query_lswitch_lports(
                         self.cluster, '*',
                         relations='LogicalPortStatus',
@@ -1538,11 +1515,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     def get_router(self, context, id, fields=None):
         router = self._get_router(context, id)
         try:
-            try:
-                lrouter = nvplib.get_lrouter(self.cluster, id)
-            except q_exc.NotFound:
-                lrouter = {}
-                router_op_status = constants.NET_STATUS_ERROR
+            lrouter = nvplib.get_lrouter(self.cluster, id)
             relations = lrouter.get('_relations')
             if relations:
                 lrouter_status = relations.get('LogicalRouterStatus')
@@ -1552,18 +1525,17 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     router_op_status = (lrouter_status.get('fabric_status')
                                         and constants.NET_STATUS_ACTIVE or
                                         constants.NET_STATUS_DOWN)
-            if router_op_status != router.status:
-                LOG.debug(_("Current router status:%(router_status)s;"
-                            "Status in Quantum DB:%(db_router_status)s"),
-                          {'router_status': router_op_status,
-                           'db_router_status': router.status})
-                 # update the router status
-                with context.session.begin(subtransactions=True):
-                    router.status = router_op_status
-        except NvpApiClient.NvpApiException:
-            err_msg = _("Unable to get logical router")
-            LOG.exception(err_msg)
-            raise nvp_exc.NvpPluginException(err_msg=err_msg)
+        except q_exc.NotFound:
+            lrouter = {}
+            router_op_status = constants.NET_STATUS_ERROR
+        if router_op_status != router.status:
+            LOG.debug(_("Current router status:%(router_status)s;"
+                        "Status in Quantum DB:%(db_router_status)s"),
+                      {'router_status': router_op_status,
+                       'db_router_status': router.status})
+            # update the router status
+            with context.session.begin(subtransactions=True):
+                router.status = router_op_status
         return self._make_router_dict(router, fields)
 
     def get_routers(self, context, filters=None, fields=None):
@@ -1885,6 +1857,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             elif floatingip_db['fixed_port_id']:
                 # This is a disassociation.
                 # Remove floating IP address from logical router port
+                internal_ip = None
                 nvplib.update_lrouter_port_ips(self.cluster,
                                                router_id,
                                                nvp_gw_port_id,
@@ -1971,18 +1944,9 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 LOG.exception(_("Unable to remove gateway service from "
                                 "NVP plaform - the resource was not found"))
 
-    def _ensure_tenant_on_net_gateway(self, context, net_gateway):
-        if not net_gateway['tenant_id']:
-            net_gateway['tenant_id'] = context.tenant_id
-        return net_gateway
-
     def get_network_gateway(self, context, id, fields=None):
         # Ensure the default gateway in the config file is in sync with the db
         self._ensure_default_network_gateway()
-        # Ensure the tenant_id attribute is populated on the returned gateway
-        #return self._ensure_tenant_on_net_gateway(
-        #    context, super(NvpPluginV2, self).get_network_gateway(
-        #        context, id, fields))
         return super(NvpPluginV2, self).get_network_gateway(context,
                                                             id, fields)
 
@@ -2016,9 +1980,6 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         return super(NvpPluginV2, self).disconnect_network(
             context, network_gateway_id, network_mapping_info)
 
-    def get_plugin_version(self):
-        return PLUGIN_VERSION
-
     def create_security_group(self, context, security_group, default_sg=False):
         """Create security group.
 
index 8befbb31a1053f917a4e68974a92b04be5925b9a..87fdd2be67e51d2dd778e53c5d11b9f9abf3f2ff 100644 (file)
@@ -47,18 +47,6 @@ def get_network_binding_by_vlanid(session, vlan_id):
         return
 
 
-def get_network_binding_by_vlanid_and_phynet(session, vlan_id,
-                                             physical_network):
-    session = session or db.get_session()
-    try:
-        binding = (session.query(nicira_models.NvpNetworkBinding).
-                   filter_by(vlan_id=vlan_id, phy_uuid=physical_network).
-                   one())
-        return binding
-    except exc.NoResultFound:
-        return
-
-
 def add_network_binding(session, network_id, binding_type, phy_uuid, vlan_id):
     with session.begin(subtransactions=True):
         binding = nicira_models.NvpNetworkBinding(network_id, binding_type,
diff --git a/quantum/plugins/nicira/nvp_plugin_version.py b/quantum/plugins/nicira/nvp_plugin_version.py
deleted file mode 100644 (file)
index 8d0753e..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 Nicira Networks, Inc.
-# All Rights Reserved
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-#
-
-# This will get updated at build time.  Version 0 indicates developer build.
-PLUGIN_VERSION = "0"
index 7aa99f29bd46870d7eafe6ab1613a542273cff28..e4ff59a23834e94039b28b2a7228c6e28844647b 100644 (file)
 # @author: Aaron Rosen, Nicira Networks, Inc.
 
 
-from copy import copy
 import hashlib
 import inspect
 import json
-import logging
 
 from oslo.config import cfg
 
@@ -32,11 +30,13 @@ from oslo.config import cfg
 # no quantum-specific logic in it
 from quantum.common import constants
 from quantum.common import exceptions as exception
+from quantum.openstack.common import log
 from quantum.plugins.nicira.common import (
     exceptions as nvp_exc)
 from quantum.plugins.nicira import NvpApiClient
 
 
+LOG = log.getLogger(__name__)
 # HTTP METHODS CONSTANTS
 HTTP_GET = "GET"
 HTTP_POST = "POST"
@@ -67,25 +67,9 @@ SNAT_KEYS = ["to_src_port_min", "to_src_port_max", "to_src_ip_min",
 DNAT_KEYS = ["to_dst_port", "to_dst_ip_min", "to_dst_ip_max"]
 
 
-LOCAL_LOGGING = False
-if LOCAL_LOGGING:
-    from logging.handlers import SysLogHandler
-    FORMAT = ("|%(levelname)s|%(filename)s|%(funcName)s|%(lineno)s"
-              "|%(message)s")
-    LOG = logging.getLogger(__name__)
-    formatter = logging.Formatter(FORMAT)
-    syslog = SysLogHandler(address="/dev/log")
-    syslog.setFormatter(formatter)
-    LOG.addHandler(syslog)
-    LOG.setLevel(logging.DEBUG)
-else:
-    LOG = logging.getLogger(__name__)
-    LOG.setLevel(logging.DEBUG)
-
 # TODO(bgh): it would be more efficient to use a bitmap
 taken_context_ids = []
 
-_net_type_cache = {}  # cache of {net_id: network_type}
 # XXX Only cache default for now
 _lqueue_cache = {}
 
@@ -156,22 +140,14 @@ def get_cluster_version(cluster):
     """Return major/minor version #."""
     # Get control-cluster nodes
     uri = "/ws.v1/control-cluster/node?_page_length=1&fields=uuid"
-    try:
-        res = do_single_request(HTTP_GET, uri, cluster=cluster)
-        res = json.loads(res)
-    except NvpApiClient.NvpApiException:
-        raise exception.QuantumException()
+    res = do_request(HTTP_GET, uri, cluster=cluster)
     if res["result_count"] == 0:
         return None
     node_uuid = res["results"][0]["uuid"]
     # Get control-cluster node status.  It's unsupported to have controllers
     # running different version so we just need the first node version.
     uri = "/ws.v1/control-cluster/node/%s/status" % node_uuid
-    try:
-        res = do_single_request(HTTP_GET, uri, cluster=cluster)
-        res = json.loads(res)
-    except NvpApiClient.NvpApiException:
-        raise exception.QuantumException()
+    res = do_request(HTTP_GET, uri, cluster=cluster)
     version_parts = res["version"].split(".")
     version = "%s.%s" % tuple(version_parts[:2])
     LOG.info(_("NVP controller cluster version: %s"), version)
@@ -186,10 +162,9 @@ def get_all_query_pages(path, c):
     while need_more_results:
         page_cursor_str = (
             "_page_cursor=%s" % page_cursor if page_cursor else "")
-        res = do_single_request(HTTP_GET, "%s%s%s" %
-                                (path, query_marker, page_cursor_str),
-                                cluster=c)
-        body = json.loads(res)
+        body = do_request(HTTP_GET,
+                          "%s%s%s" % (path, query_marker, page_cursor_str),
+                          cluster=c)
         page_cursor = body.get('page_cursor')
         if not page_cursor:
             need_more_results = False
@@ -197,70 +172,15 @@ def get_all_query_pages(path, c):
     return result_list
 
 
-def do_single_request(*args, **kwargs):
-    """Issue a request to a specified cluster if specified via kwargs
-       (cluster=<cluster>).
-    """
-    cluster = kwargs["cluster"]
-    try:
-        req = cluster.api_client.request(*args)
-    except NvpApiClient.ResourceNotFound:
-        raise exception.NotFound()
-    return req
-
-
-def do_multi_request(*args, **kwargs):
-    """Issue a request to all clusters."""
-    results = []
-    clusters = kwargs["clusters"]
-    for x in clusters:
-        LOG.debug(_("Issuing request to cluster: %s"), x.name)
-        rv = x.api_client.request(*args)
-        results.append(rv)
-    return results
-
-
 # -------------------------------------------------------------------
 # Network functions
 # -------------------------------------------------------------------
-def find_port_and_cluster(clusters, port_id):
-    """Find port and cluster.
-
-    Returns (url, cluster_id) of port or (None, None) if port does not exist.
-    """
-    for c in clusters:
-        query = "/ws.v1/lswitch/*/lport?uuid=%s&fields=*" % port_id
-        LOG.debug(_("Looking for lswitch with port id "
-                    "'%(port_id)s' on: %(c)s"),
-                  {'port_id': port_id, 'c': c})
-        try:
-            res = do_single_request(HTTP_GET, query, cluster=c)
-        except Exception as e:
-            LOG.error(_("get_port_cluster_and_url, exception: %s"), str(e))
-            continue
-        res = json.loads(res)
-        if len(res["results"]) == 1:
-            return (res["results"][0], c)
-    return (None, None)
-
-
-def find_lswitch_by_portid(clusters, port_id):
-    port, cluster = find_port_and_cluster(clusters, port_id)
-    if port and cluster:
-        href = port["_href"].split('/')
-        return (href[3], cluster)
-    return (None, None)
-
-
 def get_lswitches(cluster, quantum_net_id):
     lswitch_uri_path = _build_uri_path(LSWITCH_RESOURCE, quantum_net_id,
                                        relations="LogicalSwitchStatus")
     results = []
     try:
-        resp_obj = do_single_request(HTTP_GET,
-                                     lswitch_uri_path,
-                                     cluster=cluster)
-        ls = json.loads(resp_obj)
+        ls = do_request(HTTP_GET, lswitch_uri_path, cluster=cluster)
         results.append(ls)
         for tag in ls['tags']:
             if (tag['scope'] == "multi_lswitch" and
@@ -276,14 +196,8 @@ def get_lswitches(cluster, quantum_net_id):
                                                      cluster)
                 results.extend(extra_switches)
         return results
-    except NvpApiClient.ResourceNotFound:
+    except exception.NotFound:
         raise exception.NetworkNotFound(net_id=quantum_net_id)
-    except NvpApiClient.NvpApiException:
-        # TODO(salvatore-olrando): Do a better exception handling
-        # and re-raising
-        LOG.exception(_("An error occured while fetching logical switches "
-                        "for Quantum network %s"), quantum_net_id)
-        raise exception.QuantumException()
 
 
 def create_lswitch(cluster, tenant_id, display_name,
@@ -317,13 +231,8 @@ def create_lswitch(cluster, tenant_id, display_name,
     if "tags" in kwargs:
         lswitch_obj["tags"].extend(kwargs["tags"])
     uri = _build_uri_path(LSWITCH_RESOURCE)
-    try:
-        lswitch_res = do_single_request(HTTP_POST, uri,
-                                        json.dumps(lswitch_obj),
-                                        cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        raise exception.QuantumException()
-    lswitch = json.loads(lswitch_res)
+    lswitch = do_request(HTTP_POST, uri, json.dumps(lswitch_obj),
+                         cluster=cluster)
     LOG.debug(_("Created logical switch: %s"), lswitch['uuid'])
     return lswitch
 
@@ -337,16 +246,11 @@ def update_lswitch(cluster, lswitch_id, display_name,
     if "tags" in kwargs:
         lswitch_obj["tags"].extend(kwargs["tags"])
     try:
-        resp_obj = do_single_request(HTTP_PUT, uri, json.dumps(lswitch_obj),
-                                     cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
+        return do_request(HTTP_PUT, uri, json.dumps(lswitch_obj),
+                          cluster=cluster)
+    except exception.NotFound as e:
         LOG.error(_("Network not found, Error: %s"), str(e))
         raise exception.NetworkNotFound(net_id=lswitch_id)
-    except NvpApiClient.NvpApiException as e:
-        raise exception.QuantumException()
-
-    obj = json.loads(resp_obj)
-    return obj
 
 
 def create_l2_gw_service(cluster, tenant_id, display_name, devices):
@@ -375,16 +279,9 @@ def create_l2_gw_service(cluster, tenant_id, display_name, devices):
         "gateways": gateways,
         "type": "L2GatewayServiceConfig"
     }
-    try:
-        return json.loads(do_single_request(
-            "POST", _build_uri_path(GWSERVICE_RESOURCE),
-            json.dumps(gwservice_obj), cluster=cluster))
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to create L2 Gateway Service "
-                        "%(name)s for tenant %(id)s.")
-                      % {'name': display_name, 'id': tenant_id})
-        raise
+    return do_request(
+        "POST", _build_uri_path(GWSERVICE_RESOURCE),
+        json.dumps(gwservice_obj), cluster=cluster)
 
 
 def create_lrouter(cluster, tenant_id, display_name, nexthop):
@@ -413,70 +310,35 @@ def create_lrouter(cluster, tenant_id, display_name, nexthop):
         },
         "type": "LogicalRouterConfig"
     }
-    try:
-        return json.loads(do_single_request(HTTP_POST,
-                                            _build_uri_path(LROUTER_RESOURCE),
-                                            json.dumps(lrouter_obj),
-                                            cluster=cluster))
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to create Router "
-                        "%(name)s for tenant %(id)s.")
-                      % {'name': display_name, 'id': tenant_id})
-        raise
+    return do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE),
+                      json.dumps(lrouter_obj), cluster=cluster)
 
 
 def delete_lrouter(cluster, lrouter_id):
-    try:
-        do_single_request(HTTP_DELETE,
-                          _build_uri_path(LROUTER_RESOURCE,
-                                          resource_id=lrouter_id),
-                          cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to delete Router "
-                        "Service %s."), lrouter_id)
-        raise
+    do_request(HTTP_DELETE, _build_uri_path(LROUTER_RESOURCE,
+                                            resource_id=lrouter_id),
+               cluster=cluster)
 
 
 def delete_l2_gw_service(cluster, gateway_id):
-    try:
-        do_single_request("DELETE",
-                          _build_uri_path(GWSERVICE_RESOURCE,
-                                          resource_id=gateway_id),
-                          cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to delete L2 Gateway "
-                        "Service %s."), gateway_id)
-        raise
+    do_request("DELETE", _build_uri_path(GWSERVICE_RESOURCE,
+                                         resource_id=gateway_id),
+               cluster=cluster)
 
 
 def get_lrouter(cluster, lrouter_id):
-    try:
-        return json.loads(do_single_request(HTTP_GET,
-                          _build_uri_path(LROUTER_RESOURCE,
-                                          resource_id=lrouter_id,
-                                          relations='LogicalRouterStatus'),
-                          cluster=cluster))
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to get Router "
-                        "Service %s."), lrouter_id)
-        raise
+    return do_request(HTTP_GET,
+                      _build_uri_path(LROUTER_RESOURCE,
+                                      resource_id=lrouter_id,
+                                      relations='LogicalRouterStatus'),
+                      cluster=cluster)
 
 
 def get_l2_gw_service(cluster, gateway_id):
-    try:
-        return json.loads(do_single_request("GET",
-                          _build_uri_path(GWSERVICE_RESOURCE,
-                                          resource_id=gateway_id),
-                          cluster=cluster))
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to get L2 Gateway "
-                        "Service %s."), gateway_id)
-        raise
+    return do_request(
+        "GET", _build_uri_path(GWSERVICE_RESOURCE,
+                               resource_id=gateway_id),
+        cluster=cluster)
 
 
 def get_lrouters(cluster, tenant_id, fields=None, filters=None):
@@ -514,18 +376,9 @@ def update_l2_gw_service(cluster, gateway_id, display_name):
         # Nothing to update
         return gwservice_obj
     gwservice_obj["display_name"] = _check_and_truncate_name(display_name)
-    try:
-        return json.loads(do_single_request("PUT",
-                          _build_uri_path(GWSERVICE_RESOURCE,
-                                          resource_id=gateway_id),
-                          json.dumps(gwservice_obj),
-                          cluster=cluster))
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to update L2 Gateway Service "
-                        "%(id)s with name %(name)s.") %
-                      {'id': gateway_id, 'name': display_name})
-        raise
+    return do_request("PUT", _build_uri_path(GWSERVICE_RESOURCE,
+                                             resource_id=gateway_id),
+                      json.dumps(gwservice_obj), cluster=cluster)
 
 
 def update_lrouter(cluster, lrouter_id, display_name, nexthop):
@@ -541,71 +394,25 @@ def update_lrouter(cluster, lrouter_id, display_name, nexthop):
             "default_route_next_hop")
         if nh_element:
             nh_element["gateway_ip_address"] = nexthop
-    try:
-        return json.loads(do_single_request(HTTP_PUT,
-                          _build_uri_path(LROUTER_RESOURCE,
-                                          resource_id=lrouter_id),
-                          json.dumps(lrouter_obj),
-                          cluster=cluster))
-    except NvpApiClient.NvpApiException:
-        # just log and re-raise - let the caller handle it
-        LOG.exception(_("Unable to update Router "
-                        "%(id)s with name %(name)s.") %
-                      {'id': lrouter_id, 'name': display_name})
-        raise
-
-
-def get_all_networks(cluster, tenant_id, networks):
-    """Get all networks.
-
-    Append the quantum network uuids we can find in the given cluster
-    to "networks".
-    """
-    uri = "/ws.v1/lswitch?fields=*&tag=%s&tag_scope=os_tid" % tenant_id
-    try:
-        resp_obj = do_single_request(HTTP_GET, uri, cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        raise exception.QuantumException()
-    if not resp_obj:
-        return []
-    networks_result = copy(networks)
-    return networks_result
-
-
-def query_networks(cluster, tenant_id, fields="*", tags=None):
-    uri = "/ws.v1/lswitch?fields=%s" % fields
-    if tags:
-        for t in tags:
-            uri += "&tag=%s&tag_scope=%s" % (t[0], t[1])
-    try:
-        resp_obj = do_single_request(HTTP_GET, uri, cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        raise exception.QuantumException()
-    if not resp_obj:
-        return []
-    lswitches = json.loads(resp_obj)["results"]
-    nets = [{'net-id': lswitch["uuid"], 'net-name': lswitch["display_name"]}
-            for lswitch in lswitches]
-    return nets
+    return do_request(HTTP_PUT, _build_uri_path(LROUTER_RESOURCE,
+                                                resource_id=lrouter_id),
+                      json.dumps(lrouter_obj),
+                      cluster=cluster)
 
 
 def delete_network(cluster, net_id, lswitch_id):
     delete_networks(cluster, net_id, [lswitch_id])
 
 
+#TODO(salvatore-orlando): Simplify and harmonize
 def delete_networks(cluster, net_id, lswitch_ids):
-    if net_id in _net_type_cache:
-        del _net_type_cache[net_id]
     for ls_id in lswitch_ids:
         path = "/ws.v1/lswitch/%s" % ls_id
-
         try:
-            do_single_request(HTTP_DELETE, path, cluster=cluster)
-        except NvpApiClient.ResourceNotFound as e:
+            do_request(HTTP_DELETE, path, cluster=cluster)
+        except exception.NotFound as e:
             LOG.error(_("Network not found, Error: %s"), str(e))
             raise exception.NetworkNotFound(net_id=ls_id)
-        except NvpApiClient.NvpApiException as e:
-            raise exception.QuantumException()
 
 
 def query_lswitch_lports(cluster, ls_uuid, fields="*",
@@ -616,65 +423,28 @@ def query_lswitch_lports(cluster, ls_uuid, fields="*",
         del filters['attachment']
     uri = _build_uri_path(LSWITCHPORT_RESOURCE, parent_resource_id=ls_uuid,
                           fields=fields, filters=filters, relations=relations)
-    try:
-        resp_obj = do_single_request(HTTP_GET, uri, cluster=cluster)
-    except NvpApiClient.ResourceNotFound:
-        LOG.exception(_("Logical switch: %s not found"), ls_uuid)
-        raise
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("An error occurred while querying logical ports on "
-                        "the NVP platform"))
-        raise
-    return json.loads(resp_obj)["results"]
+    return do_request(HTTP_GET, uri, cluster=cluster)['results']
 
 
 def query_lrouter_lports(cluster, lr_uuid, fields="*",
                          filters=None, relations=None):
     uri = _build_uri_path(LROUTERPORT_RESOURCE, parent_resource_id=lr_uuid,
                           fields=fields, filters=filters, relations=relations)
-    try:
-        resp_obj = do_single_request(HTTP_GET, uri, cluster=cluster)
-    except NvpApiClient.ResourceNotFound:
-        LOG.exception(_("Logical router: %s not found"), lr_uuid)
-        raise
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("An error occured while querying logical router "
-                        "ports on the NVP platfom"))
-        raise
-    return json.loads(resp_obj)["results"]
+    return do_request(HTTP_GET, uri, cluster=cluster)['results']
 
 
 def delete_port(cluster, switch, port):
     uri = "/ws.v1/lswitch/" + switch + "/lport/" + port
     try:
-        do_single_request(HTTP_DELETE, uri, cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
-        LOG.error(_("Port or Network not found, Error: %s"), str(e))
-        raise exception.PortNotFound(port_id=port['uuid'])
-    except NvpApiClient.NvpApiException as e:
+        do_request(HTTP_DELETE, uri, cluster=cluster)
+    except exception.NotFound:
+        LOG.exception(_("Port or Network not found"))
+        raise exception.PortNotFound(net_id=switch,
+                                     port_id=port)
+    except NvpApiClient.NvpApiException:
         raise exception.QuantumException()
 
 
-def get_port_by_display_name(clusters, lswitch, display_name):
-    """Return (url, cluster_id) of port or raises PortNotFound."""
-    query = ("/ws.v1/lswitch/%s/lport?display_name=%s&fields=*" %
-             (lswitch, display_name))
-    LOG.debug(_("Looking for port with display_name "
-                "'%(display_name)s' on: %(lswitch)s"),
-              {'display_name': display_name, 'lswitch': lswitch})
-    for c in clusters:
-        try:
-            res_obj = do_single_request(HTTP_GET, query, cluster=c)
-        except Exception as e:
-            continue
-        res = json.loads(res_obj)
-        if len(res["results"]) == 1:
-            return (res["results"][0], c)
-
-    LOG.error(_("Port or Network not found, Error: %s"), str(e))
-    raise exception.PortNotFound(port_id=display_name, net_id=lswitch)
-
-
 def get_port_by_quantum_tag(cluster, lswitch_uuid, quantum_port_id):
     """Get port by quantum tag.
 
@@ -690,12 +460,7 @@ def get_port_by_quantum_tag(cluster, lswitch_uuid, quantum_port_id):
                 "on: '%(lswitch_uuid)s'") %
               {'quantum_port_id': quantum_port_id,
                'lswitch_uuid': lswitch_uuid})
-    try:
-        res_obj = do_single_request(HTTP_GET, uri, cluster=cluster)
-    except Exception:
-        LOG.exception(_("An exception occurred while querying NVP ports"))
-        raise
-    res = json.loads(res_obj)
+    res = do_request(HTTP_GET, uri, cluster=cluster)
     num_results = len(res["results"])
     if num_results >= 1:
         if num_results > 1:
@@ -714,14 +479,10 @@ def get_port(cluster, network, port, relations=None):
     if relations:
         uri += "relations=%s" % relations
     try:
-        resp_obj = do_single_request(HTTP_GET, uri, cluster=cluster)
-        port = json.loads(resp_obj)
-    except NvpApiClient.ResourceNotFound as e:
+        return do_request(HTTP_GET, uri, cluster=cluster)
+    except exception.NotFound as e:
         LOG.error(_("Port or Network not found, Error: %s"), str(e))
         raise exception.PortNotFound(port_id=port, net_id=network)
-    except NvpApiClient.NvpApiException as e:
-        raise exception.QuantumException()
-    return port
 
 
 def _configure_extensions(lport_obj, mac_address, fixed_ips,
@@ -767,17 +528,15 @@ def update_port(cluster, lswitch_uuid, lport_uuid, quantum_port_id, tenant_id,
 
     path = "/ws.v1/lswitch/" + lswitch_uuid + "/lport/" + lport_uuid
     try:
-        resp_obj = do_single_request(HTTP_PUT, path, json.dumps(lport_obj),
-                                     cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
+        result = do_request(HTTP_PUT, path, json.dumps(lport_obj),
+                            cluster=cluster)
+        LOG.debug(_("Updated logical port %(result)s "
+                    "on logical switch %(uuid)s"),
+                  {'result': result['uuid'], 'uuid': lswitch_uuid})
+        return result
+    except exception.NotFound as e:
         LOG.error(_("Port or Network not found, Error: %s"), str(e))
         raise exception.PortNotFound(port_id=lport_uuid, net_id=lswitch_uuid)
-    except NvpApiClient.NvpApiException as e:
-        raise exception.QuantumException()
-    result = json.loads(resp_obj)
-    LOG.debug(_("Updated logical port %(result)s on logical swtich %(uuid)s"),
-              {'result': result['uuid'], 'uuid': lswitch_uuid})
-    return result
 
 
 def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
@@ -804,15 +563,9 @@ def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
 
     path = _build_uri_path(LSWITCHPORT_RESOURCE,
                            parent_resource_id=lswitch_uuid)
-    try:
-        resp_obj = do_single_request(HTTP_POST, path,
-                                     json.dumps(lport_obj),
-                                     cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
-        LOG.error(_("Logical switch not found, Error: %s"), str(e))
-        raise
-
-    result = json.loads(resp_obj)
+    result = do_request(HTTP_POST, path, json.dumps(lport_obj),
+                        cluster=cluster)
+
     LOG.debug(_("Created logical port %(result)s on logical swtich %(uuid)s"),
               {'result': result['uuid'], 'uuid': lswitch_uuid})
     return result
@@ -834,15 +587,9 @@ def create_router_lport(cluster, lrouter_uuid, tenant_id, quantum_port_id,
     )
     path = _build_uri_path(LROUTERPORT_RESOURCE,
                            parent_resource_id=lrouter_uuid)
-    try:
-        resp_obj = do_single_request(HTTP_POST, path,
-                                     json.dumps(lport_obj),
-                                     cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
-        LOG.error(_("Logical router not found, Error: %s"), str(e))
-        raise
-
-    result = json.loads(resp_obj)
+    result = do_request(HTTP_POST, path, json.dumps(lport_obj),
+                        cluster=cluster)
+
     LOG.debug(_("Created logical port %(lport_uuid)s on "
                 "logical router %(lrouter_uuid)s"),
               {'lport_uuid': result['uuid'],
@@ -870,16 +617,9 @@ def update_router_lport(cluster, lrouter_uuid, lrouter_port_uuid,
     path = _build_uri_path(LROUTERPORT_RESOURCE,
                            lrouter_port_uuid,
                            parent_resource_id=lrouter_uuid)
-    try:
-        resp_obj = do_single_request(HTTP_PUT, path,
-                                     json.dumps(lport_obj),
-                                     cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
-        LOG.error(_("Logical router or router port not found, "
-                    "Error: %s"), str(e))
-        raise
-
-    result = json.loads(resp_obj)
+    result = do_request(HTTP_PUT, path,
+                        json.dumps(lport_obj),
+                        cluster=cluster)
     LOG.debug(_("Updated logical port %(lport_uuid)s on "
                 "logical router %(lrouter_uuid)s"),
               {'lport_uuid': lrouter_port_uuid, 'lrouter_uuid': lrouter_uuid})
@@ -889,11 +629,7 @@ def update_router_lport(cluster, lrouter_uuid, lrouter_port_uuid,
 def delete_router_lport(cluster, lrouter_uuid, lport_uuid):
     """Creates a logical port on the assigned logical router."""
     path = _build_uri_path(LROUTERPORT_RESOURCE, lport_uuid, lrouter_uuid)
-    try:
-        do_single_request(HTTP_DELETE, path, cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
-        LOG.error(_("Logical router not found, Error: %s"), str(e))
-        raise
+    do_request(HTTP_DELETE, path, cluster=cluster)
     LOG.debug(_("Delete logical router port %(lport_uuid)s on "
                 "logical router %(lrouter_uuid)s"),
               {'lport_uuid': lport_uuid,
@@ -903,19 +639,13 @@ def delete_router_lport(cluster, lrouter_uuid, lport_uuid):
 def delete_peer_router_lport(cluster, lr_uuid, ls_uuid, lp_uuid):
     nvp_port = get_port(cluster, ls_uuid, lp_uuid,
                         relations="LogicalPortAttachment")
-    try:
-        relations = nvp_port.get('_relations')
-        if relations:
-            att_data = relations.get('LogicalPortAttachment')
-            if att_data:
-                lrp_uuid = att_data.get('peer_port_uuid')
-                if lrp_uuid:
-                    delete_router_lport(cluster, lr_uuid, lrp_uuid)
-    except (NvpApiClient.NvpApiException, NvpApiClient.ResourceNotFound):
-        LOG.exception(_("Unable to fetch and delete peer logical "
-                        "router port for logical switch port:%s"),
-                      lp_uuid)
-        raise
+    relations = nvp_port.get('_relations')
+    if relations:
+        att_data = relations.get('LogicalPortAttachment')
+        if att_data:
+            lrp_uuid = att_data.get('peer_port_uuid')
+            if lrp_uuid:
+                delete_router_lport(cluster, lr_uuid, lrp_uuid)
 
 
 def find_router_gw_port(context, cluster, router_id):
@@ -954,36 +684,21 @@ def plug_router_port_attachment(cluster, router_id, port_id,
         if attachment_vlan:
             attach_obj['vlan_id'] = attachment_vlan
     else:
+        # TODO(salv-orlando): avoid raising generic exception
         raise Exception(_("Invalid NVP attachment type '%s'"),
                         nvp_attachment_type)
-    try:
-        resp_obj = do_single_request(
-            HTTP_PUT, uri, json.dumps(attach_obj), cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
-        LOG.exception(_("Router Port not found, Error: %s"), str(e))
-        raise
-    except NvpApiClient.Conflict as e:
-        LOG.exception(_("Conflict while setting router port attachment"))
-        raise
-    except NvpApiClient.NvpApiException as e:
-        LOG.exception(_("Unable to plug attachment into logical router port"))
-        raise
-    result = json.loads(resp_obj)
-    return result
+    return do_request(HTTP_PUT, uri, json.dumps(attach_obj), cluster=cluster)
 
 
 def get_port_status(cluster, lswitch_id, port_id):
     """Retrieve the operational status of the port."""
     try:
-        r = do_single_request(HTTP_GET,
-                              "/ws.v1/lswitch/%s/lport/%s/status" %
-                              (lswitch_id, port_id), cluster=cluster)
-        r = json.loads(r)
-    except NvpApiClient.ResourceNotFound as e:
+        r = do_request(HTTP_GET,
+                       "/ws.v1/lswitch/%s/lport/%s/status" %
+                       (lswitch_id, port_id), cluster=cluster)
+    except exception.NotFound as e:
         LOG.error(_("Port not found, Error: %s"), str(e))
         raise exception.PortNotFound(port_id=port_id, net_id=lswitch_id)
-    except NvpApiClient.NvpApiException as e:
-        raise exception.QuantumException()
     if r['link_status_up'] is True:
         return constants.PORT_STATUS_ACTIVE
     else:
@@ -993,19 +708,8 @@ def get_port_status(cluster, lswitch_id, port_id):
 def _plug_interface(cluster, lswitch_id, lport_id, att_obj):
     uri = _build_uri_path(LSWITCHPORT_RESOURCE, lport_id, lswitch_id,
                           is_attachment=True)
-    try:
-        resp_obj = do_single_request(HTTP_PUT, uri, json.dumps(att_obj),
-                                     cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("Exception while plugging an attachment:%(att)s "
-                        "into NVP port:%(port)s for NVP logical switch "
-                        "%(net)s"), {'net': lswitch_id,
-                                     'port': lport_id,
-                                     'att': att_obj})
-        raise
-
-    result = json.dumps(resp_obj)
-    return result
+    return do_request(HTTP_PUT, uri, json.dumps(att_obj),
+                      cluster=cluster)
 
 
 def plug_l2_gw_service(cluster, lswitch_id, lport_id,
@@ -1034,37 +738,36 @@ EXT_SECURITY_PROFILE_ID_SCOPE = 'nova_spid'
 TENANT_ID_SCOPE = 'os_tid'
 
 
-def format_exception(etype, e, execption_locals, request=None):
+def format_exception(etype, e, exception_locals):
     """Consistent formatting for exceptions.
 
     :param etype: a string describing the exception type.
     :param e: the exception.
-    :param request: the request object.
     :param execption_locals: calling context local variable dict.
     :returns: a formatted string.
     """
     msg = ["Error. %s exception: %s." % (etype, e)]
-    if request:
-        msg.append("request=[%s]" % request)
-        if request.body:
-            msg.append("request.body=[%s]" % str(request.body))
-    l = dict((k, v) for k, v in execption_locals if k != 'request')
+    l = dict((k, v) for k, v in exception_locals.iteritems()
+             if k != 'request')
     msg.append("locals=[%s]" % str(l))
     return ' '.join(msg)
 
 
 def do_request(*args, **kwargs):
-    """Convenience function wraps do_single_request.
+    """Issue a request to the cluster specified in kwargs.
 
     :param args: a list of positional arguments.
     :param kwargs: a list of keyworkds arguments.
-    :returns: the result of do_single_request loaded into a python object
-        or None.
+    :returns: the result of the operation loaded into a python
+        object or None.
     """
-    res = do_single_request(*args, **kwargs)
-    if res:
-        return json.loads(res)
-    return res
+    cluster = kwargs["cluster"]
+    try:
+        res = cluster.api_client.request(*args)
+        if res:
+            return json.loads(res)
+    except NvpApiClient.ResourceNotFound:
+        raise exception.NotFound()
 
 
 def mk_body(**kwargs):
@@ -1093,18 +796,14 @@ def create_security_profile(cluster, tenant_id, security_profile):
                      {'ethertype': 'IPv6'}]}
     tags = [dict(scope='os_tid', tag=tenant_id),
             dict(scope='quantum', tag=QUANTUM_VERSION)]
-    try:
-        display_name = _check_and_truncate_name(security_profile.get('name'))
-        body = mk_body(
-            tags=tags, display_name=display_name,
-            logical_port_ingress_rules=(
-                hidden_rules['logical_port_ingress_rules']),
-            logical_port_egress_rules=hidden_rules['logical_port_egress_rules']
-        )
-        rsp = do_request(HTTP_POST, path, body, cluster=cluster)
-    except NvpApiClient.NvpApiException as e:
-        LOG.error(format_exception("Unknown", e, locals()))
-        raise exception.QuantumException()
+    display_name = _check_and_truncate_name(security_profile.get('name'))
+    body = mk_body(
+        tags=tags, display_name=display_name,
+        logical_port_ingress_rules=(
+            hidden_rules['logical_port_ingress_rules']),
+        logical_port_egress_rules=hidden_rules['logical_port_egress_rules']
+    )
+    rsp = do_request(HTTP_POST, path, body, cluster=cluster)
     if security_profile.get('name') == 'default':
         # If security group is default allow ip traffic between
         # members of the same security profile is allowed and ingress traffic
@@ -1139,8 +838,9 @@ def update_security_group_rules(cluster, spid, rules):
             logical_port_ingress_rules=rules['logical_port_ingress_rules'],
             logical_port_egress_rules=rules['logical_port_egress_rules'])
         rsp = do_request(HTTP_PUT, path, body, cluster=cluster)
-    except NvpApiClient.NvpApiException as e:
+    except exception.NotFound as e:
         LOG.error(format_exception("Unknown", e, locals()))
+        #FIXME(salvatore-orlando): This should not raise QuantumException
         raise exception.QuantumException()
     LOG.debug(_("Updated Security Profile: %s"), rsp)
     return rsp
@@ -1151,7 +851,8 @@ def delete_security_profile(cluster, spid):
 
     try:
         do_request(HTTP_DELETE, path, cluster=cluster)
-    except NvpApiClient.NvpApiException as e:
+    except exception.NotFound as e:
+        # FIXME(salv-orlando): should not raise QuantumException
         LOG.error(format_exception("Unknown", e, locals()))
         raise exception.QuantumException()
 
@@ -1168,18 +869,8 @@ def _create_nat_match_obj(**kwargs):
 def _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj):
     LOG.debug(_("Creating NAT rule: %s"), nat_rule_obj)
     uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id)
-    try:
-        resp = do_single_request(HTTP_POST, uri, json.dumps(nat_rule_obj),
-                                 cluster=cluster)
-    except NvpApiClient.ResourceNotFound:
-        LOG.exception(_("NVP Logical Router %s not found"), router_id)
-        raise
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("An error occurred while creating the NAT rule "
-                        "on the NVP platform"))
-        raise
-    rule = json.loads(resp)
-    return rule
+    return do_request(HTTP_POST, uri, json.dumps(nat_rule_obj),
+                      cluster=cluster)
 
 
 def _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj):
@@ -1297,44 +988,13 @@ def delete_nat_rules_by_match(cluster, router_id, rule_type,
 
 def delete_router_nat_rule(cluster, router_id, rule_id):
     uri = _build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id)
-    try:
-        do_single_request(HTTP_DELETE, uri, cluster=cluster)
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("An error occurred while removing NAT rule "
-                        "'%(nat_rule_uuid)s' for logical "
-                        "router '%(lrouter_uuid)s'"),
-                      {'nat_rule_uuid': rule_id, 'lrouter_uuid': router_id})
-        raise
-
-
-def get_router_nat_rule(cluster, tenant_id, router_id, rule_id):
-    uri = _build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id)
-    try:
-        resp = do_single_request(HTTP_GET, uri, cluster=cluster)
-    except NvpApiClient.ResourceNotFound:
-        LOG.exception(_("NAT rule %s not found"), rule_id)
-        raise
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("An error occured while retrieving NAT rule '%s'"
-                        "from NVP platform"), rule_id)
-        raise
-    res = json.loads(resp)
-    return res
+    do_request(HTTP_DELETE, uri, cluster=cluster)
 
 
 def query_nat_rules(cluster, router_id, fields="*", filters=None):
     uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id,
                           fields=fields, filters=filters)
-    try:
-        result = get_all_query_pages(uri, cluster)
-    except NvpApiClient.ResourceNotFound:
-        LOG.exception(_("NVP Logical Router '%s' not found"), router_id)
-        raise
-    except NvpApiClient.NvpApiException:
-        LOG.exception(_("An error occured while retrieving NAT rules for "
-                        "NVP logical router '%s'"), router_id)
-        raise
-    return result
+    return get_all_query_pages(uri, cluster)
 
 
 # NOTE(salvatore-orlando): The following FIXME applies in general to
@@ -1344,7 +1004,7 @@ def update_lrouter_port_ips(cluster, lrouter_id, lport_id,
                             ips_to_add, ips_to_remove):
     uri = _build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id)
     try:
-        port = json.loads(do_single_request(HTTP_GET, uri, cluster=cluster))
+        port = do_request(HTTP_GET, uri, cluster=cluster)
         # TODO(salvatore-orlando): Enforce ips_to_add intersection with
         # ips_to_remove is empty
         ip_address_set = set(port['ip_addresses'])
@@ -1352,8 +1012,9 @@ def update_lrouter_port_ips(cluster, lrouter_id, lport_id,
         ip_address_set = ip_address_set | set(ips_to_add)
         # Set is not JSON serializable - convert to list
         port['ip_addresses'] = list(ip_address_set)
-        do_single_request(HTTP_PUT, uri, json.dumps(port), cluster=cluster)
-    except NvpApiClient.ResourceNotFound as e:
+        do_request(HTTP_PUT, uri, json.dumps(port), cluster=cluster)
+    except exception.NotFound as e:
+        # FIXME(salv-orlando):avoid raising different exception
         data = {'lport_id': lport_id, 'lrouter_id': lrouter_id}
         msg = (_("Router Port %(lport_id)s not found on router "
                  "%(lrouter_id)s") % data)
@@ -1384,21 +1045,21 @@ def create_lqueue(cluster, lqueue):
     uri = _build_uri_path(LQUEUE_RESOURCE)
     lqueue['tags'] = [{'tag': QUANTUM_VERSION, 'scope': 'quantum'}]
     try:
-        resp_obj = do_single_request(HTTP_POST, uri, json.dumps(lqueue),
-                                     cluster=cluster)
+        return do_request(HTTP_POST, uri, json.dumps(lqueue),
+                          cluster=cluster)['uuid']
     except NvpApiClient.NvpApiException:
+        # FIXME(salv-orlando): This should not raise QauntumException
         LOG.exception(_("Failed to create logical queue"))
         raise exception.QuantumException()
-    return json.loads(resp_obj)['uuid']
 
 
 def delete_lqueue(cluster, id):
     try:
-        do_single_request(HTTP_DELETE,
-                          _build_uri_path(LQUEUE_RESOURCE,
-                                          resource_id=id),
-                          cluster=cluster)
+        do_request(HTTP_DELETE, _build_uri_path(LQUEUE_RESOURCE,
+                                                resource_id=id),
+                   cluster=cluster)
     except Exception:
+        # FIXME(salv-orlando): This should not raise QauntumException
         LOG.exception(_("Failed to delete logical queue"))
         raise exception.QuantumException()
 
@@ -1408,14 +1069,13 @@ def delete_lqueue(cluster, id):
 # -----------------------------------------------------------------------------
 def config_helper(http_method, http_uri, cluster):
     try:
-        resp = do_single_request(http_method,
-                                 http_uri,
-                                 cluster=cluster)
+        return do_request(http_method,
+                          http_uri,
+                          cluster=cluster)
     except Exception as e:
         msg = ("Error '%s' when connecting to controller(s): %s."
                % (str(e), ', '.join(cluster.nvp_controllers)))
         raise Exception(msg)
-    return json.loads(resp)
 
 
 def check_cluster_connectivity(cluster):
diff --git a/quantum/tests/unit/nicira/etc/fake_get_lqueue.json b/quantum/tests/unit/nicira/etc/fake_get_lqueue.json
new file mode 100644 (file)
index 0000000..414945b
--- /dev/null
@@ -0,0 +1,11 @@
+{
+   "display_name": "%(display_name)s",
+   "uuid": "%(uuid)s",
+   "type": "LogicalSwitchConfig",
+   "_schema": "/ws.v1/schema/LogicalQueueConfig",
+   "dscp": "%(dscp)s",
+   "max_bandwidth_rate": "%(max_bandwidth_rate)s",
+   "min_bandwidth_rate": "%(min_bandwidth_rate)s",
+   "qos_marking": "%(qos_marking)s",
+   "_href": "/ws.v1/lqueue/%(uuid)s"
+}
index c4b989527b17c75243315709fe1ed96a4c12e77e..5d6bfb0de973d590443cc24eec7a469a0b4f0f5f 100644 (file)
@@ -1,5 +1,6 @@
 {
   "display_name": "%(display_name)s",
+  "admin_status_enabled": "%(admin_status_enabled)s",
   "_href": "/ws.v1/lrouter/%(lr_uuid)s/lport/%(uuid)s",
   "tags":
     [{"scope": "q_port_id", "tag": "%(quantum_port_id)s"},
index 16b78dd87987d278cf35c6e24e4c004e1c941e3b..0f5718c316d7f8ebb67c4a12630a1f83fe1891ef 100644 (file)
@@ -1,4 +1,4 @@
-{"display_name": "%(uuid)s",
+{"display_name": "%(display_name)s",
    "_relations":
    {"LogicalPortStatus":
       {"type": "LogicalSwitchPortStatus",
@@ -12,7 +12,7 @@
     {"scope": "vm_id", "tag": "%(quantum_device_id)s"},
     {"scope": "os_tid", "tag": "%(tenant_id)s"}],
  "uuid": "%(uuid)s",
- "admin_status_enabled": true,
+ "admin_status_enabled": "%(admin_status_enabled)s",
  "type": "LogicalSwitchPortConfig",
  "_schema": "/ws.v1/schema/LogicalSwitchPortConfig",
  "_href": "/ws.v1/lswitch/%(ls_uuid)s/lport/%(uuid)s"
index fa8e5f4dab02afc739a314078a84311474758d0b..0df7971b0e393ad6ebb0133d5864acf2555fe57e 100644 (file)
@@ -17,6 +17,7 @@
  "link_status_up": false,
  "_schema": "/ws.v1/schema/LogicalSwitchPortStatus",
  "admin_status_enabled": true,
- "fabric_status_up": false,
+ "fabric_status_up": true,
+ "link_status_up": true,
  "type": "LogicalSwitchPortStatus"
 }
diff --git a/quantum/tests/unit/nicira/etc/fake_get_security_profile.json b/quantum/tests/unit/nicira/etc/fake_get_security_profile.json
new file mode 100644 (file)
index 0000000..898e493
--- /dev/null
@@ -0,0 +1,10 @@
+{
+   "display_name": "%(display_name)s",
+   "_href": "/ws.v1/security-profile/%(uuid)s",
+   "tags": [{"scope": "os_tid", "tag": "%(tenant_id)s"},
+            {"scope": "nova_spid", "tag": "%(nova_spid)s"}],
+   "logical_port_egress_rules": %(logical_port_egress_rules_json)s,
+   "_schema": "/ws.v1/schema/SecurityProfileConfig",
+   "logical_port_ingress_rules": %(logical_port_ingress_rules_json)s,
+   "uuid": "%(uuid)s"
+}
index 5faa2559133a3d21ec726417a95b6836a3807a89..c58fa41c7edf9110f1345da926129da8c071341c 100644 (file)
@@ -3,6 +3,8 @@
     {
       %(peer_port_href_field)s
       %(peer_port_uuid_field)s
+      %(l3_gateway_service_uuid_field)s
+      %(vlan_id_field)s
       "_href": "/ws.v1/lrouter/%(lr_uuid)s/lport/%(lp_uuid)s/attachment",
       "type": "%(type)s",
       "schema": "/ws.v1/schema/%(type)s"
index 40ac6e853db3ba3ed59f59a2fbb83a96d141e9bb..7ea7666fa203757676956319b2b35487d863cf6d 100644 (file)
@@ -70,6 +70,8 @@ class FakeClient:
         LROUTER_LPORT_ATT: "fake_get_lrouter_lport_att.json",
         LROUTER_STATUS: "fake_get_lrouter_status.json",
         LROUTER_NAT_RESOURCE: "fake_get_lrouter_nat.json",
+        SECPROF_RESOURCE: "fake_get_security_profile.json",
+        LQUEUE_RESOURCE: "fake_get_lqueue.json",
         GWSERVICE_RESOURCE: "fake_get_gwservice.json"
     }
 
@@ -157,18 +159,24 @@ class FakeClient:
         fake_lswitch['lport_count'] = 0
         return fake_lswitch
 
-    def _add_lrouter(self, body):
+    def _build_lrouter(self, body, uuid=None):
         fake_lrouter = json.loads(body)
-        fake_lrouter['uuid'] = uuidutils.generate_uuid()
-        self._fake_lrouter_dict[fake_lrouter['uuid']] = fake_lrouter
+        if uuid:
+            fake_lrouter['uuid'] = uuid
         fake_lrouter['tenant_id'] = self._get_tag(fake_lrouter, 'os_tid')
-        fake_lrouter['lport_count'] = 0
         default_nexthop = fake_lrouter['routing_config'].get(
             'default_route_next_hop')
         fake_lrouter['default_next_hop'] = default_nexthop.get(
             'gateway_ip_address', '0.0.0.0')
         return fake_lrouter
 
+    def _add_lrouter(self, body):
+        fake_lrouter = self._build_lrouter(body,
+                                           uuidutils.generate_uuid())
+        self._fake_lrouter_dict[fake_lrouter['uuid']] = fake_lrouter
+        fake_lrouter['lport_count'] = 0
+        return fake_lrouter
+
     def _add_lqueue(self, body):
         fake_lqueue = json.loads(body)
         fake_lqueue['uuid'] = uuidutils.generate_uuid()
@@ -198,13 +206,14 @@ class FakeClient:
         self._fake_lswitch_lportstatus_dict[new_uuid] = fake_lport_status
         return fake_lport
 
-    def _add_lrouter_lport(self, body, lr_uuid):
+    def _build_lrouter_lport(self, body, new_uuid=None, lr_uuid=None):
         fake_lport = json.loads(body)
-        new_uuid = uuidutils.generate_uuid()
-        fake_lport['uuid'] = new_uuid
-        # put the tenant_id and the ls_uuid in the main dict
+        if new_uuid:
+            fake_lport['uuid'] = new_uuid
+        # put the tenant_id and the le_uuid in the main dict
         # for simplyfying templating
-        fake_lport['lr_uuid'] = lr_uuid
+        if lr_uuid:
+            fake_lport['lr_uuid'] = lr_uuid
         fake_lport['tenant_id'] = self._get_tag(fake_lport, 'os_tid')
         fake_lport['quantum_port_id'] = self._get_tag(fake_lport,
                                                       'q_port_id')
@@ -212,8 +221,16 @@ class FakeClient:
         if 'ip_addresses' in fake_lport:
             ip_addresses_json = json.dumps(fake_lport['ip_addresses'])
             fake_lport['ip_addresses_json'] = ip_addresses_json
+        return fake_lport
+
+    def _add_lrouter_lport(self, body, lr_uuid):
+        new_uuid = uuidutils.generate_uuid()
+        fake_lport = self._build_lrouter_lport(body, new_uuid, lr_uuid)
         self._fake_lrouter_lport_dict[fake_lport['uuid']] = fake_lport
-        fake_lrouter = self._fake_lrouter_dict[lr_uuid]
+        try:
+            fake_lrouter = self._fake_lrouter_dict[lr_uuid]
+        except KeyError:
+            raise NvpApiClient.ResourceNotFound()
         fake_lrouter['lport_count'] += 1
         fake_lport_status = fake_lport.copy()
         fake_lport_status['lr_tenant_id'] = fake_lrouter['tenant_id']
@@ -412,6 +429,15 @@ class FakeClient:
             for item in res_dict.itervalues():
                 if 'tags' in item:
                     item['tags_json'] = json.dumps(item['tags'])
+
+                # replace sec prof rules with their json dump
+                def jsonify_rules(rule_key):
+                    if rule_key in item:
+                        rules_json = json.dumps(item[rule_key])
+                        item['%s_json' % rule_key] = rules_json
+                jsonify_rules('logical_port_egress_rules')
+                jsonify_rules('logical_port_ingress_rules')
+
             items = [json.loads(response_template % res_dict[res_uuid])
                      for res_uuid in res_dict if res_uuid == target_uuid]
             if items:
@@ -436,6 +462,7 @@ class FakeClient:
         elif ('lswitch' in res_type or
               'lrouter' in res_type or
               self.SECPROF_RESOURCE in res_type or
+              self.LQUEUE_RESOURCE in res_type or
               'gatewayservice' in res_type):
             LOG.debug("UUIDS:%s", uuids)
             if uuids:
@@ -485,8 +512,14 @@ class FakeClient:
             val_func = self._validators.get(res_type)
             if val_func:
                 val_func(body_json)
-            resource = res_dict[uuids[-1]]
+            try:
+                resource = res_dict[uuids[-1]]
+            except KeyError:
+                raise NvpApiClient.ResourceNotFound()
             if not is_attachment:
+                edit_resource = getattr(self, '_build_%s' % res_type, None)
+                if edit_resource:
+                    body_json = edit_resource(body)
                 resource.update(body_json)
             else:
                 relations = resource.get("_relations", {})
index 5e8ddfc147dd01a23784a121d718c9c0b58367c5..de5c31fc089343ad20780c271d3a05d0dcafa800 100644 (file)
@@ -333,6 +333,16 @@ class NetworkGatewayDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
             for k, v in keys:
                 self.assertEqual(gw[self.resource][k], v)
 
+    def test_create_network_gateway_no_interface_name(self):
+        name = 'test-gw'
+        devices = [{'id': _uuid()}]
+        exp_devices = devices
+        exp_devices[0]['interface_name'] = 'breth0'
+        keys = [('devices', exp_devices), ('name', name)]
+        with self._network_gateway(name=name, devices=devices) as gw:
+            for k, v in keys:
+                self.assertEqual(gw[self.resource][k], v)
+
     def _test_delete_network_gateway(self, exp_gw_count=0):
         name = 'test-gw'
         devices = [{'id': _uuid(), 'interface_name': 'xxx'},
index c5d813fc180a2023e162619c743a2792b839f8ed..b49b1349385fcb9d6c3021a684c04a5951aa03b3 100644 (file)
@@ -28,9 +28,11 @@ from quantum.extensions import l3
 from quantum.extensions import providernet as pnet
 from quantum.extensions import securitygroup as secgrp
 from quantum import manager
+from quantum.openstack.common import uuidutils
 import quantum.plugins.nicira as nvp_plugin
 from quantum.plugins.nicira.extensions import nvp_networkgw
 from quantum.plugins.nicira.extensions import nvp_qos as ext_qos
+from quantum.plugins.nicira import NvpApiClient
 from quantum.plugins.nicira import nvplib
 from quantum.plugins.nicira import QuantumPlugin
 from quantum.tests.unit.nicira import fake_nvpapiclient
@@ -375,6 +377,21 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
     def test_router_create_with_gwinfo_and_l3_ext_net_with_vlan(self):
         self._test_router_create_with_gwinfo_and_l3_ext_net(444)
 
+    def test_router_create_nvp_error_returns_500(self, vlan_id=None):
+        with mock.patch.object(nvplib,
+                               'create_router_lport',
+                               side_effect=NvpApiClient.NvpApiException):
+            with self._create_l3_ext_network(vlan_id) as net:
+                with self.subnet(network=net) as s:
+                    data = {'router': {'tenant_id': 'whatever'}}
+                    data['router']['name'] = 'router1'
+                    data['router']['external_gateway_info'] = {
+                        'network_id': s['subnet']['network_id']}
+                    router_req = self.new_create_request(
+                        'routers', data, self.fmt)
+                    res = router_req.get_response(self.ext_api)
+                    self.assertEqual(500, res.status_int)
+
     def _test_router_update_gateway_on_l3_ext_net(self, vlan_id=None):
         with self.router() as r:
             with self.subnet() as s1:
@@ -414,6 +431,14 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
     def test_router_update_gateway_on_l3_ext_net_with_vlan(self):
         self._test_router_update_gateway_on_l3_ext_net(444)
 
+    def test_router_list_by_tenant_id(self):
+        with contextlib.nested(self.router(tenant_id='custom'),
+                               self.router(),
+                               self.router()
+                               ) as routers:
+            self._test_list_resources('router', [routers[0]],
+                                      query_params="tenant_id=custom")
+
     def test_create_l3_ext_network_with_vlan(self):
         self._test_create_l3_ext_network(666)
 
@@ -548,6 +573,22 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
             # Test that route is deleted after dhcp port is removed
             self.assertEquals(len(subnets[0]['host_routes']), 0)
 
+    def test_floatingip_disassociate(self):
+        with self.port() as p:
+            private_sub = {'subnet': {'id':
+                                      p['port']['fixed_ips'][0]['subnet_id']}}
+            with self.floatingip_no_assoc(private_sub) as fip:
+                port_id = p['port']['id']
+                body = self._update('floatingips', fip['floatingip']['id'],
+                                    {'floatingip': {'port_id': port_id}})
+                self.assertEqual(body['floatingip']['port_id'], port_id)
+                # Disassociate
+                body = self._update('floatingips', fip['floatingip']['id'],
+                                    {'floatingip': {'port_id': None}})
+                body = self._show('floatingips', fip['floatingip']['id'])
+                self.assertIsNone(body['floatingip']['port_id'])
+                self.assertIsNone(body['floatingip']['fixed_ip_address'])
+
 
 class NvpQoSTestExtensionManager(object):
 
@@ -838,6 +879,7 @@ class NiciraQuantumNVPOutOfSync(test_l3_plugin.L3NatTestCaseBase,
         res = self._create_port('json', net1['network']['id'])
         port = self.deserialize('json', res)
         self.fc._fake_lswitch_lport_dict.clear()
+        self.fc._fake_lswitch_lportstatus_dict.clear()
         req = self.new_show_request('ports', port['port']['id'])
         net = self.deserialize('json', req.get_response(self.api))
         self.assertEqual(net['port']['status'],
@@ -919,6 +961,18 @@ class TestNiciraNetworkGateway(test_l2_gw.NetworkGatewayDbTestCase,
             # Assert Quantum name is not truncated
             self.assertEqual(nw_gw[self.resource]['name'], name)
 
+    def test_create_network_gateway_nvp_error_returns_500(self):
+        def raise_nvp_api_exc(*args, **kwargs):
+            raise NvpApiClient.NvpApiException
+
+        with mock.patch.object(nvplib,
+                               'create_l2_gw_service',
+                               new=raise_nvp_api_exc):
+            res = self._create_network_gateway(
+                self.fmt, 'xxx', name='yyy',
+                devices=[{'id': uuidutils.generate_uuid()}])
+            self.assertEqual(500, res.status_int)
+
     def test_list_network_gateways(self):
         with self._network_gateway(name='test-gw-1') as gw1:
             with self._network_gateway(name='test_gw_2') as gw2:
index 87a694dd166c253f462ef533e304112e402ee13e..0153ab2671238887f3bf9d2e33d061df8eb3ed75 100644 (file)
 import mock
 import os
 
-from quantum.openstack.common import jsonutils as json
+from quantum.common import constants
+from quantum.common import exceptions
 import quantum.plugins.nicira as nvp_plugin
+from quantum.plugins.nicira.common import config  # noqa
+from quantum.plugins.nicira.common import exceptions as nvp_exc
 from quantum.plugins.nicira import nvp_cluster
 from quantum.plugins.nicira import NvpApiClient
 from quantum.plugins.nicira import nvplib
@@ -59,6 +62,10 @@ class NvplibTestCase(base.BaseTestCase):
         self.addCleanup(self.fc.reset_all)
         self.addCleanup(self.mock_nvpapi.stop)
 
+    def _build_tag_dict(self, tags):
+        # This syntax is needed for python 2.6 compatibility
+        return dict((t['scope'], t['tag']) for t in tags)
+
 
 class TestNvplibNatRules(NvplibTestCase):
 
@@ -74,8 +81,7 @@ class TestNvplibNatRules(NvplibTestCase):
         uri = nvplib._build_uri_path(nvplib.LROUTERNAT_RESOURCE,
                                      nat_rule['uuid'],
                                      lrouter['uuid'])
-        return json.loads(nvplib.do_single_request("GET", uri,
-                                                   cluster=self.fake_cluster))
+        return nvplib.do_request("GET", uri, cluster=self.fake_cluster)
 
     def test_create_lrouter_dnat_rule_v2(self):
         resp_obj = self._test_create_lrouter_dnat_rule(
@@ -179,11 +185,12 @@ class NvplibNegativeTests(base.BaseTestCase):
                           'new_hop')
 
 
-class NvplibL2GatewayTestCase(NvplibTestCase):
+class TestNvplibL2Gateway(NvplibTestCase):
 
-    def _create_gw_service(self, node_uuid, display_name):
+    def _create_gw_service(self, node_uuid, display_name,
+                           tenant_id='fake_tenant'):
         return nvplib.create_l2_gw_service(self.fake_cluster,
-                                           'fake-tenant',
+                                           tenant_id,
                                            display_name,
                                            [{'id': node_uuid,
                                              'interface_name': 'xxx'}])
@@ -227,6 +234,15 @@ class NvplibL2GatewayTestCase(NvplibTestCase):
         self.assertEqual(len(results), 2)
         self.assertEqual(sorted(gw_ids), sorted([r['uuid'] for r in results]))
 
+    def test_list_l2_gw_service_by_tenant(self):
+        gw_ids = [self._create_gw_service(
+                  _uuid(), name, tenant_id=name)['uuid']
+                  for name in ('fake-1', 'fake-2')]
+        results = nvplib.get_l2_gw_services(self.fake_cluster,
+                                            tenant_id='fake-1')
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0]['uuid'], gw_ids[0])
+
     def test_delete_l2_gw_service(self):
         display_name = 'fake-gateway'
         node_uuid = _uuid()
@@ -248,35 +264,805 @@ class NvplibL2GatewayTestCase(NvplibTestCase):
                                     'fake-gw-port',
                                     gw_id,
                                     True)
-        json.loads(nvplib.plug_l2_gw_service(self.fake_cluster,
-                                             lswitch['uuid'],
-                                             lport['uuid'],
-                                             gw_id))
+        nvplib.plug_l2_gw_service(self.fake_cluster,
+                                  lswitch['uuid'],
+                                  lport['uuid'],
+                                  gw_id)
         uri = nvplib._build_uri_path(nvplib.LSWITCHPORT_RESOURCE,
                                      lport['uuid'],
                                      lswitch['uuid'],
                                      is_attachment=True)
-        resp_obj = json.loads(
-            nvplib.do_single_request("GET", uri,
-                                     cluster=self.fake_cluster))
+        resp_obj = nvplib.do_request("GET", uri,
+                                     cluster=self.fake_cluster)
         self.assertIn('LogicalPortAttachment', resp_obj)
         self.assertEqual(resp_obj['LogicalPortAttachment']['type'],
                          'L2GatewayAttachment')
 
 
-class TestNvpLibLogicalPorts(NvplibTestCase):
+class TestNvplibLogicalSwitches(NvplibTestCase):
 
-    def test_get_port_by_tag(self):
+    def test_create_and_get_lswitches_single(self):
         tenant_id = 'pippo'
-        quantum_port_id = 'whatever'
-        lswitch = nvplib.create_lswitch(self.fake_cluster, tenant_id,
+        lswitch = nvplib.create_lswitch(self.fake_cluster,
+                                        tenant_id,
+                                        'fake-switch')
+        res_lswitch = nvplib.get_lswitches(self.fake_cluster,
+                                           lswitch['uuid'])
+        self.assertEqual(len(res_lswitch), 1)
+        self.assertEqual(res_lswitch[0]['uuid'],
+                         lswitch['uuid'])
+
+    def test_create_and_get_lswitches_single_name_exceeds_40_chars(self):
+        tenant_id = 'pippo'
+        lswitch = nvplib.create_lswitch(self.fake_cluster,
+                                        tenant_id,
+                                        '*' * 50)
+        res_lswitch = nvplib.get_lswitches(self.fake_cluster,
+                                           lswitch['uuid'])
+        self.assertEqual(len(res_lswitch), 1)
+        self.assertEqual(res_lswitch[0]['uuid'], lswitch['uuid'])
+        self.assertEqual(res_lswitch[0]['display_name'], '*' * 40)
+
+    def test_create_and_get_lswitches_multiple(self):
+        tenant_id = 'pippo'
+        main_lswitch = nvplib.create_lswitch(
+            self.fake_cluster, tenant_id, 'fake-switch',
+            tags=[{'scope': 'multi_lswitch', 'tag': 'True'}])
+        # Create secondary lswitch
+        nvplib.create_lswitch(
+            self.fake_cluster, tenant_id, 'fake-switch-2',
+            quantum_net_id=main_lswitch['uuid'])
+        res_lswitch = nvplib.get_lswitches(self.fake_cluster,
+                                           main_lswitch['uuid'])
+        self.assertEqual(len(res_lswitch), 2)
+        self.assertEqual(res_lswitch[0]['uuid'],
+                         main_lswitch['uuid'])
+        switch_1_tags = self._build_tag_dict(res_lswitch[0]['tags'])
+        switch_2_tags = self._build_tag_dict(res_lswitch[1]['tags'])
+        self.assertIn('multi_lswitch', switch_1_tags)
+        self.assertNotIn('multi_lswitch', switch_2_tags)
+        self.assertNotIn('quantum_net_id', switch_1_tags)
+        self.assertIn('quantum_net_id', switch_2_tags)
+        self.assertEqual(switch_2_tags['quantum_net_id'],
+                         main_lswitch['uuid'])
+
+    def test_update_lswitch(self):
+        new_name = 'new-name'
+        new_tags = [{'scope': 'new_tag', 'tag': 'xxx'}]
+        lswitch = nvplib.create_lswitch(self.fake_cluster,
+                                        'pippo',
                                         'fake-switch')
+        nvplib.update_lswitch(self.fake_cluster, lswitch['uuid'],
+                              new_name, tags=new_tags)
+        res_lswitch = nvplib.get_lswitches(self.fake_cluster,
+                                           lswitch['uuid'])
+        self.assertEqual(len(res_lswitch), 1)
+        self.assertEqual(res_lswitch[0]['display_name'], new_name)
+        switch_tags = self._build_tag_dict(res_lswitch[0]['tags'])
+        self.assertIn('new_tag', switch_tags)
+        self.assertEqual(switch_tags['new_tag'], 'xxx')
+
+    def test_update_non_existing_lswitch_raises(self):
+        self.assertRaises(exceptions.NetworkNotFound,
+                          nvplib.update_lswitch,
+                          self.fake_cluster, 'whatever',
+                          'foo', 'bar')
+
+    def test_delete_networks(self):
+        lswitch = nvplib.create_lswitch(self.fake_cluster,
+                                        'pippo',
+                                        'fake-switch')
+        nvplib.delete_networks(self.fake_cluster, lswitch['uuid'],
+                               [lswitch['uuid']])
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.get_lswitches,
+                          self.fake_cluster,
+                          lswitch['uuid'])
+
+    def test_delete_non_existing_lswitch_raises(self):
+        self.assertRaises(exceptions.NetworkNotFound,
+                          nvplib.delete_networks,
+                          self.fake_cluster, 'whatever', ['whatever'])
+
+
+class TestNvplibLogicalRouters(NvplibTestCase):
+
+    def _verify_lrouter(self, res_lrouter,
+                        expected_uuid,
+                        expected_display_name,
+                        expected_nexthop,
+                        expected_tenant_id):
+        self.assertEqual(res_lrouter['uuid'], expected_uuid)
+        nexthop = (res_lrouter['routing_config']
+                   ['default_route_next_hop']['gateway_ip_address'])
+        self.assertEqual(nexthop, expected_nexthop)
+        router_tags = self._build_tag_dict(res_lrouter['tags'])
+        self.assertIn('os_tid', router_tags)
+        self.assertEqual(res_lrouter['display_name'], expected_display_name)
+        self.assertEqual(expected_tenant_id, router_tags['os_tid'])
+
+    def test_get_lrouters(self):
+        lrouter_uuids = [nvplib.create_lrouter(
+            self.fake_cluster, 'pippo', 'fake-lrouter-%s' % k,
+            '10.0.0.1')['uuid'] for k in range(0, 3)]
+        routers = nvplib.get_lrouters(self.fake_cluster, 'pippo')
+        for router in routers:
+            self.assertIn(router['uuid'], lrouter_uuids)
+
+    def test_create_and_get_lrouter(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        res_lrouter = nvplib.get_lrouter(self.fake_cluster,
+                                         lrouter['uuid'])
+        self._verify_lrouter(res_lrouter, lrouter['uuid'],
+                             'fake-lrouter', '10.0.0.1', 'pippo')
+
+    def test_create_and_get_lrouter_name_exceeds_40chars(self):
+        display_name = '*' * 50
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        display_name,
+                                        '10.0.0.1')
+        res_lrouter = nvplib.get_lrouter(self.fake_cluster,
+                                         lrouter['uuid'])
+        self._verify_lrouter(res_lrouter, lrouter['uuid'],
+                             '*' * 40, '10.0.0.1', 'pippo')
+
+    def test_update_lrouter_no_nexthop(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter = nvplib.update_lrouter(self.fake_cluster,
+                                        lrouter['uuid'],
+                                        'new_name',
+                                        None)
+        res_lrouter = nvplib.get_lrouter(self.fake_cluster,
+                                         lrouter['uuid'])
+        self._verify_lrouter(res_lrouter, lrouter['uuid'],
+                             'new_name', '10.0.0.1', 'pippo')
+
+    def test_update_lrouter(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter = nvplib.update_lrouter(self.fake_cluster,
+                                        lrouter['uuid'],
+                                        'new_name',
+                                        '192.168.0.1')
+        res_lrouter = nvplib.get_lrouter(self.fake_cluster,
+                                         lrouter['uuid'])
+        self._verify_lrouter(res_lrouter, lrouter['uuid'],
+                             'new_name', '192.168.0.1', 'pippo')
+
+    def test_update_nonexistent_lrouter_raises(self):
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.update_lrouter,
+                          self.fake_cluster, 'whatever',
+                          'foo', '9.9.9.9')
+
+    def test_delete_lrouter(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        nvplib.delete_lrouter(self.fake_cluster, lrouter['uuid'])
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.get_lrouter,
+                          self.fake_cluster,
+                          lrouter['uuid'])
+
+    def test_query_lrouter_ports(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        router_port_uuids = [nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo',
+            'qp_id_%s' % k, 'port-%s' % k, True,
+            ['192.168.0.%s' % k])['uuid'] for k in range(0, 3)]
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 3)
+        for res_port in ports:
+            self.assertIn(res_port['uuid'], router_port_uuids)
+
+    def test_query_lrouter_lports_nonexistent_lrouter_raises(self):
+        self.assertRaises(
+            exceptions.NotFound, nvplib.create_router_lport,
+            self.fake_cluster, 'booo', 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+
+    def test_create_and_get_lrouter_port(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 1)
+        res_port = ports[0]
+        port_tags = self._build_tag_dict(res_port['tags'])
+        self.assertEqual(['192.168.0.1'], res_port['ip_addresses'])
+        self.assertIn('os_tid', port_tags)
+        self.assertIn('q_port_id', port_tags)
+        self.assertEqual('pippo', port_tags['os_tid'])
+        self.assertEqual('quantum_port_id', port_tags['q_port_id'])
+
+    def test_create_lrouter_port_nonexistent_router_raises(self):
+        self.assertRaises(
+            exceptions.NotFound, nvplib.create_router_lport,
+            self.fake_cluster, 'booo', 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+
+    def test_update_lrouter_port(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        nvplib.update_router_lport(
+            self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'],
+            'pippo', 'another_port_id', 'name', False,
+            ['192.168.0.1', '10.10.10.254'])
+
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 1)
+        res_port = ports[0]
+        port_tags = self._build_tag_dict(res_port['tags'])
+        self.assertEqual(['192.168.0.1', '10.10.10.254'],
+                         res_port['ip_addresses'])
+        self.assertEqual('False', res_port['admin_status_enabled'])
+        self.assertIn('os_tid', port_tags)
+        self.assertIn('q_port_id', port_tags)
+        self.assertEqual('pippo', port_tags['os_tid'])
+        self.assertEqual('another_port_id', port_tags['q_port_id'])
+
+    def test_update_lrouter_port_nonexistent_router_raises(self):
+        self.assertRaises(
+            exceptions.NotFound, nvplib.update_router_lport,
+            self.fake_cluster, 'boo-router', 'boo-port', 'pippo',
+            'quantum_port_id', 'name', True, ['192.168.0.1'])
+
+    def test_update_lrouter_port_nonexistent_port_raises(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        self.assertRaises(
+            exceptions.NotFound, nvplib.update_router_lport,
+            self.fake_cluster, lrouter['uuid'], 'boo-port', 'pippo',
+            'quantum_port_id', 'name', True, ['192.168.0.1'])
+
+    def test_delete_lrouter_port(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'x', 'y', True, [])
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 1)
+        nvplib.delete_router_lport(self.fake_cluster, lrouter['uuid'],
+                                   lrouter_port['uuid'])
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertFalse(len(ports))
+
+    def test_delete_lrouter_port_nonexistent_router_raises(self):
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.delete_router_lport,
+                          self.fake_cluster, 'xyz', 'abc')
+
+    def test_delete_lrouter_port_nonexistent_port_raises(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.delete_router_lport,
+                          self.fake_cluster, lrouter['uuid'], 'abc')
+
+    def test_delete_peer_lrouter_port(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'x', 'y', True, [])
+
+        def fakegetport(*args, **kwargs):
+            return {'_relations': {'LogicalPortAttachment':
+                                   {'peer_port_uuid': lrouter_port['uuid']}}}
+        # mock get_port
+        with mock.patch.object(nvplib, 'get_port', new=fakegetport):
+            nvplib.delete_peer_router_lport(self.fake_cluster,
+                                            lrouter_port['uuid'],
+                                            'whatwever', 'whatever')
+
+    def test_update_lrouter_port_ips_add_only(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        nvplib.update_lrouter_port_ips(
+            self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'],
+            ['10.10.10.254'], [])
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 1)
+        res_port = ports[0]
+        self.assertEqual(['10.10.10.254', '192.168.0.1'],
+                         res_port['ip_addresses'])
+
+    def test_update_lrouter_port_ips_remove_only(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1', '10.10.10.254'])
+        nvplib.update_lrouter_port_ips(
+            self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'],
+            [], ['10.10.10.254'])
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 1)
+        res_port = ports[0]
+        self.assertEqual(['192.168.0.1'], res_port['ip_addresses'])
+
+    def test_update_lrouter_port_ips_add_and_remove(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        nvplib.update_lrouter_port_ips(
+            self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'],
+            ['10.10.10.254'], ['192.168.0.1'])
+        ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(ports), 1)
+        res_port = ports[0]
+        self.assertEqual(['10.10.10.254'], res_port['ip_addresses'])
+
+    def test_update_lrouter_port_ips_nonexistent_router_raises(self):
+        self.assertRaises(
+            nvp_exc.NvpPluginException, nvplib.update_lrouter_port_ips,
+            self.fake_cluster, 'boo-router', 'boo-port', [], [])
+
+    def test_update_lrouter_port_ips_nvp_exception_raises(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+
+        def raise_nvp_exc(*args, **kwargs):
+            raise NvpApiClient.NvpApiException()
+
+        with mock.patch.object(nvplib, 'do_request', new=raise_nvp_exc):
+            self.assertRaises(
+                nvp_exc.NvpPluginException, nvplib.update_lrouter_port_ips,
+                self.fake_cluster, lrouter['uuid'],
+                lrouter_port['uuid'], [], [])
+
+    def test_plug_lrouter_port_patch_attachment(self):
+        tenant_id = 'pippo'
+        lswitch = nvplib.create_lswitch(self.fake_cluster,
+                                        tenant_id, 'fake-switch')
+        lport = nvplib.create_lport(self.fake_cluster, lswitch['uuid'],
+                                    tenant_id, 'xyz',
+                                    'name', 'device_id', True)
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        tenant_id,
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        result = nvplib.plug_router_port_attachment(
+            self.fake_cluster, lrouter['uuid'],
+            lrouter_port['uuid'],
+            lport['uuid'], 'PatchAttachment')
+        self.assertEqual(lport['uuid'],
+                         result['LogicalPortAttachment']['peer_port_uuid'])
+
+    def test_plug_lrouter_port_l3_gw_attachment(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        result = nvplib.plug_router_port_attachment(
+            self.fake_cluster, lrouter['uuid'],
+            lrouter_port['uuid'],
+            'gw_att', 'L3GatewayAttachment')
+        self.assertEqual(
+            'gw_att',
+            result['LogicalPortAttachment']['l3_gateway_service_uuid'])
+
+    def test_plug_lrouter_port_l3_gw_attachment_with_vlan(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        result = nvplib.plug_router_port_attachment(
+            self.fake_cluster, lrouter['uuid'],
+            lrouter_port['uuid'],
+            'gw_att', 'L3GatewayAttachment', 123)
+        self.assertEqual(
+            'gw_att',
+            result['LogicalPortAttachment']['l3_gateway_service_uuid'])
+        self.assertEqual(
+            '123',
+            result['LogicalPortAttachment']['vlan_id'])
+
+    def test_plug_lrouter_port_invalid_attachment_type_raises(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        lrouter_port = nvplib.create_router_lport(
+            self.fake_cluster, lrouter['uuid'], 'pippo', 'quantum_port_id',
+            'name', True, ['192.168.0.1'])
+        self.assertRaises(Exception,
+                          nvplib.plug_router_port_attachment,
+                          self.fake_cluster, lrouter['uuid'],
+                          lrouter_port['uuid'], 'gw_att', 'BadType')
+
+    def _test_create_router_snat_rule(self, version):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        with mock.patch.object(self.fake_cluster.api_client,
+                               'get_nvp_version',
+                               new=lambda: version):
+            nvplib.create_lrouter_snat_rule(
+                self.fake_cluster, lrouter['uuid'],
+                '10.0.0.2', '10.0.0.2', order=200,
+                match_criteria={'source_ip_addresses': '192.168.0.24'})
+            rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+            self.assertEqual(len(rules), 1)
+
+    def test_create_router_snat_rule_v3(self):
+        self._test_create_router_snat_rule('3.0')
+
+    def test_create_router_snat_rule_v2(self):
+        self._test_create_router_snat_rule('2.0')
+
+    def _test_create_router_dnat_rule(self, version, dest_port=None):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        with mock.patch.object(self.fake_cluster.api_client,
+                               'get_nvp_version',
+                               return_value=version):
+            nvplib.create_lrouter_dnat_rule(
+                self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200,
+                dest_port=dest_port,
+                match_criteria={'destination_ip_addresses': '10.0.0.3'})
+            rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+            self.assertEqual(len(rules), 1)
+
+    def test_create_router_dnat_rule_v3(self):
+        self._test_create_router_dnat_rule('3.0')
+
+    def test_create_router_dnat_rule_v2(self):
+        self._test_create_router_dnat_rule('2.0')
+
+    def test_create_router_dnat_rule_v2_with_destination_port(self):
+        self._test_create_router_dnat_rule('2.0', 8080)
+
+    def test_create_router_dnat_rule_v3_with_destination_port(self):
+        self._test_create_router_dnat_rule('3.0', 8080)
+
+    def test_create_router_snat_rule_invalid_match_keys_raises(self):
+        # In this case the version does not make a difference
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+
+        with mock.patch.object(self.fake_cluster.api_client,
+                               'get_nvp_version',
+                               new=lambda: '2.0'):
+            self.assertRaises(Exception,
+                              nvplib.create_lrouter_snat_rule,
+                              self.fake_cluster, lrouter['uuid'],
+                              '10.0.0.2', '10.0.0.2', order=200,
+                              match_criteria={'foo': 'bar'})
+
+    def _test_create_router_nosnat_rule(self, version, expected=1):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        with mock.patch.object(self.fake_cluster.api_client,
+                               'get_nvp_version',
+                               new=lambda: version):
+            nvplib.create_lrouter_nosnat_rule(
+                self.fake_cluster, lrouter['uuid'],
+                order=100,
+                match_criteria={'destination_ip_addresses': '192.168.0.0/24'})
+            rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+            # NoSNAT rules do not exist in V2
+            self.assertEqual(len(rules), expected)
+
+    def test_create_router_nosnat_rule_v2(self):
+        self._test_create_router_nosnat_rule('2.0', expected=0)
+
+    def test_create_router_nosnat_rule_v3(self):
+        self._test_create_router_nosnat_rule('3.0')
+
+    def _prepare_nat_rules_for_delete_tests(self):
+        lrouter = nvplib.create_lrouter(self.fake_cluster,
+                                        'pippo',
+                                        'fake-lrouter',
+                                        '10.0.0.1')
+        # v2 or v3 makes no difference for this test
+        with mock.patch.object(self.fake_cluster.api_client,
+                               'get_nvp_version',
+                               new=lambda: '2.0'):
+            nvplib.create_lrouter_snat_rule(
+                self.fake_cluster, lrouter['uuid'],
+                '10.0.0.2', '10.0.0.2', order=220,
+                match_criteria={'source_ip_addresses': '192.168.0.0/24'})
+            nvplib.create_lrouter_snat_rule(
+                self.fake_cluster, lrouter['uuid'],
+                '10.0.0.3', '10.0.0.3', order=200,
+                match_criteria={'source_ip_addresses': '192.168.0.2/32'})
+            nvplib.create_lrouter_dnat_rule(
+                self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200,
+                match_criteria={'destination_ip_addresses': '10.0.0.3'})
+        return lrouter
+
+    def test_delete_router_nat_rules_by_match_on_destination_ip(self):
+        lrouter = self._prepare_nat_rules_for_delete_tests()
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 3)
+        nvplib.delete_nat_rules_by_match(
+            self.fake_cluster, lrouter['uuid'], 'DestinationNatRule', 1, 1,
+            destination_ip_addresses='10.0.0.3')
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 2)
+
+    def test_delete_router_nat_rules_by_match_on_source_ip(self):
+        lrouter = self._prepare_nat_rules_for_delete_tests()
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 3)
+        nvplib.delete_nat_rules_by_match(
+            self.fake_cluster, lrouter['uuid'], 'SourceNatRule', 1, 1,
+            source_ip_addresses='192.168.0.2/32')
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 2)
+
+    def test_delete_router_nat_rules_by_match_no_match_expected(self):
+        lrouter = self._prepare_nat_rules_for_delete_tests()
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 3)
+        nvplib.delete_nat_rules_by_match(
+            self.fake_cluster, lrouter['uuid'], 'SomeWeirdType', 0)
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 3)
+        nvplib.delete_nat_rules_by_match(
+            self.fake_cluster, lrouter['uuid'], 'DestinationNatRule', 0,
+            destination_ip_addresses='99.99.99.99')
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 3)
+
+    def test_delete_router_nat_rules_by_match_no_match_raises(self):
+        lrouter = self._prepare_nat_rules_for_delete_tests()
+        rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid'])
+        self.assertEqual(len(rules), 3)
+        self.assertRaises(
+            nvp_exc.NvpNatRuleMismatch,
+            nvplib.delete_nat_rules_by_match,
+            self.fake_cluster, lrouter['uuid'],
+            'SomeWeirdType', 1, 1)
+
+
+class TestNvplibSecurityProfile(NvplibTestCase):
+
+    def test_create_and_get_security_profile(self):
+        sec_prof = nvplib.create_security_profile(self.fake_cluster,
+                                                  'pippo', {'name': 'test'})
+        sec_prof_res = nvplib.do_request(
+            nvplib.HTTP_GET,
+            nvplib._build_uri_path('security-profile',
+                                   resource_id=sec_prof['uuid']),
+            cluster=self.fake_cluster)
+        self.assertEqual(sec_prof['uuid'], sec_prof_res['uuid'])
+        # Check for builtin rules
+        self.assertEqual(len(sec_prof_res['logical_port_egress_rules']), 1)
+        self.assertEqual(len(sec_prof_res['logical_port_ingress_rules']), 2)
+
+    def test_create_and_get_default_security_profile(self):
+        sec_prof = nvplib.create_security_profile(self.fake_cluster,
+                                                  'pippo',
+                                                  {'name': 'default'})
+        sec_prof_res = nvplib.do_request(
+            nvplib.HTTP_GET,
+            nvplib._build_uri_path('security-profile',
+                                   resource_id=sec_prof['uuid']),
+            cluster=self.fake_cluster)
+        self.assertEqual(sec_prof['uuid'], sec_prof_res['uuid'])
+        # Check for builtin rules
+        self.assertEqual(len(sec_prof_res['logical_port_egress_rules']), 3)
+        self.assertEqual(len(sec_prof_res['logical_port_ingress_rules']), 2)
+
+    def test_update_security_profile_rules(self):
+        sec_prof = nvplib.create_security_profile(self.fake_cluster,
+                                                  'pippo', {'name': 'test'})
+        ingress_rule = {'ethertype': 'IPv4'}
+        egress_rule = {'ethertype': 'IPv4', 'profile_uuid': 'xyz'}
+        new_rules = {'logical_port_egress_rules': [egress_rule],
+                     'logical_port_ingress_rules': [ingress_rule]}
+        nvplib.update_security_group_rules(self.fake_cluster,
+                                           sec_prof['uuid'],
+                                           new_rules)
+        sec_prof_res = nvplib.do_request(
+            nvplib.HTTP_GET,
+            nvplib._build_uri_path('security-profile',
+                                   resource_id=sec_prof['uuid']),
+            cluster=self.fake_cluster)
+        self.assertEqual(sec_prof['uuid'], sec_prof_res['uuid'])
+        # Check for builtin rules
+        self.assertEqual(len(sec_prof_res['logical_port_egress_rules']), 2)
+        self.assertIn(egress_rule,
+                      sec_prof_res['logical_port_egress_rules'])
+        self.assertEqual(len(sec_prof_res['logical_port_ingress_rules']), 1)
+        self.assertIn(ingress_rule,
+                      sec_prof_res['logical_port_ingress_rules'])
+
+    def test_update_security_profile_rules_noingress(self):
+        sec_prof = nvplib.create_security_profile(self.fake_cluster,
+                                                  'pippo', {'name': 'test'})
+        hidden_ingress_rule = {'ethertype': 'IPv4',
+                               'ip_prefix': '127.0.0.1/32'}
+        egress_rule = {'ethertype': 'IPv4', 'profile_uuid': 'xyz'}
+        new_rules = {'logical_port_egress_rules': [egress_rule],
+                     'logical_port_ingress_rules': []}
+        nvplib.update_security_group_rules(self.fake_cluster,
+                                           sec_prof['uuid'],
+                                           new_rules)
+        sec_prof_res = nvplib.do_request(
+            nvplib.HTTP_GET,
+            nvplib._build_uri_path('security-profile',
+                                   resource_id=sec_prof['uuid']),
+            cluster=self.fake_cluster)
+        self.assertEqual(sec_prof['uuid'], sec_prof_res['uuid'])
+        # Check for builtin rules
+        self.assertEqual(len(sec_prof_res['logical_port_egress_rules']), 2)
+        self.assertIn(egress_rule,
+                      sec_prof_res['logical_port_egress_rules'])
+        self.assertEqual(len(sec_prof_res['logical_port_ingress_rules']), 1)
+        self.assertIn(hidden_ingress_rule,
+                      sec_prof_res['logical_port_ingress_rules'])
+
+    def test_update_non_existing_securityprofile_raises(self):
+        self.assertRaises(exceptions.QuantumException,
+                          nvplib.update_security_group_rules,
+                          self.fake_cluster, 'whatever',
+                          {'logical_port_egress_rules': [],
+                           'logical_port_ingress_rules': []})
+
+    def test_delete_security_profile(self):
+        sec_prof = nvplib.create_security_profile(self.fake_cluster,
+                                                  'pippo', {'name': 'test'})
+        nvplib.delete_security_profile(self.fake_cluster, sec_prof['uuid'])
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.do_request,
+                          nvplib.HTTP_GET,
+                          nvplib._build_uri_path(
+                              'security-profile',
+                              resource_id=sec_prof['uuid']),
+                          cluster=self.fake_cluster)
+
+    def test_delete_non_existing_securityprofile_raises(self):
+        self.assertRaises(exceptions.QuantumException,
+                          nvplib.delete_security_profile,
+                          self.fake_cluster, 'whatever')
+
+
+class TestNvplibLQueue(NvplibTestCase):
+
+    def test_create_and_get_lqueue(self):
+        queue_id = nvplib.create_lqueue(self.fake_cluster,
+                                        {'display_name': 'fake_queue',
+                                         'min_bandwidth_rate': 0,
+                                         'max_bandwidth_rate': 256,
+                                         'dscp': 0,
+                                         'qos_marking': False})
+        queue_res = nvplib.do_request(
+            nvplib.HTTP_GET,
+            nvplib._build_uri_path('lqueue', resource_id=queue_id),
+            cluster=self.fake_cluster)
+        self.assertEqual(queue_id, queue_res['uuid'])
+        self.assertEqual('fake_queue', queue_res['display_name'])
+
+    def test_create_lqueue_nvp_error_raises(self):
+        def raise_nvp_exc(*args, **kwargs):
+            raise NvpApiClient.NvpApiException()
+
+        with mock.patch.object(nvplib, 'do_request', new=raise_nvp_exc):
+            self.assertRaises(
+                exceptions.QuantumException, nvplib.create_lqueue,
+                self.fake_cluster, {})
+
+    def test_delete_lqueue(self):
+        queue_id = nvplib.create_lqueue(self.fake_cluster,
+                                        {'display_name': 'fake_queue',
+                                         'min_bandwidth_rate': 0,
+                                         'max_bandwidth_rate': 256,
+                                         'dscp': 0,
+                                         'qos_marking': False})
+        nvplib.delete_lqueue(self.fake_cluster, queue_id)
+        self.assertRaises(exceptions.NotFound,
+                          nvplib.do_request,
+                          nvplib.HTTP_GET,
+                          nvplib._build_uri_path(
+                              'lqueue', resource_id=queue_id),
+                          cluster=self.fake_cluster)
+
+    def test_delete_non_existing_lqueue_raises(self):
+        self.assertRaises(exceptions.QuantumException,
+                          nvplib.delete_lqueue,
+                          self.fake_cluster, 'whatever')
+
+
+class TestNvplibLogicalPorts(NvplibTestCase):
+
+    def _create_switch_and_port(self, tenant_id='pippo',
+                                quantum_port_id='whatever'):
+        lswitch = nvplib.create_lswitch(self.fake_cluster,
+                                        tenant_id, 'fake-switch')
         lport = nvplib.create_lport(self.fake_cluster, lswitch['uuid'],
                                     tenant_id, quantum_port_id,
                                     'name', 'device_id', True)
+        return lswitch, lport
+
+    def test_create_and_get_port(self):
+        lswitch, lport = self._create_switch_and_port()
+        lport_res = nvplib.get_port(self.fake_cluster,
+                                    lswitch['uuid'], lport['uuid'])
+        self.assertEqual(lport['uuid'], lport_res['uuid'])
+        # Try again with relation
+        lport_res = nvplib.get_port(self.fake_cluster,
+                                    lswitch['uuid'], lport['uuid'],
+                                    relations='LogicalPortStatus')
+        self.assertEqual(lport['uuid'], lport_res['uuid'])
+
+    def test_plug_interface(self):
+        lswitch, lport = self._create_switch_and_port()
+        nvplib.plug_interface(self.fake_cluster, lswitch['uuid'],
+                              lport['uuid'], 'VifAttachment', 'fake')
+        lport_res = nvplib.get_port(self.fake_cluster,
+                                    lswitch['uuid'], lport['uuid'])
+        self.assertEqual(lport['uuid'], lport_res['uuid'])
+
+    def test_get_port_by_tag(self):
+        lswitch, lport = self._create_switch_and_port()
         lport2 = nvplib.get_port_by_quantum_tag(self.fake_cluster,
                                                 lswitch['uuid'],
-                                                quantum_port_id)
+                                                'whatever')
         self.assertIsNotNone(lport2)
         self.assertEqual(lport['uuid'], lport2['uuid'])
 
@@ -289,3 +1075,92 @@ class TestNvpLibLogicalPorts(NvplibTestCase):
                                                lswitch['uuid'],
                                                quantum_port_id)
         self.assertIsNone(lport)
+
+    def test_get_port_status(self):
+        lswitch, lport = self._create_switch_and_port()
+        status = nvplib.get_port_status(self.fake_cluster,
+                                        lswitch['uuid'],
+                                        lport['uuid'])
+        self.assertEqual(constants.PORT_STATUS_ACTIVE, status)
+
+    def test_get_port_status_non_existent_raises(self):
+        self.assertRaises(exceptions.PortNotFound,
+                          nvplib.get_port_status,
+                          self.fake_cluster,
+                          'boo', 'boo')
+
+    def test_update_port(self):
+        lswitch, lport = self._create_switch_and_port()
+        nvplib.update_port(
+            self.fake_cluster, lswitch['uuid'], lport['uuid'],
+            'quantum_port_id', 'pippo2', 'new_name', 'device_id', False)
+        lport_res = nvplib.get_port(self.fake_cluster,
+                                    lswitch['uuid'], lport['uuid'])
+        self.assertEqual(lport['uuid'], lport_res['uuid'])
+        self.assertEqual('new_name', lport_res['display_name'])
+        self.assertEqual('False', lport_res['admin_status_enabled'])
+        port_tags = self._build_tag_dict(lport_res['tags'])
+        self.assertIn('os_tid', port_tags)
+        self.assertIn('q_port_id', port_tags)
+        self.assertIn('vm_id', port_tags)
+
+    def test_update_non_existent_port_raises(self):
+        self.assertRaises(exceptions.PortNotFound,
+                          nvplib.update_port, self.fake_cluster,
+                          'boo', 'boo', 'boo', 'boo', 'boo', 'boo', False)
+
+    def test_delete_port(self):
+        lswitch, lport = self._create_switch_and_port()
+        nvplib.delete_port(self.fake_cluster,
+                           lswitch['uuid'], lport['uuid'])
+        self.assertRaises(exceptions.PortNotFound,
+                          nvplib.get_port, self.fake_cluster,
+                          lswitch['uuid'], lport['uuid'])
+
+    def test_delete_non_existent_port_raises(self):
+        lswitch = self._create_switch_and_port()[0]
+        self.assertRaises(exceptions.PortNotFound,
+                          nvplib.delete_port, self.fake_cluster,
+                          lswitch['uuid'], 'bad_port_uuid')
+
+    def test_query_lswitch_ports(self):
+        lswitch, lport = self._create_switch_and_port()
+        switch_port_uuids = [
+            nvplib.create_lport(
+                self.fake_cluster, lswitch['uuid'], 'pippo', 'qportid-%s' % k,
+                'port-%s' % k, 'deviceid-%s' % k, True)['uuid']
+            for k in range(0, 2)]
+        switch_port_uuids.append(lport['uuid'])
+        ports = nvplib.query_lswitch_lports(self.fake_cluster, lswitch['uuid'])
+        self.assertEqual(len(ports), 3)
+        for res_port in ports:
+            self.assertIn(res_port['uuid'], switch_port_uuids)
+
+
+class TestNvplibClusterVersion(NvplibTestCase):
+
+    def test_get_cluster_version(self):
+
+        def fakedorequest(*args, **kwargs):
+            uri = args[1]
+            if 'node/xyz' in uri:
+                return {'version': '3.0.9999'}
+            elif 'node' in uri:
+                return {'result_count': 1,
+                        'results': [{'uuid': 'xyz'}]}
+
+        # mock do_request
+        with mock.patch.object(nvplib, 'do_request', new=fakedorequest):
+            version = nvplib.get_cluster_version('whatever')
+            self.assertEqual(version, '3.0')
+
+    def test_get_cluster_version_no_nodes(self):
+        def fakedorequest(*args, **kwargs):
+            uri = args[1]
+            if 'node' in uri:
+                return {'result_count': 0}
+
+        # mock do_request
+        with mock.patch.object(nvplib, 'do_request', new=fakedorequest):
+            version = nvplib.get_cluster_version('whatever')
+            self.assertIsNone(version)