]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Make MidoNet plugin code more testable
authorRyu Ishimoto <ryu@midokura.com>
Mon, 25 Feb 2013 16:34:46 +0000 (01:34 +0900)
committerTomoe Sugihara <tomoe@midokura.com>
Mon, 27 May 2013 17:26:09 +0000 (02:26 +0900)
Refactor plugin.py and midonet_lib.py to improve unit testing of
the MidoNet plugin.  Make all the tests pass in the plugin except
those that test features that are not currently supported by the
plugin, which are IPv6 and multiple subnets per network.
Consolidate the mocking code in mock_lib.py to be shared by
plugin.py and midonet_lib.py.

Change-Id: Idcdfc3d90d5ec9e0be29bb6833d336af596fe363
Fixes: bug #1133215
Signed-off-by: Ryu Ishimoto <ryu@midokura.com>
Signed-off-by: Tomoe Sugihara <tomoe@midokura.com>
quantum/plugins/midonet/midonet_lib.py
quantum/plugins/midonet/plugin.py
quantum/tests/unit/midonet/etc/midonet.ini.test [new file with mode: 0644]
quantum/tests/unit/midonet/mock_lib.py [new file with mode: 0644]
quantum/tests/unit/midonet/test_midonet_lib.py
quantum/tests/unit/midonet/test_midonet_plugin.py

index fbd7a6bdf0dc4e1de27754d1c65f911e91ff74f1..9ad6b94ccf24dac35cacf9940a44af52b505ea9d 100644 (file)
 # @author: Ryu Ishimoto, Midokura Japan KK
 
 
+from webob import exc as w_exc
+
+from quantum.common import exceptions as q_exc
 from quantum.openstack.common import log as logging
 
 
 LOG = logging.getLogger(__name__)
 
 PREFIX = 'OS_SG_'
-SUFFIX_IN = '_IN'
-SUFFIX_OUT = '_OUT'
+NAME_IDENTIFIABLE_PREFIX_LEN = len(PREFIX) + 36  # 36 = length of uuid
+OS_FLOATING_IP_RULE_KEY = 'OS_FLOATING_IP'
 OS_ROUTER_IN_CHAIN_NAME_FORMAT = 'OS_ROUTER_IN_%s'
 OS_ROUTER_OUT_CHAIN_NAME_FORMAT = 'OS_ROUTER_OUT_%s'
-NAME_IDENTIFIABLE_PREFIX_LEN = len(PREFIX) + 36  # 36 = length of uuid
+OS_SG_KEY = 'os_sg_rule_id'
+OS_TENANT_ROUTER_RULE_KEY = 'OS_TENANT_ROUTER_RULE'
+SNAT_RULE = 'SNAT'
+SNAT_RULE_PROPERTY = {OS_TENANT_ROUTER_RULE_KEY: SNAT_RULE}
+SUFFIX_IN = '_IN'
+SUFFIX_OUT = '_OUT'
 
 
 def sg_label(sg_id, sg_name):
     """Construct the security group ID used as chain identifier in MidoNet."""
     return PREFIX + str(sg_id) + '_' + sg_name
 
+
+def sg_rule_properties(os_sg_rule_id):
+    return {OS_SG_KEY: str(os_sg_rule_id)}
+
 port_group_name = sg_label
 
 
@@ -48,47 +60,527 @@ def chain_names(sg_id, sg_name):
     return {'in': in_chain_name, 'out': out_chain_name}
 
 
-class ChainManager:
+def router_chain_names(router_id):
+    in_name = OS_ROUTER_IN_CHAIN_NAME_FORMAT % router_id
+    out_name = OS_ROUTER_OUT_CHAIN_NAME_FORMAT % router_id
+    return {'in': in_name, 'out': out_name}
+
+
+def handle_api_error(fn):
+    def wrapped(*args, **kwargs):
+        try:
+            return fn(*args, **kwargs)
+        except w_exc.HTTPException as ex:
+            raise MidonetApiException(msg=ex)
+    return wrapped
+
+
+class MidonetResourceNotFound(q_exc.NotFound):
+    message = _('MidoNet %(resource_type)s %(id)s could not be found')
+
+
+class MidonetApiException(q_exc.QuantumException):
+    message = _("MidoNet API error: %(msg)s")
+
+
+class MidoClient:
 
     def __init__(self, mido_api):
         self.mido_api = mido_api
 
-    def create_for_sg(self, tenant_id, sg_id, sg_name):
-        """Create a new chain for security group.
+    @handle_api_error
+    def create_bridge(self, tenant_id, name):
+        """Create a new bridge
 
-        Creating a security group creates a pair of chains in MidoNet, one for
-        inbound and the other for outbound.
+        :param tenant_id: id of tenant creating the bridge
+        :param name: name of the bridge
+        :returns: newly created bridge
         """
-        LOG.debug(_("ChainManager.create_for_sg called: "
-                    "tenant_id=%(tenant_id)s sg_id=%(sg_id)s "
-                    "sg_name=%(sg_name)s "),
-                  {'tenant_id': tenant_id, 'sg_id': sg_id, 'sg_name': sg_name})
+        LOG.debug(_("MidoClient.create_bridge called: "
+                    "tenant_id=%(tenant_id)s, name=%(name)s"),
+                  {'tenant_id': tenant_id, 'name': name})
+        return self.mido_api.add_bridge().name(name).tenant_id(
+            tenant_id).create()
 
-        cnames = chain_names(sg_id, sg_name)
-        self.mido_api.add_chain().tenant_id(tenant_id).name(
-            cnames['in']).create()
-        self.mido_api.add_chain().tenant_id(tenant_id).name(
-            cnames['out']).create()
+    @handle_api_error
+    def delete_bridge(self, id):
+        """Delete a bridge
 
-    def delete_for_sg(self, tenant_id, sg_id, sg_name):
-        """Delete a chain mapped to a security group.
+        :param id: id of the bridge
+        """
+        LOG.debug(_("MidoClient.delete_bridge called: id=%(id)s"), {'id': id})
+        return self.mido_api.delete_bridge(id)
 
-        Delete a SG means deleting all the chains (inbound and outbound)
-        associated with the SG in MidoNet.
+    @handle_api_error
+    def get_bridge(self, id):
+        """Get a bridge
+
+        :param id: id of the bridge
+        :returns: requested bridge. None if bridge does not exist.
         """
-        LOG.debug(_("ChainManager.delete_for_sg called: "
-                    "tenant_id=%(tenant_id)s sg_id=%(sg_id)s "
-                    "sg_name=%(sg_name)s "),
-                  {'tenant_id': tenant_id, 'sg_id': sg_id, 'sg_name': sg_name})
+        LOG.debug(_("MidoClient.get_bridge called: id=%s"), id)
+        try:
+            return self.mido_api.get_bridge(id)
+        except w_exc.HTTPNotFound:
+            raise MidonetResourceNotFound(resource_type='Bridge', id=id)
+
+    @handle_api_error
+    def update_bridge(self, id, name):
+        """Update a bridge of the given id with the new name
+
+        :param id: id of the bridge
+        :param name: name of the bridge to set to
+        :returns: bridge object
+        """
+        LOG.debug(_("MidoClient.update_bridge called: "
+                    "id=%(id)s, name=%(name)s"), {'id': id, 'name': name})
+        try:
+            return self.mido_api.get_bridge(id).name(name).update()
+        except w_exc.HTTPNotFound:
+            raise MidonetResourceNotFound(resource_type='Bridge', id=id)
+
+    @handle_api_error
+    def create_dhcp(self, bridge, gateway_ip, net_addr, net_len):
+        """Create a new DHCP entry
+
+        :param bridge: bridge object to add dhcp to
+        :param gateway_ip: IP address of gateway
+        :param net_addr: network IP address
+        :param net_len: network IP address length
+        :returns: newly created dhcp
+        """
+        LOG.debug(_("MidoClient.create_dhcp called: bridge=%s(bridge)s, "
+                    "net_addr=%(net_addr)s, net_len=%(net_len)s, "
+                    "gateway_ip=%(gateway_ip)s"),
+                  {'bridge': bridge, 'net_addr': net_addr, 'net_len': net_len,
+                   'gateway_ip': gateway_ip})
+        return bridge.add_dhcp_subnet().default_gateway(
+            gateway_ip).subnet_prefix(net_addr).subnet_length(
+                net_len).create()
+
+    @handle_api_error
+    def create_dhcp_hosts(self, bridge, ip, mac):
+        """Create DHCP host entries
+
+        :param bridge: bridge of the DHCP
+        :param ip: IP address
+        :param mac: MAC address
+        """
+        LOG.debug(_("MidoClient.create_dhcp_hosts called: bridge=%s(bridge), "
+                    "ip=%(ip)s, mac=%(mac)s"), {'bridge': bridge, 'ip': ip,
+                                                'mac': mac})
+        dhcp_subnets = bridge.get_dhcp_subnets()
+        if dhcp_subnets:
+            # Add the host to the first subnet as we currently support one
+            # subnet per network.
+            dhcp_subnets[0].add_dhcp_host().ip_addr(ip).mac_addr(mac).create()
+
+    @handle_api_error
+    def delete_dhcp_hosts(self, bridge_id, ip, mac):
+        """Delete DHCP host entries
+
+        :param bridge_id: id of the bridge of the DHCP
+        :param ip: IP address
+        :param mac: MAC address
+        """
+        LOG.debug(_("MidoClient.delete_dhcp_hosts called: "
+                    "bridge_id=%s(bridge_id), ip=%(ip)s, mac=%(mac)s"),
+                  {'bridge_id': bridge_id, 'ip': ip, 'mac': mac})
+        bridge = self.get_bridge(bridge_id)
+        dhcp_subnets = bridge.get_dhcp_subnets()
+        if dhcp_subnets:
+            for dh in dhcp_subnets[0].get_dhcp_hosts():
+                if dh.get_mac_addr() == mac and dh.get_ip_addr() == ip:
+                    dh.delete()
+
+    @handle_api_error
+    def delete_dhcp(self, bridge):
+        """Delete a DHCP entry
+
+        :param bridge: bridge to remove DHCP from
+        """
+        LOG.debug(_("MidoClient.delete_dhcp called: bridge=%s(bridge), "),
+                  {'bridge': bridge})
+        dhcp = bridge.get_dhcp_subnets()
+        if not dhcp:
+            raise MidonetApiException(msg="Tried to delete non-existent DHCP")
+        dhcp[0].delete()
+
+    @handle_api_error
+    def delete_port(self, id):
+        """Delete a port
+
+        :param id: id of the port
+        """
+        LOG.debug(_("MidoClient.delete_port called: id=%(id)s"), {'id': id})
+        self.mido_api.delete_port(id)
 
-        cnames = chain_names(sg_id, sg_name)
-        chains = self.mido_api.get_chains({'tenant_id': tenant_id})
-        for c in chains:
-            if c.get_name() == cnames['in'] or c.get_name() == cnames['out']:
-                LOG.debug(_('ChainManager.delete_for_sg: deleting chain=%r'),
-                          c)
-                c.delete()
+    @handle_api_error
+    def get_port(self, id):
+        """Get a port
+
+        :param id: id of the port
+        :returns: requested port. None if it does not exist
+        """
+        LOG.debug(_("MidoClient.get_port called: id=%(id)s"), {'id': id})
+        try:
+            return self.mido_api.get_port(id)
+        except w_exc.HTTPNotFound:
+            raise MidonetResourceNotFound(resource_type='Port', id=id)
+
+    @handle_api_error
+    def create_exterior_bridge_port(self, bridge):
+        """Create a new exterior bridge port
+
+        :param bridge: bridge object to add port to
+        :returns: newly created port
+        """
+        LOG.debug(_("MidoClient.create_exterior_bridge_port called: "
+                    "bridge=%(bridge)s"), {'bridge': bridge})
+        return bridge.add_exterior_port().create()
+
+    @handle_api_error
+    def create_interior_bridge_port(self, bridge):
+        """Create a new interior bridge port
+
+        :param bridge: bridge object to add port to
+        :returns: newly created port
+        """
+        LOG.debug(_("MidoClient.create_interior_bridge_port called: "
+                    "bridge=%(bridge)s"), {'bridge': bridge})
+        return bridge.add_interior_port().create()
+
+    @handle_api_error
+    def create_router(self, tenant_id, name):
+        """Create a new router
+
+        :param tenant_id: id of tenant creating the router
+        :param name: name of the router
+        :returns: newly created router
+        """
+        LOG.debug(_("MidoClient.create_router called: "
+                    "tenant_id=%(tenant_id)s, name=%(name)s"),
+                  {'tenant_id': tenant_id, 'name': name})
+        return self.mido_api.add_router().name(name).tenant_id(
+            tenant_id).create()
+
+    @handle_api_error
+    def create_tenant_router(self, tenant_id, name, metadata_router):
+        """Create a new tenant router
+
+        :param tenant_id: id of tenant creating the router
+        :param name: name of the router
+        :param metadata_router: metadata router
+        :returns: newly created router
+        """
+        LOG.debug(_("MidoClient.create_tenant_router called: "
+                    "tenant_id=%(tenant_id)s, name=%(name)s"
+                    "metadata_router=%(metadata_router)s"),
+                  {'tenant_id': tenant_id, 'name': name,
+                   'metadata_router': metadata_router})
+        router = self.create_router(tenant_id, name)
+        self.link_router_to_metadata_router(router, metadata_router)
+        return router
+
+    @handle_api_error
+    def delete_tenant_router(self, id, metadata_router):
+        """Delete a tenant router
+
+        :param id: id of router
+        :param metadata_router: metadata router
+        """
+        LOG.debug(_("MidoClient.delete_tenant_router called: "
+                    "id=%(id)s, metadata_router=%(metadata_router)s"),
+                  {'id': id, 'metadata_router': metadata_router})
+        self.unlink_router_from_metadata_router(id, metadata_router)
+        self.destroy_router_chains(id)
+
+        # delete the router
+        self.delete_router(id)
+
+    @handle_api_error
+    def delete_router(self, id):
+        """Delete a router
+
+        :param id: id of the router
+        """
+        LOG.debug(_("MidoClient.delete_router called: id=%(id)s"), {'id': id})
+        return self.mido_api.delete_router(id)
+
+    @handle_api_error
+    def get_router(self, id):
+        """Get a router with the given id
 
+        :param id: id of the router
+        :returns: requested router object.  None if it does not exist.
+        """
+        LOG.debug(_("MidoClient.get_router called: id=%(id)s"), {'id': id})
+        try:
+            return self.mido_api.get_router(id)
+        except w_exc.HTTPNotFound:
+            raise MidonetResourceNotFound(resource_type='Router', id=id)
+
+    @handle_api_error
+    def update_router(self, id, name):
+        """Update a router of the given id with the new name
+
+        :param id: id of the router
+        :param name: name of the router to set to
+        :returns: router object
+        """
+        LOG.debug(_("MidoClient.update_router called: "
+                    "id=%(id)s, name=%(name)s"), {'id': id, 'name': name})
+        try:
+            return self.mido_api.get_router(id).name(name).update()
+        except w_exc.HTTPNotFound:
+            raise MidonetResourceNotFound(resource_type='Router', id=id)
+
+    @handle_api_error
+    def link_bridge_port_to_router(self, port_id, router_id, gateway_ip,
+                                   net_addr, net_len, metadata_router):
+        """Link a tenant bridge port to the router
+
+        :param port_id: port ID
+        :param router_id: router id to link to
+        :param gateway_ip: IP address of gateway
+        :param net_addr: network IP address
+        :param net_len: network IP address length
+        :param metadata_router: metadata router instance
+        """
+        LOG.debug(_("MidoClient.link_bridge_port_to_router called: "
+                    "port_id=%(port_id)s, router_id=%(router_id)s, "
+                    "gateway_ip=%(gateway_ip)s net_addr=%(net_addr)s, "
+                    "net_len=%(net_len)s, "
+                    "metadata_router=%(metadata_router)s"),
+                  {'port_id': port_id, 'router_id': router_id,
+                   'gateway_ip': gateway_ip, 'net_addr': net_addr,
+                   'net_len': net_len, 'metadata_router': metadata_router})
+        router = self.get_router(router_id)
+
+        # create an interior port on the router
+        in_port = router.add_interior_port()
+        router_port = in_port.port_address(gateway_ip).network_address(
+            net_addr).network_length(net_len).create()
+
+        br_port = self.get_port(port_id)
+        router_port.link(br_port.get_id())
+
+        # add a route for the subnet in the provider router
+        router.add_route().type('Normal').src_network_addr(
+            '0.0.0.0').src_network_length(0).dst_network_addr(
+                net_addr).dst_network_length(net_len).weight(
+                    100).next_hop_port(router_port.get_id()).create()
+
+        # add a route for the subnet in metadata router; forward
+        # packets destined to the subnet to the tenant router
+        for pp in metadata_router.get_peer_ports():
+            if pp.get_device_id() == router.get_id():
+                mdr_port_id = pp.get_peer_id()
+                break
+        else:
+            raise Exception(
+                _("Couldn't find a md router port for the router=%r"), router)
+
+        metadata_router.add_route().type('Normal').src_network_addr(
+            '0.0.0.0').src_network_length(0).dst_network_addr(
+                net_addr).dst_network_length(net_len).weight(
+                    100).next_hop_port(mdr_port_id).create()
+
+    @handle_api_error
+    def unlink_bridge_port_from_router(self, port_id, net_addr, net_len,
+                                       metadata_router):
+        """Unlink a tenant bridge port from the router
+
+        :param bridge_id: bridge ID
+        :param net_addr: network IP address
+        :param net_len: network IP address length
+        :param metadata_router: metadata router instance
+        """
+        LOG.debug(_("MidoClient.unlink_bridge_port_from_router called: "
+                    "port_id=%(port_id)s, net_addr=%(net_addr)s, "
+                    "net_len=%(net_len)s, "
+                    "metadata_router=%(metadata_router)s"),
+                  {'port_id': port_id, 'net_addr': net_addr,
+                   'net_len': net_len, 'metadata_router': metadata_router})
+        port = self.get_port(port_id)
+        port.unlink()
+        self.delete_port(port.get_peer_id())
+        self.delete_port(port.get_id())
+
+        # delete the route for the subnet in the metadata router
+        for r in metadata_router.get_routes():
+            if (r.get_dst_network_addr() == net_addr and
+                r.get_dst_network_length() == net_len):
+                LOG.debug(_('Deleting route=%r ...'), r)
+                self.mido_api.delete_route(r.get_id())
+                break
+
+    @handle_api_error
+    def link_bridge_to_provider_router(self, bridge, provider_router,
+                                       gateway_ip, net_addr, net_len):
+        """Link a tenant bridge to the provider router
+
+        :param bridge: tenant bridge
+        :param provider_router: provider router to link to
+        :param gateway_ip: IP address of gateway
+        :param net_addr: network IP address
+        :param net_len: network IP address length
+        """
+        LOG.debug(_("MidoClient.link_bridge_to_provider_router called: "
+                    "bridge=%(bridge)s, provider_router=%(provider_router)s, "
+                    "gateway_ip=%(gateway_ip)s, net_addr=%(net_addr)s, "
+                    "net_len=%(net_len)s"),
+                  {'bridge': bridge, 'provider_router': provider_router,
+                   'gateway_ip': gateway_ip, 'net_addr': net_addr,
+                   'net_len': net_len})
+        # create an interior port on the provider router
+        in_port = provider_router.add_interior_port()
+        pr_port = in_port.port_address(gateway_ip).network_address(
+            net_addr).network_length(net_len).create()
+
+        # create an interior bridge port, then link it to the router.
+        br_port = bridge.add_interior_port().create()
+        pr_port.link(br_port.get_id())
+
+        # add a route for the subnet in the provider router
+        provider_router.add_route().type('Normal').src_network_addr(
+            '0.0.0.0').src_network_length(0).dst_network_addr(
+                net_addr).dst_network_length(net_len).weight(
+                    100).next_hop_port(pr_port.get_id()).create()
+
+    @handle_api_error
+    def unlink_bridge_from_provider_router(self, bridge, provider_router):
+        """Unlink a tenant bridge from the provider router
+
+        :param bridge: tenant bridge
+        :param provider_router: provider router to link to
+        """
+        LOG.debug(_("MidoClient.unlink_bridge_from_provider_router called: "
+                    "bridge=%(bridge)s, provider_router=%(provider_router)s"),
+                  {'bridge': bridge, 'provider_router': provider_router})
+        # Delete routes and unlink the router and the bridge.
+        routes = provider_router.get_routes()
+
+        bridge_ports_to_delete = [
+            p for p in provider_router.get_peer_ports()
+            if p.get_device_id() == bridge.get_id()]
+
+        for p in bridge.get_peer_ports():
+            if p.get_device_id() == provider_router.get_id():
+                # delete the routes going to the bridge
+                for r in routes:
+                    if r.get_next_hop_port() == p.get_id():
+                        self.mido_api.delete_route(r.get_id())
+                p.unlink()
+                self.mido_api.delete_port(p.get_id())
+
+        # delete bridge port
+        for port in bridge_ports_to_delete:
+            self.mido_api.delete_port(port.get_id())
+
+    @handle_api_error
+    def set_router_external_gateway(self, id, provider_router, snat_ip):
+        """Set router external gateway
+
+        :param ID: ID of the tenant router
+        :param provider_router: provider router
+        :param snat_ip: SNAT IP address
+        """
+        LOG.debug(_("MidoClient.set_router_external_gateway called: "
+                    "id=%(id)s, provider_router=%(provider_router)s, "
+                    "snat_ip=%s(snat_ip)s)"),
+                  {'id': id, 'provider_router': provider_router,
+                   'snat_ip': snat_ip})
+        tenant_router = self.get_router(id)
+
+        # Create a interior port in the provider router
+        in_port = provider_router.add_interior_port()
+        pr_port = in_port.network_address(
+            '169.254.255.0').network_length(30).port_address(
+                '169.254.255.1').create()
+
+        # Create a port in the tenant router
+        tr_port = tenant_router.add_interior_port().network_address(
+            '169.254.255.0').network_length(30).port_address(
+                '169.254.255.2').create()
+
+        # Link them
+        pr_port.link(tr_port.get_id())
+
+        # Add a route for snat_ip to bring it down to tenant
+        provider_router.add_route().type(
+            'Normal').src_network_addr('0.0.0.0').src_network_length(
+                0).dst_network_addr(snat_ip).dst_network_length(
+                    32).weight(100).next_hop_port(
+                        pr_port.get_id()).create()
+
+        # Add default route to uplink in the tenant router
+        tenant_router.add_route().type('Normal').src_network_addr(
+            '0.0.0.0').src_network_length(0).dst_network_addr(
+                '0.0.0.0').dst_network_length(0).weight(
+                    100).next_hop_port(tr_port.get_id()).create()
+
+        # ADD SNAT(masquerade) rules
+        chains = self.get_router_chains(
+            tenant_router.get_tenant_id(), tenant_router.get_id())
+
+        chains['in'].add_rule().nw_dst_address(snat_ip).nw_dst_length(
+            32).type('rev_snat').flow_action('accept').in_ports(
+                [tr_port.get_id()]).properties(
+                    SNAT_RULE_PROPERTY).position(1).create()
+
+        nat_targets = []
+        nat_targets.append(
+            {'addressFrom': snat_ip, 'addressTo': snat_ip,
+             'portFrom': 1, 'portTo': 65535})
+
+        chains['out'].add_rule().type('snat').flow_action(
+            'accept').nat_targets(nat_targets).out_ports(
+                [tr_port.get_id()]).properties(
+                    SNAT_RULE_PROPERTY).position(1).create()
+
+    @handle_api_error
+    def clear_router_external_gateway(self, id):
+        """Clear router external gateway
+
+        :param ID: ID of the tenant router
+        """
+        LOG.debug(_("MidoClient.clear_router_external_gateway called: "
+                    "id=%(id)s"), {'id': id})
+        tenant_router = self.get_router(id)
+
+        # delete the port that is connected to provider router
+        for p in tenant_router.get_ports():
+            if p.get_port_address() == '169.254.255.2':
+                peer_port_id = p.get_peer_id()
+                p.unlink()
+                self.mido_api.delete_port(peer_port_id)
+                self.mido_api.delete_port(p.get_id())
+
+        # delete default route
+        for r in tenant_router.get_routes():
+            if (r.get_dst_network_addr() == '0.0.0.0' and
+                    r.get_dst_network_length() == 0):
+                self.mido_api.delete_route(r.get_id())
+
+        # delete SNAT(masquerade) rules
+        chains = self.get_router_chains(
+            tenant_router.get_tenant_id(),
+            tenant_router.get_id())
+
+        for r in chains['in'].get_rules():
+            if OS_TENANT_ROUTER_RULE_KEY in r.get_properties():
+                if r.get_properties()[
+                    OS_TENANT_ROUTER_RULE_KEY] == SNAT_RULE:
+                    self.mido_api.delete_rule(r.get_id())
+
+        for r in chains['out'].get_rules():
+            if OS_TENANT_ROUTER_RULE_KEY in r.get_properties():
+                if r.get_properties()[
+                    OS_TENANT_ROUTER_RULE_KEY] == SNAT_RULE:
+                    self.mido_api.delete_rule(r.get_id())
+
+    @handle_api_error
     def get_router_chains(self, tenant_id, router_id):
         """Get router chains.
 
@@ -96,41 +588,266 @@ class ChainManager:
         and 'out' respectively, given the tenant_id and the router_id passed
         in in the arguments.
         """
-        LOG.debug(_("ChainManager.get_router_chains called: "
+        LOG.debug(_("MidoClient.get_router_chains called: "
                     "tenant_id=%(tenant_id)s router_id=%(router_id)s"),
                   {'tenant_id': tenant_id, 'router_id': router_id})
 
-        router_chain_names = self._get_router_chain_names(router_id)
+        chain_names = router_chain_names(router_id)
         chains = {}
         for c in self.mido_api.get_chains({'tenant_id': tenant_id}):
-            if c.get_name() == router_chain_names['in']:
+            if c.get_name() == chain_names['in']:
                 chains['in'] = c
-            elif c.get_name() == router_chain_names['out']:
+            elif c.get_name() == chain_names['out']:
                 chains['out'] = c
         return chains
 
-    def create_router_chains(self, tenant_id, router_id):
-        """Create a new chain on a router.
+    @handle_api_error
+    def create_router_chains(self, router):
+        """Create chains for a new router.
 
         Creates chains for the router and returns the same dictionary as
         get_router_chains() returns.
-        """
-        LOG.debug(_("ChainManager.create_router_chains called: "
-                    "tenant_id=%(tenant_id)s router_id=%(router_id)s"),
-                  {'tenant_id': tenant_id, 'router_id': router_id})
 
+        :param router: router to set chains for
+        """
+        LOG.debug(_("MidoClient.create_router_chains called: "
+                    "router=%(router)s"), {'router': router})
         chains = {}
-        router_chain_names = self._get_router_chain_names(router_id)
+        router_id = router.get_id()
+        tenant_id = router.get_tenant_id()
+        chain_names = router_chain_names(router_id)
         chains['in'] = self.mido_api.add_chain().tenant_id(tenant_id).name(
-            router_chain_names['in']).create()
+            chain_names['in']).create()
 
         chains['out'] = self.mido_api.add_chain().tenant_id(tenant_id).name(
-            router_chain_names['out']).create()
+            chain_names['out']).create()
+
+        # set chains to in/out filters
+        router.inbound_filter_id(
+            chains['in'].get_id()).outbound_filter_id(
+                chains['out'].get_id()).update()
         return chains
 
+    @handle_api_error
+    def destroy_router_chains(self, id):
+        """Deletes chains of a router.
+
+        :param id: router ID to delete chains of
+        """
+        LOG.debug(_("MidoClient.destroy_router_chains called: "
+                    "id=%(id)s"), {'id': id})
+        # delete corresponding chains
+        router = self.get_router(id)
+        chains = self.get_router_chains(router.get_tenant_id(), id)
+        self.mido_api.delete_chain(chains['in'].get_id())
+        self.mido_api.delete_chain(chains['out'].get_id())
+
+    @handle_api_error
+    def link_router_to_metadata_router(self, router, metadata_router):
+        """Link a router to the metadata router
+
+        :param router: router to link
+        :param metadata_router: metadata router
+        """
+        LOG.debug(_("MidoClient.link_router_to_metadata_router called: "
+                    "router=%(router)s, metadata_router=%(metadata_router)s"),
+                  {'router': router, 'metadata_router': metadata_router})
+        # link to metadata router
+        in_port = metadata_router.add_interior_port()
+        mdr_port = in_port.network_address('169.254.255.0').network_length(
+            30).port_address('169.254.255.1').create()
+
+        tr_port = router.add_interior_port().network_address(
+            '169.254.255.0').network_length(30).port_address(
+                '169.254.255.2').create()
+        mdr_port.link(tr_port.get_id())
+
+        # forward metadata traffic to metadata router
+        router.add_route().type('Normal').src_network_addr(
+            '0.0.0.0').src_network_length(0).dst_network_addr(
+                '169.254.169.254').dst_network_length(32).weight(
+                    100).next_hop_port(tr_port.get_id()).create()
+
+    @handle_api_error
+    def unlink_router_from_metadata_router(self, id, metadata_router):
+        """Unlink a router from the metadata router
+
+        :param id: ID of router
+        :param metadata_router: metadata router
+        """
+        LOG.debug(_("MidoClient.unlink_router_from_metadata_router called: "
+                    "id=%(id)s, metadata_router=%(metadata_router)s"),
+                  {'id': id, 'metadata_router': metadata_router})
+        # unlink from metadata router and delete the interior ports
+        # that connect metadata router and this router.
+        for pp in metadata_router.get_peer_ports():
+            if pp.get_device_id() == id:
+                mdr_port = self.get_port(pp.get_peer_id())
+                pp.unlink()
+                self.mido_api.delete_port(pp.get_id())
+                self.mido_api.delete_port(mdr_port.get_id())
+
+    @handle_api_error
+    def setup_floating_ip(self, router_id, provider_router, floating_ip,
+                          fixed_ip, identifier):
+        """Setup MidoNet for floating IP
+
+        :param router_id: router_id
+        :param provider_router: provider router
+        :param floating_ip: floating IP address
+        :param fixed_ip: fixed IP address
+        :param identifier: identifier to use to map to MidoNet
+        """
+        LOG.debug(_("MidoClient.setup_floating_ip called: "
+                    "router_id=%(router_id)s, "
+                    "provider_router=%(provider_router)s"
+                    "floating_ip=%(floating_ip)s, fixed_ip=%(fixed_ip)s"
+                    "identifier=%(identifier)s"),
+                  {'router_id': router_id, 'provider_router': provider_router,
+                   'floating_ip': floating_ip, 'fixed_ip': fixed_ip,
+                   'identifier': identifier})
+        # unlink from metadata router and delete the interior ports
+        router = self.mido_api.get_router(router_id)
+        # find the provider router port that is connected to the tenant
+        # of the floating ip
+        for p in router.get_peer_ports():
+            if p.get_device_id() == provider_router.get_id():
+                pr_port = p
+
+        # get the tenant router port id connected to provider router
+        tr_port_id = pr_port.get_peer_id()
+
+        # add a route for the floating ip to bring it to the tenant
+        provider_router.add_route().type(
+            'Normal').src_network_addr('0.0.0.0').src_network_length(
+                0).dst_network_addr(
+                    floating_ip).dst_network_length(
+                        32).weight(100).next_hop_port(
+                            pr_port.get_id()).create()
+
+        chains = self.get_router_chains(router.get_tenant_id(), router_id)
+
+        # add dnat/snat rule pair for the floating ip
+        nat_targets = []
+        nat_targets.append(
+            {'addressFrom': fixed_ip, 'addressTo': fixed_ip,
+             'portFrom': 0, 'portTo': 0})
+
+        floating_property = {OS_FLOATING_IP_RULE_KEY: identifier}
+        chains['in'].add_rule().nw_dst_address(
+            floating_ip).nw_dst_length(32).type(
+                'dnat').flow_action('accept').nat_targets(
+                    nat_targets).in_ports([tr_port_id]).position(
+                        1).properties(floating_property).create()
+
+        nat_targets = []
+        nat_targets.append(
+            {'addressFrom': floating_ip, 'addressTo': floating_ip,
+             'portFrom': 0, 'portTo': 0})
+
+        chains['out'].add_rule().nw_src_address(
+            fixed_ip).nw_src_length(32).type(
+                'snat').flow_action('accept').nat_targets(
+                    nat_targets).out_ports(
+                        [tr_port_id]).position(1).properties(
+                            floating_property).create()
+
+    @handle_api_error
+    def clear_floating_ip(self, router_id, provider_router, floating_ip,
+                          identifier):
+        """Remove floating IP
+
+        :param router_id: router_id
+        :param provider_router: provider router
+        :param floating_ip: floating IP address
+        :param identifier: identifier to use to map to MidoNet
+        """
+        LOG.debug(_("MidoClient.clear_floating_ip called: "
+                    "router_id=%(router_id)s, "
+                    "provider_router=%(provider_router)s"
+                    "floating_ip=%(floating_ip)s, identifier=%(identifier)s"),
+                  {'router_id': router_id, 'provider_router': provider_router,
+                   'floating_ip': floating_ip, 'identifier': identifier})
+        router = self.mido_api.get_router(router_id)
+
+        # find the provider router port that is connected to the tenant
+        # delete the route for this floating ip
+        for r in provider_router.get_routes():
+            if (r.get_dst_network_addr() == floating_ip and
+                    r.get_dst_network_length() == 32):
+                self.mido_api.delete_route(r.get_id())
+
+        # delete snat/dnat rule pair for this floating ip
+        chains = self.get_router_chains(router.get_tenant_id(), router_id)
+
+        for r in chains['in'].get_rules():
+            if OS_FLOATING_IP_RULE_KEY in r.get_properties():
+                if r.get_properties()[OS_FLOATING_IP_RULE_KEY] == identifier:
+                    LOG.debug(_('deleting rule=%r'), r)
+                    self.mido_api.delete_rule(r.get_id())
+                    break
+
+        for r in chains['out'].get_rules():
+            if OS_FLOATING_IP_RULE_KEY in r.get_properties():
+                if r.get_properties()[OS_FLOATING_IP_RULE_KEY] == identifier:
+                    LOG.debug(_('deleting rule=%r'), r)
+                    self.mido_api.delete_rule(r.get_id())
+                    break
+
+    @handle_api_error
+    def create_for_sg(self, tenant_id, sg_id, sg_name):
+        """Create a new chain for security group.
+
+        Creating a security group creates a pair of chains in MidoNet, one for
+        inbound and the other for outbound.
+        """
+        LOG.debug(_("MidoClient.create_for_sg called: "
+                    "tenant_id=%(tenant_id)s sg_id=%(sg_id)s "
+                    "sg_name=%(sg_name)s "),
+                  {'tenant_id': tenant_id, 'sg_id': sg_id, 'sg_name': sg_name})
+
+        cnames = chain_names(sg_id, sg_name)
+        self.mido_api.add_chain().tenant_id(tenant_id).name(
+            cnames['in']).create()
+        self.mido_api.add_chain().tenant_id(tenant_id).name(
+            cnames['out']).create()
+
+        pg_name = port_group_name(sg_id, sg_name)
+        self.mido_api.add_port_group().tenant_id(tenant_id).name(
+            pg_name).create()
+
+    @handle_api_error
+    def delete_for_sg(self, tenant_id, sg_id, sg_name):
+        """Delete a chain mapped to a security group.
+
+        Delete a SG means deleting all the chains (inbound and outbound)
+        associated with the SG in MidoNet.
+        """
+        LOG.debug(_("MidoClient.delete_for_sg called: "
+                    "tenant_id=%(tenant_id)s sg_id=%(sg_id)s "
+                    "sg_name=%(sg_name)s "),
+                  {'tenant_id': tenant_id, 'sg_id': sg_id, 'sg_name': sg_name})
+
+        cnames = chain_names(sg_id, sg_name)
+        chains = self.mido_api.get_chains({'tenant_id': tenant_id})
+        for c in chains:
+            if c.get_name() == cnames['in'] or c.get_name() == cnames['out']:
+                LOG.debug(_('MidoClient.delete_for_sg: deleting chain=%r'),
+                          c.get_id())
+                self.mido_api.delete_chain(c.get_id())
+
+        pg_name = port_group_name(sg_id, sg_name)
+        pgs = self.mido_api.get_port_groups({'tenant_id': tenant_id})
+        for pg in pgs:
+            if pg.get_name() == pg_name:
+                LOG.debug(_("MidoClient.delete_for_sg: deleting pg=%r"),
+                          pg)
+                self.mido_api.delete_port_group(pg.get_id())
+
+    @handle_api_error
     def get_sg_chains(self, tenant_id, sg_id):
         """Get a list of chains mapped to a security group."""
-        LOG.debug(_("ChainManager.get_sg_chains called: "
+        LOG.debug(_("MidoClient.get_sg_chains called: "
                     "tenant_id=%(tenant_id)s sg_id=%(sg_id)s"),
                   {'tenant_id': tenant_id, 'sg_id': sg_id})
 
@@ -148,44 +865,9 @@ class ChainManager:
         assert 'out' in chains
         return chains
 
-    def _get_router_chain_names(self, router_id):
-        LOG.debug(_("ChainManager.get_router_chain_names called: "
-                    "router_id=%(router_id)s"), {'router_id': router_id})
-
-        in_name = OS_ROUTER_IN_CHAIN_NAME_FORMAT % router_id
-        out_name = OS_ROUTER_OUT_CHAIN_NAME_FORMAT % router_id
-        router_chain_names = {'in': in_name, 'out': out_name}
-        return router_chain_names
-
-
-class PortGroupManager:
-
-    def __init__(self, mido_api):
-        self.mido_api = mido_api
-
-    def create(self, tenant_id, sg_id, sg_name):
-        LOG.debug(_("PortGroupManager.create called: "
-                    "tenant_id=%(tenant_id)s sg_id=%(sg_id)s "
-                    "sg_name=%(sg_name)s"),
-                  {'tenant_id': tenant_id, 'sg_id': sg_id, 'sg_name': sg_name})
-        pg_name = port_group_name(sg_id, sg_name)
-        self.mido_api.add_port_group().tenant_id(tenant_id).name(
-            pg_name).create()
-
-    def delete(self, tenant_id, sg_id, sg_name):
-        LOG.debug(_("PortGroupManager.delete called: "
-                    "tenant_id=%(tenant_id)s sg_id=%(sg_id)s "
-                    "sg_name=%(sg_name)s"),
-                  {'tenant_id': tenant_id, 'sg_id': sg_id, 'sg_name': sg_name})
-        pg_name = port_group_name(sg_id, sg_name)
-        pgs = self.mido_api.get_port_groups({'tenant_id': tenant_id})
-        for pg in pgs:
-            if pg.get_name() == pg_name:
-                LOG.debug(_("PortGroupManager.delete: deleting pg=%r"), pg)
-                pg.delete()
-
-    def get_for_sg(self, tenant_id, sg_id):
-        LOG.debug(_("PortGroupManager.get_for_sg called: "
+    @handle_api_error
+    def get_port_groups_for_sg(self, tenant_id, sg_id):
+        LOG.debug(_("MidoClient.get_port_groups_for_sg called: "
                     "tenant_id=%(tenant_id)s sg_id=%(sg_id)s"),
                   {'tenant_id': tenant_id, 'sg_id': sg_id})
 
@@ -194,25 +876,14 @@ class PortGroupManager:
         port_groups = self.mido_api.get_port_groups({'tenant_id': tenant_id})
         for pg in port_groups:
             if pg.get_name().startswith(pg_name_prefix):
-                LOG.debug(_("PortGroupManager.get_for_sg exiting: pg=%r"), pg)
+                LOG.debug(_(
+                    "MidoClient.get_port_groups_for_sg exiting: pg=%r"), pg)
                 return pg
         return None
 
-
-class RuleManager:
-
-    OS_SG_KEY = 'os_sg_rule_id'
-
-    def __init__(self, mido_api):
-        self.mido_api = mido_api
-        self.chain_manager = ChainManager(mido_api)
-        self.pg_manager = PortGroupManager(mido_api)
-
-    def _properties(self, os_sg_rule_id):
-        return {self.OS_SG_KEY: str(os_sg_rule_id)}
-
+    @handle_api_error
     def create_for_sg_rule(self, rule):
-        LOG.debug(_("RuleManager.create_for_sg_rule called: rule=%r"), rule)
+        LOG.debug(_("MidoClient.create_for_sg_rule called: rule=%r"), rule)
 
         direction = rule['direction']
         protocol = rule['protocol']
@@ -264,7 +935,7 @@ class RuleManager:
             tp_src_start = tp_src_end = icmp_type
             tp_dst_start = tp_dst_end = icmp_code
 
-        chains = self.chain_manager.get_sg_chains(tenant_id, security_group_id)
+        chains = self.get_sg_chains(tenant_id, security_group_id)
         chain = None
         if direction == 'egress':
             chain = chains['in']
@@ -274,8 +945,8 @@ class RuleManager:
             raise Exception(_("Don't know what to do with rule=%r"), rule)
 
         # create an accept rule
-        properties = self._properties(rule_id)
-        LOG.debug(_("RuleManager.create_for_sg_rule: adding accept rule "
+        properties = sg_rule_properties(rule_id)
+        LOG.debug(_("MidoClient.create_for_sg_rule: adding accept rule "
                     "%(rule_id)s in portgroup %(port_group_id)s"),
                   {'rule_id': rule_id, 'port_group_id': port_group_id})
         chain.add_rule().port_group(port_group_id).type('accept').nw_proto(
@@ -284,20 +955,21 @@ class RuleManager:
                     tp_src_end).tp_dst_start(tp_dst_start).tp_dst_end(
                         tp_dst_end).properties(properties).create()
 
+    @handle_api_error
     def delete_for_sg_rule(self, rule):
-        LOG.debug(_("RuleManager.delete_for_sg_rule called: rule=%r"), rule)
+        LOG.debug(_("MidoClient.delete_for_sg_rule called: rule=%r"), rule)
 
         tenant_id = rule['tenant_id']
         security_group_id = rule['security_group_id']
         rule_id = rule['id']
 
-        properties = self._properties(rule_id)
+        properties = sg_rule_properties(rule_id)
         # search for the chains to find the rule to delete
-        chains = self.chain_manager.get_sg_chains(tenant_id, security_group_id)
+        chains = self.get_sg_chains(tenant_id, security_group_id)
         for c in chains['in'], chains['out']:
             rules = c.get_rules()
             for r in rules:
                 if r.get_properties() == properties:
-                    LOG.debug(_("RuleManager.delete_for_sg_rule: deleting "
+                    LOG.debug(_("MidoClient.delete_for_sg_rule: deleting "
                                 "rule %r"), r)
-                    r.delete()
+                    self.mido_api.delete_rule(r.get_id())
index 2c158f2b02e8ea74ec49c77b51015b394334b426..1c8d3e1186f6757556f73613ada415f32227bc23 100644 (file)
@@ -22,7 +22,6 @@
 
 from midonetclient import api
 from oslo.config import cfg
-from webob import exc as w_exc
 
 from quantum.common import exceptions as q_exc
 from quantum.db import api as db
@@ -38,15 +37,6 @@ from quantum.plugins.midonet import midonet_lib
 
 LOG = logging.getLogger(__name__)
 
-OS_TENANT_ROUTER_RULE_KEY = 'OS_TENANT_ROUTER_RULE'
-OS_FLOATING_IP_RULE_KEY = 'OS_FLOATING_IP'
-SNAT_RULE = 'SNAT'
-SNAT_RULE_PROPERTY = {OS_TENANT_ROUTER_RULE_KEY: SNAT_RULE}
-
-
-class MidonetResourceNotFound(q_exc.NotFound):
-    message = _('MidoNet %(resource_type)s %(id)s could not be found')
-
 
 class MidonetPluginException(q_exc.QuantumException):
     message = _("%(msg)s")
@@ -57,6 +47,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                       securitygroups_db.SecurityGroupDbMixin):
 
     supported_extension_aliases = ['router', 'security-group']
+    __native_bulk_support = False
 
     def __init__(self):
 
@@ -73,26 +64,23 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         self.mido_api = api.MidonetApi(midonet_uri, admin_user,
                                        admin_pass,
                                        project_id=admin_project_id)
+        self.client = midonet_lib.MidoClient(self.mido_api)
 
-        # get MidoNet provider router and metadata router
         if provider_router_id and metadata_router_id:
-            self.provider_router = self.mido_api.get_router(provider_router_id)
-            self.metadata_router = self.mido_api.get_router(metadata_router_id)
-
-        # for dev purpose only
-        elif mode == 'dev':
-            msg = _('No provider router and metadata device ids found. '
-                    'But skipping because running in dev env.')
-            LOG.debug(msg)
-        else:
-            msg = _('provider_router_id and metadata_router_id '
-                    'should be configured in the plugin config file')
-            LOG.exception(msg)
-            raise MidonetPluginException(msg=msg)
-
-        self.chain_manager = midonet_lib.ChainManager(self.mido_api)
-        self.pg_manager = midonet_lib.PortGroupManager(self.mido_api)
-        self.rule_manager = midonet_lib.RuleManager(self.mido_api)
+            # get MidoNet provider router and metadata router
+            self.provider_router = self.client.get_router(provider_router_id)
+            self.metadata_router = self.client.get_router(metadata_router_id)
+
+        elif not provider_router_id or not metadata_router_id:
+            if mode == 'dev':
+                msg = _('No provider router and metadata device ids found. '
+                        'But skipping because running in dev env.')
+                LOG.debug(msg)
+            else:
+                msg = _('provider_router_id and metadata_router_id '
+                        'should be configured in the plugin config file')
+                LOG.exception(msg)
+                raise MidonetPluginException(msg=msg)
 
         db.configure_db()
 
@@ -118,114 +106,27 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         with session.begin(subtransactions=True):
             sn_entry = super(MidonetPluginV2, self).create_subnet(context,
                                                                   subnet)
-            try:
-                bridge = self.mido_api.get_bridge(sn_entry['network_id'])
-            except w_exc.HTTPNotFound:
-                raise MidonetResourceNotFound(resource_type='Bridge',
-                                              id=sn_entry['network_id'])
+            bridge = self.client.get_bridge(sn_entry['network_id'])
 
             gateway_ip = subnet['subnet']['gateway_ip']
             network_address, prefix = subnet['subnet']['cidr'].split('/')
-            bridge.add_dhcp_subnet().default_gateway(gateway_ip).subnet_prefix(
-                network_address).subnet_length(prefix).create()
+            self.client.create_dhcp(bridge, gateway_ip, network_address,
+                                    prefix)
 
-            # If the network is external, link the bridge to MidoNet provider
-            # router
+            # For external network, link the bridge to the provider router.
             self._extend_network_dict_l3(context, net)
             if net['router:external']:
                 gateway_ip = sn_entry['gateway_ip']
                 network_address, length = sn_entry['cidr'].split('/')
 
-                # create a interior port in the MidoNet provider router
-                in_port = self.provider_router.add_interior_port()
-                pr_port = in_port.port_address(gateway_ip).network_address(
-                    network_address).network_length(length).create()
-
-                # create a interior port in the bridge, then link
-                # it to the provider router.
-                br_port = bridge.add_interior_port().create()
-                pr_port.link(br_port.get_id())
-
-                # add a route for the subnet in the provider router
-                self.provider_router.add_route().type(
-                    'Normal').src_network_addr('0.0.0.0').src_network_length(
-                        0).dst_network_addr(
-                            network_address).dst_network_length(
-                                length).weight(100).next_hop_port(
-                                    pr_port.get_id()).create()
+                self.client.link_bridge_to_provider_router(
+                    bridge, self.provider_router, gateway_ip, network_address,
+                    length)
 
         LOG.debug(_("MidonetPluginV2.create_subnet exiting: sn_entry=%r"),
                   sn_entry)
         return sn_entry
 
-    def get_subnet(self, context, id, fields=None):
-        """Get Quantum subnet.
-
-        Retrieves a Quantum subnet record but also including the DHCP entry
-        data stored in MidoNet.
-        """
-        LOG.debug(_("MidonetPluginV2.get_subnet called: id=%(id)s "
-                    "fields=%(fields)s"), {'id': id, 'fields': fields})
-
-        qsubnet = super(MidonetPluginV2, self).get_subnet(context, id)
-        bridge_id = qsubnet['network_id']
-        try:
-            bridge = self.mido_api.get_bridge(bridge_id)
-        except w_exc.HTTPNotFound:
-            raise MidonetResourceNotFound(resource_type='Bridge',
-                                          id=bridge_id)
-
-        # get dhcp subnet data from MidoNet bridge.
-        dhcps = bridge.get_dhcp_subnets()
-        b_network_address = dhcps[0].get_subnet_prefix()
-        b_prefix = dhcps[0].get_subnet_length()
-
-        # Validate against quantum database.
-        network_address, prefix = qsubnet['cidr'].split('/')
-        if network_address != b_network_address or int(prefix) != b_prefix:
-            raise MidonetResourceNotFound(resource_type='DhcpSubnet',
-                                          id=qsubnet['cidr'])
-
-        LOG.debug(_("MidonetPluginV2.get_subnet exiting: qsubnet=%s"), qsubnet)
-        return qsubnet
-
-    def get_subnets(self, context, filters=None, fields=None):
-        """List Quantum subnets.
-
-        Retrieves Quantum subnets with some fields populated by the data
-        stored in MidoNet.
-        """
-        LOG.debug(_("MidonetPluginV2.get_subnets called: filters=%(filters)r, "
-                    "fields=%(fields)r"),
-                  {'filters': filters, 'fields': fields})
-        subnets = super(MidonetPluginV2, self).get_subnets(context, filters,
-                                                           fields)
-        for sn in subnets:
-            if not 'network_id' in sn:
-                continue
-            try:
-                bridge = self.mido_api.get_bridge(sn['network_id'])
-            except w_exc.HTTPNotFound:
-                raise MidonetResourceNotFound(resource_type='Bridge',
-                                              id=sn['network_id'])
-
-            # TODO(tomoe): dedupe this part.
-            # get dhcp subnet data from MidoNet bridge.
-            dhcps = bridge.get_dhcp_subnets()
-            b_network_address = dhcps[0].get_subnet_prefix()
-            b_prefix = dhcps[0].get_subnet_length()
-
-            # Validate against quantum database.
-            if sn.get('cidr'):
-                network_address, prefix = sn['cidr'].split('/')
-                if network_address != b_network_address or int(
-                    prefix) != b_prefix:
-                    raise MidonetResourceNotFound(resource_type='DhcpSubnet',
-                                                  id=sn['cidr'])
-
-        LOG.debug(_("MidonetPluginV2.create_subnet exiting"))
-        return subnets
-
     def delete_subnet(self, context, id):
         """Delete Quantum subnet.
 
@@ -237,37 +138,14 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         net = super(MidonetPluginV2, self).get_network(context,
                                                        subnet['network_id'],
                                                        fields=None)
-        bridge_id = subnet['network_id']
-        try:
-            bridge = self.mido_api.get_bridge(bridge_id)
-        except w_exc.HTTPNotFound:
-            raise MidonetResourceNotFound(resource_type='Bridge', id=bridge_id)
-
-        dhcp = bridge.get_dhcp_subnets()
-        dhcp[0].delete()
+        bridge = self.client.get_bridge(subnet['network_id'])
+        self.client.delete_dhcp(bridge)
 
         # If the network is external, clean up routes, links, ports.
         self._extend_network_dict_l3(context, net)
         if net['router:external']:
-            # Delete routes and unlink the router and the bridge.
-            routes = self.provider_router.get_routes()
-
-            bridge_ports_to_delete = []
-            for p in self.provider_router.get_peer_ports():
-                if p.get_device_id() == bridge.get_id():
-                    bridge_ports_to_delete.append(p)
-
-            for p in bridge.get_peer_ports():
-                if p.get_device_id() == self.provider_router.get_id():
-                    # delete the routes going to the brdge
-                    for r in routes:
-                        if r.get_next_hop_port() == p.get_id():
-                            r.delete()
-                    p.unlink()
-                    p.delete()
-
-            # delete bridge port
-            map(lambda x: x.delete(), bridge_ports_to_delete)
+            self.client.unlink_bridge_from_provider_router(
+                bridge, self.provider_router)
 
         super(MidonetPluginV2, self).delete_subnet(context, id)
         LOG.debug(_("MidonetPluginV2.delete_subnet exiting"))
@@ -281,9 +159,8 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                   network)
 
         if network['network']['admin_state_up'] is False:
-            LOG.warning(_('Ignoring admin_state_up=False for network=%r'
-                          'Overriding with True'), network)
-            network['network']['admin_state_up'] = True
+            LOG.warning(_('Ignoring admin_state_up=False for network=%r '
+                          'because it is not yet supported'), network)
 
         tenant_id = self._get_tenant_id_for_create(context, network['network'])
 
@@ -291,8 +168,8 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
         session = context.session
         with session.begin(subtransactions=True):
-            bridge = self.mido_api.add_bridge().name(
-                network['network']['name']).tenant_id(tenant_id).create()
+            bridge = self.client.create_bridge(tenant_id,
+                                               network['network']['name'])
 
             # Set MidoNet bridge ID to the quantum DB entry
             network['network']['id'] = bridge.get_id()
@@ -324,11 +201,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         with session.begin(subtransactions=True):
             net = super(MidonetPluginV2, self).update_network(
                 context, id, network)
-            try:
-                bridge = self.mido_api.get_bridge(id)
-            except w_exc.HTTPNotFound:
-                raise MidonetResourceNotFound(resource_type='Bridge', id=id)
-            bridge.name(net['name']).update()
+            self.client.update_bridge(id, net['name'])
 
         self._extend_network_dict_l3(context, net)
         LOG.debug(_("MidonetPluginV2.update_network exiting: net=%r"), net)
@@ -345,10 +218,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         # NOTE: Get network data with all fields (fields=None) for
         #       _extend_network_dict_l3() method, which needs 'id' field
         qnet = super(MidonetPluginV2, self).get_network(context, id, None)
-        try:
-            self.mido_api.get_bridge(id)
-        except w_exc.HTTPNotFound:
-            raise MidonetResourceNotFound(resource_type='Bridge', id=id)
+        self.client.get_bridge(id)
 
         self._extend_network_dict_l3(context, qnet)
         LOG.debug(_("MidonetPluginV2.get_network exiting: qnet=%r"), qnet)
@@ -364,13 +234,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         #       _extend_network_dict_l3() method, which needs 'id' field
         qnets = super(MidonetPluginV2, self).get_networks(context, filters,
                                                           None)
-        self.mido_api.get_bridges({'tenant_id': context.tenant_id})
         for n in qnets:
-            try:
-                self.mido_api.get_bridge(n['id'])
-            except w_exc.HTTPNotFound:
-                raise MidonetResourceNotFound(resource_type='Bridge',
-                                              id=n['id'])
             self._extend_network_dict_l3(context, n)
 
         return [self._fields(net, fields) for net in qnets]
@@ -378,8 +242,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     def delete_network(self, context, id):
         """Delete a network and its corresponding MidoNet bridge."""
         LOG.debug(_("MidonetPluginV2.delete_network called: id=%r"), id)
-
-        self.mido_api.get_bridge(id).delete()
+        self.client.delete_bridge(id)
         try:
             super(MidonetPluginV2, self).delete_network(context, id)
         except Exception:
@@ -394,25 +257,21 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         is_compute_interface = False
         port_data = port['port']
         # get the bridge and create a port on it.
-        try:
-            bridge = self.mido_api.get_bridge(port_data['network_id'])
-        except w_exc.HTTPNotFound:
-            raise MidonetResourceNotFound(resource_type='Bridge',
-                                          id=port_data['network_id'])
+        bridge = self.client.get_bridge(port_data['network_id'])
 
         device_owner = port_data['device_owner']
 
         if device_owner.startswith('compute:') or device_owner is '':
             is_compute_interface = True
-            bridge_port = bridge.add_exterior_port().create()
+            bridge_port = self.client.create_exterior_bridge_port(bridge)
         elif device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF:
-            bridge_port = bridge.add_interior_port().create()
+            bridge_port = self.client.create_interior_bridge_port(bridge)
         elif (device_owner == l3_db.DEVICE_OWNER_ROUTER_GW or
                 device_owner == l3_db.DEVICE_OWNER_FLOATINGIP):
 
             # This is a dummy port to make l3_db happy.
             # This will not be used in MidoNet
-            bridge_port = bridge.add_interior_port().create()
+            bridge_port = self.client.create_interior_bridge_port(bridge)
 
         if bridge_port:
             # set midonet port id to quantum port id and create a DB record.
@@ -433,20 +292,11 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     fixed_ip = port_db_entry['fixed_ips'][0]['ip_address']
                     mac = port_db_entry['mac_address']
                     # create dhcp host entry under the bridge.
-                    dhcp_subnets = bridge.get_dhcp_subnets()
-                    if dhcp_subnets:
-                        dhcp_subnets[0].add_dhcp_host().ip_addr(
-                            fixed_ip).mac_addr(mac).create()
+                    self.client.create_dhcp_hosts(bridge, fixed_ip, mac)
         LOG.debug(_("MidonetPluginV2.create_port exiting: port_db_entry=%r"),
                   port_db_entry)
         return port_db_entry
 
-    def update_port(self, context, id, port):
-        """Update port."""
-        LOG.debug(_("MidonetPluginV2.update_port called: id=%(id)s "
-                    "port=%(port)r"), {'id': id, 'port': port})
-        return super(MidonetPluginV2, self).update_port(context, id, port)
-
     def get_port(self, context, id, fields=None):
         """Retrieve port."""
         LOG.debug(_("MidonetPluginV2.get_port called: id=%(id)s "
@@ -456,10 +306,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         port_db_entry = super(MidonetPluginV2, self).get_port(context,
                                                               id, fields)
         # verify that corresponding port exists in MidoNet.
-        try:
-            self.mido_api.get_port(id)
-        except w_exc.HTTPNotFound:
-            raise MidonetResourceNotFound(resource_type='Port', id=id)
+        self.client.get_port(id)
 
         LOG.debug(_("MidonetPluginV2.get_port exiting: port_db_entry=%r"),
                   port_db_entry)
@@ -474,12 +321,9 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                                                                 filters,
                                                                 fields)
         if ports_db_entry:
-            try:
-                for port in ports_db_entry:
-                    self.mido_api.get_port(port['id'])
-            except w_exc.HTTPNotFound:
-                raise MidonetResourceNotFound(resource_type='Port',
-                                              id=port['id'])
+            for port in ports_db_entry:
+                if 'security_gorups' in port:
+                    self._extend_port_dict_security_group(context, port)
         return ports_db_entry
 
     def delete_port(self, context, id, l3_port_check=True):
@@ -496,7 +340,6 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         with session.begin(subtransactions=True):
             port_db_entry = super(MidonetPluginV2, self).get_port(context,
                                                                   id, None)
-            bridge = self.mido_api.get_bridge(port_db_entry['network_id'])
             # Clean up dhcp host entry if needed.
             if 'ip_address' in (port_db_entry['fixed_ips'] or [{}])[0]:
                 # get ip and mac from DB record.
@@ -504,13 +347,10 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 mac = port_db_entry['mac_address']
 
                 # create dhcp host entry under the bridge.
-                dhcp_subnets = bridge.get_dhcp_subnets()
-                if dhcp_subnets:
-                    for dh in dhcp_subnets[0].get_dhcp_hosts():
-                        if dh.get_mac_addr() == mac and dh.get_ip_addr() == ip:
-                            dh.delete()
+                self.client.delete_dhcp_hosts(port_db_entry['network_id'], ip,
+                                              mac)
 
-            self.mido_api.get_port(id).delete()
+            self.client.delete_port(id)
             return super(MidonetPluginV2, self).delete_port(context, id)
 
     #
@@ -528,40 +368,17 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         tenant_id = self._get_tenant_id_for_create(context, router['router'])
         session = context.session
         with session.begin(subtransactions=True):
-            mrouter = self.mido_api.add_router().name(
-                router['router']['name']).tenant_id(tenant_id).create()
+            mrouter = self.client.create_tenant_router(
+                tenant_id, router['router']['name'], self.metadata_router)
+
             qrouter = super(MidonetPluginV2, self).create_router(context,
                                                                  router)
 
-            chains = self.chain_manager.create_router_chains(tenant_id,
-                                                             mrouter.get_id())
-
-            # set chains to in/out filters
-            mrouter.inbound_filter_id(
-                chains['in'].get_id()).outbound_filter_id(
-                    chains['out'].get_id()).update()
-
             # get entry from the DB and update 'id' with MidoNet router id.
             qrouter_entry = self._get_router(context, qrouter['id'])
             qrouter['id'] = mrouter.get_id()
             qrouter_entry.update(qrouter)
 
-            # link to metadata router
-            in_port = self.metadata_router.add_interior_port()
-            mdr_port = in_port.network_address('169.254.255.0').network_length(
-                30).port_address('169.254.255.1').create()
-
-            tr_port = mrouter.add_interior_port().network_address(
-                '169.254.255.0').network_length(30).port_address(
-                    '169.254.255.2').create()
-            mdr_port.link(tr_port.get_id())
-
-            # forward metadata traffic to metadata router
-            mrouter.add_route().type('Normal').src_network_addr(
-                '0.0.0.0').src_network_length(0).dst_network_addr(
-                    '169.254.169.254').dst_network_length(32).weight(
-                        100).next_hop_port(tr_port.get_id()).create()
-
             LOG.debug(_("MidonetPluginV2.create_router exiting: qrouter=%r"),
                       qrouter)
             return qrouter
@@ -603,9 +420,8 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
             changed_name = router['router'].get('name')
             if changed_name:
-                self.mido_api.get_router(id).name(changed_name).update()
+                self.client.update_router(id, changed_name)
 
-            tenant_router = self.mido_api.get_router(id)
             if op_gateway_set:
                 # find a qport with the network_id for the router
                 qports = super(MidonetPluginV2, self).get_ports(
@@ -615,82 +431,12 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 qport = qports[0]
                 snat_ip = qport['fixed_ips'][0]['ip_address']
 
-                in_port = self.provider_router.add_interior_port()
-                pr_port = in_port.network_address(
-                    '169.254.255.0').network_length(30).port_address(
-                        '169.254.255.1').create()
-
-                # Create a port in the tenant router
-                tr_port = tenant_router.add_interior_port().network_address(
-                    '169.254.255.0').network_length(30).port_address(
-                        '169.254.255.2').create()
-
-                # Link them
-                pr_port.link(tr_port.get_id())
-
-                # Add a route for snat_ip to bring it down to tenant
-                self.provider_router.add_route().type(
-                    'Normal').src_network_addr('0.0.0.0').src_network_length(
-                        0).dst_network_addr(snat_ip).dst_network_length(
-                            32).weight(100).next_hop_port(
-                                pr_port.get_id()).create()
-
-                # Add default route to uplink in the tenant router
-                tenant_router.add_route().type('Normal').src_network_addr(
-                    '0.0.0.0').src_network_length(0).dst_network_addr(
-                        '0.0.0.0').dst_network_length(0).weight(
-                            100).next_hop_port(tr_port.get_id()).create()
-
-                # ADD SNAT(masquerade) rules
-                chains = self.chain_manager.get_router_chains(
-                    tenant_router.get_tenant_id(), tenant_router.get_id())
-
-                chains['in'].add_rule().nw_dst_address(snat_ip).nw_dst_length(
-                    32).type('rev_snat').flow_action('accept').in_ports(
-                        [tr_port.get_id()]).properties(
-                            SNAT_RULE_PROPERTY).position(1).create()
-
-                nat_targets = []
-                nat_targets.append(
-                    {'addressFrom': snat_ip, 'addressTo': snat_ip,
-                     'portFrom': 1, 'portTo': 65535})
-
-                chains['out'].add_rule().type('snat').flow_action(
-                    'accept').nat_targets(nat_targets).out_ports(
-                        [tr_port.get_id()]).properties(
-                            SNAT_RULE_PROPERTY).position(1).create()
+                self.client.set_router_external_gateway(id,
+                                                        self.provider_router,
+                                                        snat_ip)
 
             if op_gateway_clear:
-                # delete the port that is connected to provider router
-                for p in tenant_router.get_ports():
-                    if p.get_port_address() == '169.254.255.2':
-                        peer_port_id = p.get_peer_id()
-                        p.unlink()
-                        self.mido_api.get_port(peer_port_id).delete()
-                        p.delete()
-
-                # delete default route
-                for r in tenant_router.get_routes():
-                    if (r.get_dst_network_addr() == '0.0.0.0' and
-                            r.get_dst_network_length() == 0):
-                        r.delete()
-
-                # delete SNAT(masquerade) rules
-                chains = self.chain_manager.get_router_chains(
-                    tenant_router.get_tenant_id(),
-                    tenant_router.get_id())
-
-                for r in chains['in'].get_rules():
-                    if OS_TENANT_ROUTER_RULE_KEY in r.get_properties():
-                        if r.get_properties()[
-                            OS_TENANT_ROUTER_RULE_KEY] == SNAT_RULE:
-                            r.delete()
-
-                for r in chains['out'].get_rules():
-                    if OS_TENANT_ROUTER_RULE_KEY in r.get_properties():
-                        if r.get_properties()[
-                            OS_TENANT_ROUTER_RULE_KEY] == SNAT_RULE:
-                            r.delete()
+                self.client.clear_router_external_gateway(id)
 
         LOG.debug(_("MidonetPluginV2.update_router exiting: qrouter=%r"),
                   qrouter)
@@ -699,61 +445,13 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     def delete_router(self, context, id):
         LOG.debug(_("MidonetPluginV2.delete_router called: id=%s"), id)
 
-        mrouter = self.mido_api.get_router(id)
-        tenant_id = mrouter.get_tenant_id()
-
-        # unlink from metadata router and delete the interior ports
-        # that connect metadata router and this router.
-        for pp in self.metadata_router.get_peer_ports():
-            if pp.get_device_id() == mrouter.get_id():
-                mdr_port = self.mido_api.get_port(pp.get_peer_id())
-                pp.unlink()
-                pp.delete()
-                mdr_port.delete()
-
-        # delete corresponding chains
-        chains = self.chain_manager.get_router_chains(tenant_id,
-                                                      mrouter.get_id())
-        chains['in'].delete()
-        chains['out'].delete()
-
-        # delete the router
-        mrouter.delete()
+        self.client.delete_tenant_router(id)
 
         result = super(MidonetPluginV2, self).delete_router(context, id)
         LOG.debug(_("MidonetPluginV2.delete_router exiting: result=%s"),
                   result)
         return result
 
-    def get_router(self, context, id, fields=None):
-        LOG.debug(_("MidonetPluginV2.get_router called: id=%(id)s "
-                    "fields=%(fields)r"), {'id': id, 'fields': fields})
-        qrouter = super(MidonetPluginV2, self).get_router(context, id, fields)
-
-        try:
-            self.mido_api.get_router(id)
-        except w_exc.HTTPNotFound:
-            raise MidonetResourceNotFound(resource_type='Router', id=id)
-
-        LOG.debug(_("MidonetPluginV2.get_router exiting: qrouter=%r"),
-                  qrouter)
-        return qrouter
-
-    def get_routers(self, context, filters=None, fields=None):
-        LOG.debug(_("MidonetPluginV2.get_routers called: filters=%(filters)s "
-                    "fields=%(fields)r"),
-                  {'filters': filters, 'fields': fields})
-
-        qrouters = super(MidonetPluginV2, self).get_routers(
-            context, filters, fields)
-        for qr in qrouters:
-            try:
-                self.mido_api.get_router(qr['id'])
-            except w_exc.HTTPNotFound:
-                raise MidonetResourceNotFound(resource_type='Router',
-                                              id=qr['id'])
-        return qrouters
-
     def add_router_interface(self, context, router_id, interface_info):
         LOG.debug(_("MidonetPluginV2.add_router_interface called: "
                     "router_id=%(router_id)s "
@@ -772,33 +470,10 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             network_address, length = subnet['cidr'].split('/')
 
             # Link the router and the bridge port.
-            mrouter = self.mido_api.get_router(router_id)
-            mrouter_port = mrouter.add_interior_port().port_address(
-                gateway_ip).network_address(
-                    network_address).network_length(length).create()
-
-            mbridge_port = self.mido_api.get_port(qport['port_id'])
-            mrouter_port.link(mbridge_port.get_id())
-
-            # Add a route entry to the subnet
-            mrouter.add_route().type('Normal').src_network_addr(
-                '0.0.0.0').src_network_length(0).dst_network_addr(
-                    network_address).dst_network_length(length).weight(
-                        100).next_hop_port(mrouter_port.get_id()).create()
-
-            # add a route for the subnet in metadata router; forward
-            # packets destined to the subnet to the tenant router
-            found = False
-            for pp in self.metadata_router.get_peer_ports():
-                if pp.get_device_id() == mrouter.get_id():
-                    mdr_port_id = pp.get_peer_id()
-                    found = True
-            assert found
-
-            self.metadata_router.add_route().type(
-                'Normal').src_network_addr('0.0.0.0').src_network_length(
-                    0).dst_network_addr(network_address).dst_network_length(
-                        length).weight(100).next_hop_port(mdr_port_id).create()
+            self.client.link_bridge_port_to_router(qport['port_id'], router_id,
+                                                   gateway_ip, network_address,
+                                                   length,
+                                                   self.metadata_router)
 
         LOG.debug(_("MidonetPluginV2.add_router_interface exiting: "
                     "qport=%r"), qport)
@@ -810,9 +485,10 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     "router_id=%(router_id)s "
                     "interface_info=%(interface_info)r"),
                   {'router_id': router_id, 'interface_info': interface_info})
+        port_id = None
         if 'port_id' in interface_info:
 
-            mbridge_port = self.mido_api.get_port(interface_info['port_id'])
+            port_id = interface_info['port_id']
             subnet_id = self.get_port(context,
                                       interface_info['port_id']
                                       )['fixed_ips'][0]['subnet_id']
@@ -837,36 +513,18 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     network_port = p
                     break
             assert network_port
-            mbridge_port = self.mido_api.get_port(network_port['id'])
+            port_id = network_port['id']
+
+        assert port_id
 
         # get network information from subnet data
         network_addr, network_length = subnet['cidr'].split('/')
         network_length = int(network_length)
 
         # Unlink the router and the bridge.
-        mrouter = self.mido_api.get_router(router_id)
-        mrouter_port = self.mido_api.get_port(mbridge_port.get_peer_id())
-        mrouter_port.unlink()
-
-        # Delete the route for the subnet.
-        found = False
-        for r in mrouter.get_routes():
-            if r.get_next_hop_port() == mrouter_port.get_id():
-                r.delete()
-                found = True
-                #break   # commented out due to issue#314
-        assert found
-
-        # delete the route for the subnet in the metadata router
-        found = False
-        for r in self.metadata_router.get_routes():
-            if (r.get_dst_network_addr() == network_addr and
-                r.get_dst_network_length() == network_length):
-                LOG.debug(_('Deleting route=%r ...'), r)
-                r.delete()
-                found = True
-                break
-        assert found
+        self.client.unlink_bridge_port_from_router(port_id, network_addr,
+                                                   network_length,
+                                                   self.metadata_router)
 
         info = super(MidonetPluginV2, self).remove_router_interface(
             context, router_id, interface_info)
@@ -883,91 +541,18 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             if floatingip['floatingip']['port_id']:
                 fip = super(MidonetPluginV2, self).update_floatingip(
                     context, id, floatingip)
-                router_id = fip['router_id']
-                floating_address = fip['floating_ip_address']
-                fixed_address = fip['fixed_ip_address']
-
-                tenant_router = self.mido_api.get_router(router_id)
-                # find the provider router port that is connected to the tenant
-                # of the floating ip
-                for p in tenant_router.get_peer_ports():
-                    if p.get_device_id() == self.provider_router.get_id():
-                        pr_port = p
-
-                # get the tenant router port id connected to provider router
-                tr_port_id = pr_port.get_peer_id()
-
-                # add a route for the floating ip to bring it to the tenant
-                self.provider_router.add_route().type(
-                    'Normal').src_network_addr('0.0.0.0').src_network_length(
-                        0).dst_network_addr(
-                            floating_address).dst_network_length(
-                                32).weight(100).next_hop_port(
-                                    pr_port.get_id()).create()
-
-                chains = self.chain_manager.get_router_chains(fip['tenant_id'],
-                                                              fip['router_id'])
-                # add dnat/snat rule pair for the floating ip
-                nat_targets = []
-                nat_targets.append(
-                    {'addressFrom': fixed_address, 'addressTo': fixed_address,
-                     'portFrom': 0, 'portTo': 0})
-
-                floating_property = {OS_FLOATING_IP_RULE_KEY: id}
-                chains['in'].add_rule().nw_dst_address(
-                    floating_address).nw_dst_length(32).type(
-                        'dnat').flow_action('accept').nat_targets(
-                            nat_targets).in_ports([tr_port_id]).position(
-                                1).properties(floating_property).create()
-
-                nat_targets = []
-                nat_targets.append(
-                    {'addressFrom': floating_address,
-                     'addressTo': floating_address,
-                     'portFrom': 0,
-                     'portTo': 0})
-
-                chains['out'].add_rule().nw_src_address(
-                    fixed_address).nw_src_length(32).type(
-                        'snat').flow_action('accept').nat_targets(
-                            nat_targets).out_ports(
-                                [tr_port_id]).position(1).properties(
-                                    floating_property).create()
 
+                self.client.setup_floating_ip(fip['router_id'],
+                                              self.provider_router,
+                                              fip['floating_ip_address'],
+                                              fip['fixed_ip_address'], id)
             # disassociate floating IP
             elif floatingip['floatingip']['port_id'] is None:
 
                 fip = super(MidonetPluginV2, self).get_floatingip(context, id)
-
-                router_id = fip['router_id']
-                floating_address = fip['floating_ip_address']
-                fixed_address = fip['fixed_ip_address']
-
-                # delete the route for this floating ip
-                for r in self.provider_router.get_routes():
-                    if (r.get_dst_network_addr() == floating_address and
-                            r.get_dst_network_length() == 32):
-                        r.delete()
-
-                # delete snat/dnat rule pair for this floating ip
-                chains = self.chain_manager.get_router_chains(fip['tenant_id'],
-                                                              fip['router_id'])
-                LOG.debug(_('chains=%r'), chains)
-
-                for r in chains['in'].get_rules():
-                    if OS_FLOATING_IP_RULE_KEY in r.get_properties():
-                        if r.get_properties()[OS_FLOATING_IP_RULE_KEY] == id:
-                            LOG.debug(_('deleting rule=%r'), r)
-                            r.delete()
-                            break
-
-                for r in chains['out'].get_rules():
-                    if OS_FLOATING_IP_RULE_KEY in r.get_properties():
-                        if r.get_properties()[OS_FLOATING_IP_RULE_KEY] == id:
-                            LOG.debug(_('deleting rule=%r'), r)
-                            r.delete()
-                            break
-
+                self.client.clear_floating_ip(fip['router_id'],
+                                              self.provider_router,
+                                              fip['floating_ip_address'], id)
                 super(MidonetPluginV2, self).update_floatingip(context, id,
                                                                floatingip)
 
@@ -993,10 +578,8 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 context, security_group, default_sg)
 
             # Create MidoNet chains and portgroup for the SG
-            sg_id = sg_db_entry['id']
-            sg_name = sg_db_entry['name']
-            self.chain_manager.create_for_sg(tenant_id, sg_id, sg_name)
-            self.pg_manager.create(tenant_id, sg_id, sg_name)
+            self.client.create_for_sg(tenant_id, sg_db_entry['id'],
+                                      sg_db_entry['name'])
 
             LOG.debug(_("MidonetPluginV2.create_security_group exiting: "
                         "sg_db_entry=%r"), sg_db_entry)
@@ -1026,8 +609,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 raise ext_sg.SecurityGroupInUse(id=sg_id)
 
             # Delete MidoNet Chains and portgroup for the SG
-            self.chain_manager.delete_for_sg(tenant_id, sg_id, sg_name)
-            self.pg_manager.delete(tenant_id, sg_id, sg_name)
+            self.client.delete_for_sg(tenant_id, sg_id, sg_name)
 
             return super(MidonetPluginV2, self).delete_security_group(
                 context, id)
@@ -1057,7 +639,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 MidonetPluginV2, self).create_security_group_rule(
                     context, security_group_rule)
 
-            self.rule_manager.create_for_sg_rule(rule_db_entry)
+            self.client.create_for_sg_rule(rule_db_entry)
             LOG.debug(_("MidonetPluginV2.create_security_group_rule exiting: "
                         "rule_db_entry=%r"), rule_db_entry)
             return rule_db_entry
@@ -1073,7 +655,7 @@ class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             if not rule_db_entry:
                 raise ext_sg.SecurityGroupRuleNotFound(id=sgrid)
 
-            self.rule_manager.delete_for_sg_rule(rule_db_entry)
+            self.client.delete_for_sg_rule(rule_db_entry)
             return super(MidonetPluginV2,
                          self).delete_security_group_rule(context, sgrid)
 
diff --git a/quantum/tests/unit/midonet/etc/midonet.ini.test b/quantum/tests/unit/midonet/etc/midonet.ini.test
new file mode 100644 (file)
index 0000000..36d7156
--- /dev/null
@@ -0,0 +1,16 @@
+[MIDONET]
+
+# MidoNet API server URI
+midonet_uri = http://localhost:8080/midonet-api
+
+# MidoNet admin username
+username = admin
+
+# MidoNet admin password
+password = passw0rd
+
+# Virtual provider router ID
+provider_router_id = 00112233-0011-0011-0011-001122334455
+
+# Virtual metadata router ID
+metadata_router_id = ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa
diff --git a/quantum/tests/unit/midonet/mock_lib.py b/quantum/tests/unit/midonet/mock_lib.py
new file mode 100644 (file)
index 0000000..8b9224e
--- /dev/null
@@ -0,0 +1,253 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (C) 2013 Midokura PTE LTD
+# 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.
+#
+# @author: Ryu Ishimoto, Midokura Japan KK
+
+import mock
+import uuid
+
+
+def get_bridge_mock(id=None, tenant_id='test-tenant', name='net'):
+    if id is None:
+        id = str(uuid.uuid4())
+
+    bridge = mock.Mock()
+    bridge.get_id.return_value = id
+    bridge.get_tenant_id.return_value = tenant_id
+    bridge.get_name.return_value = name
+    return bridge
+
+
+def get_bridge_port_mock(id=None, bridge_id=None,
+                         type='ExteriorBridge'):
+    if id is None:
+        id = str(uuid.uuid4())
+    if bridge_id is None:
+        bridge_id = str(uuid.uuid4())
+
+    port = mock.Mock()
+    port.get_id.return_value = id
+    port.get_brige_id.return_value = bridge_id
+    port.get_type.return_value = type
+    return port
+
+
+def get_chain_mock(id=None, tenant_id='test-tenant', name='chain',
+                   rules=None):
+    if id is None:
+        id = str(uuid.uuid4())
+
+    if rules is None:
+        rules = []
+
+    chain = mock.Mock()
+    chain.get_id.return_value = id
+    chain.get_tenant_id.return_value = tenant_id
+    chain.get_name.return_value = name
+    chain.get_rules.return_value = rules
+    return chain
+
+
+def get_exterior_bridge_port_mock(id=None, bridge_id=None):
+    if id is None:
+        id = str(uuid.uuid4())
+    if bridge_id is None:
+        bridge_id = str(uuid.uuid4())
+
+    return get_bridge_port_mock(id=id, bridge_id=bridge_id,
+                                type='ExteriorBridge')
+
+
+def get_interior_bridge_port_mock(id=None, bridge_id=None):
+    if id is None:
+        id = str(uuid.uuid4())
+    if bridge_id is None:
+        bridge_id = str(uuid.uuid4())
+
+    return get_bridge_port_mock(id=id, bridge_id=bridge_id,
+                                type='InteriorBridge')
+
+
+def get_port_group_mock(id=None, tenant_id='test-tenant', name='pg'):
+    if id is None:
+        id = str(uuid.uuid4())
+
+    port_group = mock.Mock()
+    port_group.get_id.return_value = id
+    port_group.get_tenant_id.return_value = tenant_id
+    port_group.get_name.return_value = name
+    return port_group
+
+
+def get_router_mock(id=None, tenant_id='test-tenant', name='router'):
+    if id is None:
+        id = str(uuid.uuid4())
+
+    router = mock.Mock()
+    router.get_id.return_value = id
+    router.get_tenant_id.return_value = tenant_id
+    router.get_name.return_value = name
+    return router
+
+
+def get_rule_mock(id=None, chain_id=None, properties=None):
+    if id is None:
+        id = str(uuid.uuid4())
+
+    if chain_id is None:
+        chain_id = str(uuid.uuid4())
+
+    if properties is None:
+        properties = {}
+
+    rule = mock.Mock()
+    rule.get_id.return_value = id
+    rule.get_chain_id.return_value = chain_id
+    rule.get_properties.return_value = properties
+    return rule
+
+
+def get_subnet_mock(bridge_id=None, gateway_ip='10.0.0.1',
+                    subnet_prefix='10.0.0.0', subnet_len=int(24)):
+    if bridge_id is None:
+        bridge_id = str(uuid.uuid4())
+
+    subnet = mock.Mock()
+    subnet.get_id.return_value = subnet_prefix + '/' + str(subnet_len)
+    subnet.get_bridge_id.return_value = bridge_id
+    subnet.get_default_gateway.return_value = gateway_ip
+    subnet.get_subnet_prefix.return_value = subnet_prefix
+    subnet.get_subnet_length.return_value = subnet_len
+    return subnet
+
+
+class MidonetLibMockConfig():
+
+    def __init__(self, inst):
+        self.inst = inst
+
+    def _create_bridge(self, tenant_id, name):
+        return get_bridge_mock(tenant_id=tenant_id, name=name)
+
+    def _create_exterior_bridge_port(self, bridge):
+        return get_exterior_bridge_port_mock(bridge_id=bridge.get_id())
+
+    def _create_interior_bridge_port(self, bridge):
+        return get_interior_bridge_port_mock(bridge_id=bridge.get_id())
+
+    def _create_subnet(self, bridge, gateway_ip, subnet_prefix, subnet_len):
+        return get_subnet_mock(bridge.get_id(), gateway_ip=gateway_ip,
+                               subnet_prefix=subnet_prefix,
+                               subnet_len=subnet_len)
+
+    def _get_bridge(self, id):
+        return get_bridge_mock(id=id)
+
+    def _get_port(self, id):
+        return get_exterior_bridge_port_mock(id=id)
+
+    def _get_router(self, id):
+        return get_router_mock(id=id)
+
+    def _update_bridge(self, id, name):
+        return get_bridge_mock(id=id, name=name)
+
+    def setup(self):
+        # Bridge methods side effects
+        self.inst.create_bridge.side_effect = self._create_bridge
+        self.inst.get_bridge.side_effect = self._get_bridge
+        self.inst.update_bridge.side_effect = self._update_bridge
+
+        # Subnet methods side effects
+        self.inst.create_subnet.side_effect = self._create_subnet
+
+        # Port methods side effects
+        ex_bp = self.inst.create_exterior_bridge_port
+        ex_bp.side_effect = self._create_exterior_bridge_port
+        in_bp = self.inst.create_interior_bridge_port
+        in_bp.side_effect = self._create_interior_bridge_port
+        self.inst.get_port.side_effect = self._get_port
+
+        # Router methods side effects
+        self.inst.get_router.side_effect = self._get_router
+
+
+class MidoClientMockConfig():
+
+    def __init__(self, inst):
+        self.inst = inst
+        self.chains_in = None
+        self.port_groups_in = None
+        self.chains_out = None
+        self.rules_out = None
+        self.port_groups_out = None
+
+    def _get_query_tenant_id(self, query):
+        if query is not None and query['tenant_id']:
+            tenant_id = query['tenant_id']
+        else:
+            tenant_id = 'test-tenant'
+        return tenant_id
+
+    def _get_bridge(self, id):
+        return get_bridge_mock(id=id)
+
+    def _get_chains(self, query=None):
+        if not self.chains_in:
+            return []
+
+        tenant_id = self._get_query_tenant_id(query)
+        self.chains_out = []
+        self.rules_out = []
+        for chain in self.chains_in:
+            chain_id = chain['id']
+
+            rule_mocks = []
+            if 'rules' in chain:
+                for rule in chain['rules']:
+                    rule_mocks.append(
+                        get_rule_mock(id=rule['id'],
+                                      chain_id=id,
+                                      properties=rule['properties']))
+                    self.rules_out += rule_mocks
+
+            self.chains_out.append(get_chain_mock(id=chain_id,
+                                                  name=chain['name'],
+                                                  tenant_id=tenant_id,
+                                                  rules=rule_mocks))
+        return self.chains_out
+
+    def _get_port_groups(self, query=None):
+        if not self.port_groups_in:
+            return []
+
+        tenant_id = self._get_query_tenant_id(query)
+        self.port_groups_out = []
+        for port_group in self.port_groups_in:
+            self.port_groups_out.append(get_port_group_mock(
+                id=port_group['id'], name=port_group['name'],
+                tenant_id=tenant_id))
+        return self.port_groups_out
+
+    def _get_router(self, id):
+        return get_router_mock(id=id)
+
+    def setup(self):
+        self.inst.get_bridge.side_effect = self._get_bridge
+        self.inst.get_chains.side_effect = self._get_chains
+        self.inst.get_port_groups.side_effect = self._get_port_groups
+        self.inst.get_router.side_effect = self._get_router
index cd8dc95fb83474c101d50ce2407c9b26216c65df..17e3534669200b17964380a88d0fde6708e3e858 100644 (file)
 # @author: Ryu Ishimoto, Midokura Japan KK
 # @author: Tomoe Sugihara, Midokura Japan KK
 
-import uuid
 
 import mock
+import testtools
+import webob.exc as w_exc
 
+from quantum.openstack.common import uuidutils
 from quantum.plugins.midonet import midonet_lib
-from quantum.tests import base
+import quantum.tests.unit.midonet.mock_lib as mock_lib
 
 
-class MidonetLibTestCase(base.BaseTestCase):
+def _create_test_chain(id, name, tenant_id):
+    return {'id': id, 'name': name, 'tenant_id': tenant_id}
 
-    def setUp(self):
-        super(MidonetLibTestCase, self).setUp()
-        self.mock_api = mock.Mock()
 
-    def _create_mock_chains(self, sg_id, sg_name):
-        mock_in_chain = mock.Mock()
-        mock_in_chain.get_name.return_value = "OS_SG_%s_%s_IN" % (sg_id,
-                                                                  sg_name)
-        mock_out_chain = mock.Mock()
-        mock_out_chain.get_name.return_value = "OS_SG_%s_%s_OUT" % (sg_id,
-                                                                    sg_name)
-        return (mock_in_chain, mock_out_chain)
+def _create_test_port_group(sg_id, sg_name, id, tenant_id):
+    return {"id": id, "name": "OS_SG_%s_%s" % (sg_id, sg_name),
+            "tenant_id": tenant_id}
 
-    def _create_mock_router_chains(self, router_id):
-        mock_in_chain = mock.Mock()
-        mock_in_chain.get_name.return_value = "OS_ROUTER_IN_%s" % (router_id)
 
-        mock_out_chain = mock.Mock()
-        mock_out_chain.get_name.return_value = "OS_ROUTER_OUT_%s" % (router_id)
-        return (mock_in_chain, mock_out_chain)
+def _create_test_router_in_chain(router_id, id, tenant_id):
+    name = "OS_ROUTER_IN_%s" % router_id
+    return _create_test_chain(id, name, tenant_id)
 
-    def _create_mock_port_group(self, sg_id, sg_name):
-        mock_pg = mock.Mock()
-        mock_pg.get_name.return_value = "OS_SG_%s_%s" % (sg_id, sg_name)
-        return mock_pg
 
-    def _create_mock_rule(self, rule_id):
-        mock_rule = mock.Mock()
-        mock_rule.get_properties.return_value = {"os_sg_rule_id": rule_id}
-        return mock_rule
+def _create_test_router_out_chain(router_id, id, tenant_id):
+    name = "OS_ROUTER_OUT_%s" % router_id
+    return _create_test_chain(id, name, tenant_id)
 
 
-class MidonetChainManagerTestCase(MidonetLibTestCase):
+def _create_test_rule(id, chain_id, properties):
+    return {"id": id, "chain_id": chain_id, "properties": properties}
 
-    def setUp(self):
-        super(MidonetChainManagerTestCase, self).setUp()
-        self.mgr = midonet_lib.ChainManager(self.mock_api)
 
-    def test_create_for_sg(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        sg_name = 'test_sg_name'
-        calls = [mock.call.add_chain().tenant_id(tenant_id)]
+def _create_test_sg_in_chain(sg_id, sg_name, id, tenant_id):
+    if sg_name:
+        name = "OS_SG_%s_%s_IN" % (sg_id, sg_name)
+    else:
+        name = "OS_SG_%s_IN" % sg_id
+    return _create_test_chain(id, name, tenant_id)
 
-        self.mgr.create_for_sg(tenant_id, sg_id, sg_name)
 
-        self.mock_api.assert_has_calls(calls)
+def _create_test_sg_out_chain(sg_id, sg_name, id, tenant_id):
+    if sg_name:
+        name = "OS_SG_%s_%s_OUT" % (sg_id, sg_name)
+    else:
+        name = "OS_SG_%s_OUT" % sg_id
+    return _create_test_chain(id, name, tenant_id)
 
-    def test_delete_for_sg(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        sg_name = 'test_sg_name'
-        in_chain, out_chain = self._create_mock_chains(sg_id, sg_name)
 
-        # Mock get_chains returned values
-        self.mock_api.get_chains.return_value = [in_chain, out_chain]
+def _create_test_sg_rule(tenant_id, sg_id, id,
+                         direction="egress", protocol="tcp", port_min=1,
+                         port_max=65535, src_ip='192.168.1.0/24',
+                         src_group_id=None, ethertype=0x0800, properties=None):
+    return {"tenant_id": tenant_id, "security_group_id": sg_id,
+            "id": id, "direction": direction, "protocol": protocol,
+            "remote_ip_prefix": src_ip, "remote_group_id": src_group_id,
+            "port_range_min": port_min, "port_range_max": port_max,
+            "ethertype": ethertype, "external_id": None}
 
-        self.mgr.delete_for_sg(tenant_id, sg_id, sg_name)
 
-        self.mock_api.assert_has_calls(mock.call.get_chains(
-            {"tenant_id": tenant_id}))
-        in_chain.delete.assert_called_once_with()
-        out_chain.delete.assert_called_once_with()
+def _create_test_sg_chain_rule(id, chain_id, sg_rule_id):
+    props = {"os_sg_rule_id": sg_rule_id}
+    return _create_test_rule(id, chain_id, props)
 
-    def test_get_router_chains(self):
-        tenant_id = 'test_tenant'
-        router_id = str(uuid.uuid4())
-        in_chain, out_chain = self._create_mock_router_chains(router_id)
 
-        # Mock get_chains returned values
-        self.mock_api.get_chains.return_value = [in_chain, out_chain]
+class MidoClientTestCase(testtools.TestCase):
 
-        chains = self.mgr.get_router_chains(tenant_id, router_id)
+    def setUp(self):
+        super(MidoClientTestCase, self).setUp()
+        self._tenant_id = 'test-tenant'
+        self.mock_api = mock.Mock()
+        self.mock_api_cfg = mock_lib.MidoClientMockConfig(self.mock_api)
+        self.mock_api_cfg.setup()
+        self.client = midonet_lib.MidoClient(self.mock_api)
 
-        self.mock_api.assert_has_calls(mock.call.get_chains(
-            {"tenant_id": tenant_id}))
-        self.assertEqual(len(chains), 2)
-        self.assertEqual(chains['in'], in_chain)
-        self.assertEqual(chains['out'], out_chain)
+    def test_create_for_sg(self):
+        sg_id = uuidutils.generate_uuid()
+        sg_name = 'test-sg'
+        calls = [mock.call.add_chain().tenant_id(self._tenant_id),
+                 mock.call.add_port_group().tenant_id(self._tenant_id)]
 
-    def test_create_router_chains(self):
-        tenant_id = 'test_tenant'
-        router_id = str(uuid.uuid4())
-        calls = [mock.call.add_chain().tenant_id(tenant_id)]
+        self.client.create_for_sg(self._tenant_id, sg_id, sg_name)
 
-        self.mgr.create_router_chains(tenant_id, router_id)
+        self.mock_api.assert_has_calls(calls, any_order=True)
 
-        self.mock_api.assert_has_calls(calls)
+    def test_create_for_sg_rule(self):
+        sg_id = uuidutils.generate_uuid()
+        sg_name = 'test-sg'
+        in_chain_id = uuidutils.generate_uuid()
+        out_chain_id = uuidutils.generate_uuid()
+        self.mock_api_cfg.chains_in = [
+            _create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
+                                     self._tenant_id),
+            _create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
+                                      self._tenant_id)]
+
+        sg_rule_id = uuidutils.generate_uuid()
+        sg_rule = _create_test_sg_rule(self._tenant_id, sg_id, sg_rule_id)
+
+        props = {"os_sg_rule_id": sg_rule_id}
+        calls = [mock.call.add_rule().port_group(None).type(
+            'accept').nw_proto(6).nw_src_address(
+                '192.168.1.0').nw_src_length(24).tp_src_start(
+                    None).tp_src_end(None).tp_dst_start(1).tp_dst_end(
+                        65535).properties(props).create()]
 
-    def test_get_sg_chains(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        in_chain, out_chain = self._create_mock_chains(sg_id, 'foo')
+        self.client.create_for_sg_rule(sg_rule)
 
-        # Mock get_chains returned values
-        self.mock_api.get_chains.return_value = [in_chain, out_chain]
+        # Egress chain rule added
+        self.mock_api_cfg.chains_out[0].assert_has_calls(calls)
 
-        chains = self.mgr.get_sg_chains(tenant_id, sg_id)
+    def test_create_router_chains(self):
+        router = mock_lib.get_router_mock(tenant_id=self._tenant_id)
+        api_calls = [mock.call.add_chain().tenant_id(self._tenant_id)]
+        router_calls = [
+            mock.call.inbound_filter_id().outbound_filter_id().update()]
 
-        self.mock_api.assert_has_calls(mock.call.get_chains(
-            {"tenant_id": tenant_id}))
-        self.assertEqual(len(chains), 2)
-        self.assertEqual(chains['in'], in_chain)
-        self.assertEqual(chains['out'], out_chain)
+        self.client.create_router_chains(router)
 
+        self.mock_api.assert_has_calls(api_calls)
+        router.assert_has_calls(router_calls)
 
-class MidonetPortGroupManagerTestCase(MidonetLibTestCase):
+    def test_delete_for_sg(self):
+        sg_id = uuidutils.generate_uuid()
+        sg_name = 'test-sg'
+        in_chain_id = uuidutils.generate_uuid()
+        out_chain_id = uuidutils.generate_uuid()
+        pg_id = uuidutils.generate_uuid()
+        self.mock_api_cfg.chains_in = [
+            _create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
+                                     self._tenant_id),
+            _create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
+                                      self._tenant_id)]
+        self.mock_api_cfg.port_groups_in = [
+            _create_test_port_group(sg_id, sg_name, pg_id, self._tenant_id)]
+
+        calls = [mock.call.get_chains({"tenant_id": self._tenant_id}),
+                 mock.call.delete_chain(in_chain_id),
+                 mock.call.delete_chain(out_chain_id),
+                 mock.call.get_port_groups({"tenant_id": self._tenant_id}),
+                 mock.call.delete_port_group(pg_id)]
+
+        self.client.delete_for_sg(self._tenant_id, sg_id, sg_name)
 
-    def setUp(self):
-        super(MidonetPortGroupManagerTestCase, self).setUp()
-        self.mgr = midonet_lib.PortGroupManager(self.mock_api)
+        self.mock_api.assert_has_calls(calls)
 
-    def test_create(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        sg_name = 'test_sg'
-        pg_mock = self._create_mock_port_group(sg_id, sg_name)
-        rv = self.mock_api.add_port_group.return_value.tenant_id.return_value
-        rv.name.return_value = pg_mock
+    def test_delete_for_sg_rule(self):
+        sg_id = uuidutils.generate_uuid()
+        sg_name = 'test-sg'
+        in_chain_id = uuidutils.generate_uuid()
+        out_chain_id = uuidutils.generate_uuid()
+        self.mock_api_cfg.chains_in = [
+            _create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
+                                     self._tenant_id),
+            _create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
+                                      self._tenant_id)]
 
-        self.mgr.create(tenant_id, sg_id, sg_name)
+        rule_id = uuidutils.generate_uuid()
+        sg_rule_id = uuidutils.generate_uuid()
+        rule = _create_test_sg_chain_rule(rule_id, in_chain_id, sg_rule_id)
+        self.mock_api_cfg.chains_in[0]['rules'] = [rule]
+        sg_rule = _create_test_sg_rule(self._tenant_id, sg_id, sg_rule_id)
 
-        pg_mock.create.assert_called_once_with()
+        self.client.delete_for_sg_rule(sg_rule)
 
-    def test_delete(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        sg_name = 'test_sg'
-        pg_mock1 = self._create_mock_port_group(sg_id, sg_name)
-        pg_mock2 = self._create_mock_port_group(sg_id, sg_name)
-        self.mock_api.get_port_groups.return_value = [pg_mock1, pg_mock2]
+        self.mock_api.delete_rule.assert_called_once_with(rule_id)
 
-        self.mgr.delete(tenant_id, sg_id, sg_name)
+    def test_get_bridge(self):
+        bridge_id = uuidutils.generate_uuid()
 
-        self.mock_api.assert_has_calls(mock.call.get_port_groups(
-            {"tenant_id": tenant_id}))
-        pg_mock1.delete.assert_called_once_with()
-        pg_mock2.delete.assert_called_once_with()
+        bridge = self.client.get_bridge(bridge_id)
 
-    def test_get_for_sg(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        pg_mock = self._create_mock_port_group(sg_id, 'foo')
-        self.mock_api.get_port_groups.return_value = [pg_mock]
+        self.assertIsNotNone(bridge)
+        self.assertEqual(bridge.get_id(), bridge_id)
 
-        pg = self.mgr.get_for_sg(tenant_id, sg_id)
+    def test_get_bridge_error(self):
+        self.mock_api.get_bridge.side_effect = w_exc.HTTPInternalServerError()
+        self.assertRaises(midonet_lib.MidonetApiException,
+                          self.client.get_bridge, uuidutils.generate_uuid())
 
-        self.assertEqual(pg, pg_mock)
+    def test_get_bridge_not_found(self):
+        self.mock_api.get_bridge.side_effect = w_exc.HTTPNotFound()
+        self.assertRaises(midonet_lib.MidonetResourceNotFound,
+                          self.client.get_bridge, uuidutils.generate_uuid())
 
+    def test_get_port_groups_for_sg(self):
+        sg_id = uuidutils.generate_uuid()
+        pg_id = uuidutils.generate_uuid()
+        self.mock_api_cfg.port_groups_in = [
+            _create_test_port_group(sg_id, 'test-sg', pg_id, self._tenant_id)]
 
-class MidonetRuleManagerTestCase(MidonetLibTestCase):
+        pg = self.client.get_port_groups_for_sg(self._tenant_id, sg_id)
 
-    def setUp(self):
-        super(MidonetRuleManagerTestCase, self).setUp()
-        self.mgr = midonet_lib.RuleManager(self.mock_api)
-        self.mgr.chain_manager = mock.Mock()
-        self.mgr.pg_manager = mock.Mock()
+        self.assertIsNotNone(pg)
+        self.assertEqual(pg.get_id(), pg_id)
 
     def _create_test_rule(self, tenant_id, sg_id, rule_id, direction="egress",
                           protocol="tcp", port_min=1, port_max=65535,
@@ -198,41 +226,53 @@ class MidonetRuleManagerTestCase(MidonetLibTestCase):
                 "port_range_min": port_min, "port_range_max": port_max,
                 "ethertype": ethertype, "id": rule_id, "external_id": None}
 
-    def test_create_for_sg_rule(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        rule_id = str(uuid.uuid4())
-        in_chain, out_chain = self._create_mock_chains(sg_id, 'foo')
-        self.mgr.chain_manager.get_sg_chains.return_value = {"in": in_chain,
-                                                             "out": out_chain}
-        props = {"os_sg_rule_id": rule_id}
-        rule = self._create_test_rule(tenant_id, sg_id, rule_id)
-        calls = [mock.call.add_rule().port_group(None).type(
-            'accept').nw_proto(6).nw_src_address(
-                '192.168.1.0').nw_src_length(24).tp_src_start(
-                    None).tp_src_end(None).tp_dst_start(1).tp_dst_end(
-                        65535).properties(props).create()]
+    def test_get_router_error(self):
+        self.mock_api.get_router.side_effect = w_exc.HTTPInternalServerError()
+        self.assertRaises(midonet_lib.MidonetApiException,
+                          self.client.get_router, uuidutils.generate_uuid())
 
-        self.mgr.create_for_sg_rule(rule)
+    def test_get_router_not_found(self):
+        self.mock_api.get_router.side_effect = w_exc.HTTPNotFound()
+        self.assertRaises(midonet_lib.MidonetResourceNotFound,
+                          self.client.get_router, uuidutils.generate_uuid())
 
-        in_chain.assert_has_calls(calls)
+    def test_get_router_chains(self):
+        router_id = uuidutils.generate_uuid()
+        in_chain_id = uuidutils.generate_uuid()
+        out_chain_id = uuidutils.generate_uuid()
+        self.mock_api_cfg.chains_in = [
+            _create_test_router_in_chain(router_id, in_chain_id,
+                                         self._tenant_id),
+            _create_test_router_out_chain(router_id, out_chain_id,
+                                          self._tenant_id)]
 
-    def test_delete_for_sg_rule(self):
-        tenant_id = 'test_tenant'
-        sg_id = str(uuid.uuid4())
-        rule_id = str(uuid.uuid4())
-        in_chain, out_chain = self._create_mock_chains(sg_id, 'foo')
-        self.mgr.chain_manager.get_sg_chains.return_value = {"in": in_chain,
-                                                             "out": out_chain}
-
-        # Mock the rules returned for each chain
-        mock_rule_in = self._create_mock_rule(rule_id)
-        mock_rule_out = self._create_mock_rule(rule_id)
-        in_chain.get_rules.return_value = [mock_rule_in]
-        out_chain.get_rules.return_value = [mock_rule_out]
-
-        rule = self._create_test_rule(tenant_id, sg_id, rule_id)
-        self.mgr.delete_for_sg_rule(rule)
-
-        mock_rule_in.delete.assert_called_once_with()
-        mock_rule_out.delete.assert_called_once_with()
+        chains = self.client.get_router_chains(self._tenant_id, router_id)
+
+        self.mock_api.assert_has_calls(mock.call.get_chains(
+            {"tenant_id": self._tenant_id}))
+        self.assertEqual(len(chains), 2)
+        self.assertIn('in', chains)
+        self.assertIn('out', chains)
+        self.assertEqual(chains['in'].get_id(), in_chain_id)
+        self.assertEqual(chains['out'].get_id(), out_chain_id)
+
+    def test_get_sg_chains(self):
+        sg_id = uuidutils.generate_uuid()
+        sg_name = 'test-sg'
+        in_chain_id = uuidutils.generate_uuid()
+        out_chain_id = uuidutils.generate_uuid()
+        self.mock_api_cfg.chains_in = [
+            _create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
+                                     self._tenant_id),
+            _create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
+                                      self._tenant_id)]
+
+        chains = self.client.get_sg_chains(self._tenant_id, sg_id)
+
+        self.mock_api.assert_has_calls(mock.call.get_chains(
+            {"tenant_id": self._tenant_id}))
+        self.assertEqual(len(chains), 2)
+        self.assertIn('in', chains)
+        self.assertIn('out', chains)
+        self.assertEqual(chains['in'].get_id(), in_chain_id)
+        self.assertEqual(chains['out'].get_id(), out_chain_id)
index 424beadb13ead429d81c6cc94c7c320dd45f17bd..b33782b145d4176a6cc00994b789bf42b8bfaf9d 100644 (file)
 # @author: Ryu Ishimoto, Midokura Japan KK
 # @author: Tomoe Sugihara, Midokura Japan KK
 
-import sys
-import uuid
-
 import mock
+import os
+import sys
 
+import quantum.common.test_lib as test_lib
+import quantum.tests.unit.midonet.mock_lib as mock_lib
 import quantum.tests.unit.test_db_plugin as test_plugin
 
 
 MIDOKURA_PKG_PATH = "quantum.plugins.midonet.plugin"
 
+
 # Need to mock the midonetclient module since the plugin will try to load it.
 sys.modules["midonetclient"] = mock.Mock()
 
@@ -39,730 +41,83 @@ class MidonetPluginV2TestCase(test_plugin.QuantumDbPluginV2TestCase):
     _plugin_name = ('%s.MidonetPluginV2' % MIDOKURA_PKG_PATH)
 
     def setUp(self):
-        self.mock_api = mock.patch('midonetclient.api.MidonetApi')
+        self.mock_api = mock.patch(
+            'quantum.plugins.midonet.midonet_lib.MidoClient')
+        etc_path = os.path.join(os.path.dirname(__file__), 'etc')
+        test_lib.test_config['config_files'] = [os.path.join(
+            etc_path, 'midonet.ini.test')]
+
         self.instance = self.mock_api.start()
+        mock_cfg = mock_lib.MidonetLibMockConfig(self.instance.return_value)
+        mock_cfg.setup()
         super(MidonetPluginV2TestCase, self).setUp(self._plugin_name)
 
     def tearDown(self):
         super(MidonetPluginV2TestCase, self).tearDown()
         self.mock_api.stop()
 
-    def _setup_bridge_mock(self, bridge_id=str(uuid.uuid4()), name='net'):
-        # Set up mocks needed for the parent network() method
-        bridge = mock.Mock()
-        bridge.get_id.return_value = bridge_id
-        bridge.get_name.return_value = name
-
-        self.instance.return_value.add_bridge.return_value.name.return_value\
-            .tenant_id.return_value.create.return_value = bridge
-        self.instance.return_value.get_bridges.return_value = [bridge]
-        self.instance.return_value.get_bridge.return_value = bridge
-        return bridge
-
-    def _setup_subnet_mocks(self, subnet_id=str(uuid.uuid4()),
-                            subnet_prefix='10.0.0.0', subnet_len=int(24)):
-        # Set up mocks needed for the parent subnet() method
-        bridge = self._setup_bridge_mock()
-        subnet = mock.Mock()
-        subnet.get_subnet_prefix.return_value = subnet_prefix
-        subnet.get_subnet_length.return_value = subnet_len
-        subnet.get_id.return_value = subnet_prefix + '/' + str(subnet_len)
-        bridge.add_dhcp_subnet.return_value.default_gateway\
-            .return_value.subnet_prefix.return_value.subnet_length\
-            .return_value.create.return_value = subnet
-        bridge.get_dhcp_subnets.return_value = [subnet]
-        return (bridge, subnet)
-
-    def _setup_port_mocks(self, port_id=str(uuid.uuid4())):
-        # Set up mocks needed for the parent port() method
-        bridge, subnet = self._setup_subnet_mocks()
-        port = mock.Mock()
-        port.get_id.return_value = port_id
-        self.instance.return_value.create_port.return_value = port
-        self.instance.return_value.get_port.return_value = port
-        bridge.add_exterior_port.return_value.create.return_value = (
-            port
-        )
-
-        dhcp_host = mock.Mock()
-        rv1 = subnet.add_dhcp_host.return_value.ip_addr.return_value
-        rv1.mac_addr.return_value.create.return_value = dhcp_host
-
-        subnet.get_dhcp_hosts.return_value = [dhcp_host]
-        return (bridge, subnet, port, dhcp_host)
-
 
 class TestMidonetNetworksV2(test_plugin.TestNetworksV2,
                             MidonetPluginV2TestCase):
-
-    def test_create_network(self):
-        self._setup_bridge_mock()
-        super(TestMidonetNetworksV2, self).test_create_network()
-
-    def test_create_public_network(self):
-        self._setup_bridge_mock()
-        super(TestMidonetNetworksV2, self).test_create_public_network()
-
-    def test_create_public_network_no_admin_tenant(self):
-        self._setup_bridge_mock()
-        super(TestMidonetNetworksV2,
-              self).test_create_public_network_no_admin_tenant()
-
-    def test_update_network(self):
-        self._setup_bridge_mock()
-        super(TestMidonetNetworksV2, self).test_update_network()
-
-    def test_list_networks(self):
-        self._setup_bridge_mock()
-        with self.network(name='net1') as net1:
-            req = self.new_list_request('networks')
-            res = self.deserialize('json', req.get_response(self.api))
-            self.assertEqual(res['networks'][0]['name'],
-                             net1['network']['name'])
-
-    def test_show_network(self):
-        self._setup_bridge_mock()
-        super(TestMidonetNetworksV2, self).test_show_network()
-
-    def test_update_shared_network_noadmin_returns_403(self):
-        self._setup_bridge_mock()
-        super(TestMidonetNetworksV2,
-              self).test_update_shared_network_noadmin_returns_403()
-
-    def test_update_network_set_shared(self):
-        pass
-
-    def test_update_network_with_subnet_set_shared(self):
-        pass
-
-    def test_update_network_set_not_shared_single_tenant(self):
-        pass
-
-    def test_update_network_set_not_shared_other_tenant_returns_409(self):
-        pass
-
-    def test_update_network_set_not_shared_multi_tenants_returns_409(self):
-        pass
-
-    def test_update_network_set_not_shared_multi_tenants2_returns_409(self):
-        pass
-
-    def test_create_networks_bulk_native(self):
-        pass
-
-    def test_create_networks_bulk_native_quotas(self):
-        pass
-
-    def test_create_networks_bulk_tenants_and_quotas(self):
-        pass
-
-    def test_create_networks_bulk_tenants_and_quotas_fail(self):
-        pass
-
-    def test_create_networks_bulk_emulated(self):
-        pass
-
-    def test_create_networks_bulk_wrong_input(self):
-        pass
-
-    def test_create_networks_bulk_emulated_plugin_failure(self):
-        pass
-
-    def test_create_networks_bulk_native_plugin_failure(self):
-        pass
-
-    def test_list_networks_with_parameters(self):
-        pass
-
-    def test_list_networks_with_fields(self):
-        pass
-
-    def test_list_networks_with_parameters_invalid_values(self):
-        pass
-
-    def test_list_shared_networks_with_non_admin_user(self):
-        pass
-
-    def test_show_network_with_subnet(self):
-        pass
-
-    def test_invalid_admin_status(self):
-        pass
-
-    def test_list_networks_with_pagination_emulated(self):
-        pass
-
-    def test_list_networks_with_pagination_reverse_emulated(self):
-        pass
-
-    def test_list_networks_with_sort_emulated(self):
-        pass
-
-    def test_list_networks_without_pk_in_fields_pagination_emulated(self):
-        pass
+    pass
 
 
 class TestMidonetSubnetsV2(test_plugin.TestSubnetsV2,
                            MidonetPluginV2TestCase):
 
-    def test_create_subnet(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet()
-
-    def test_create_two_subnets(self):
-        pass
-
-    def test_create_two_subnets_same_cidr_returns_400(self):
-        pass
-
-    def test_create_subnet_bad_V4_cidr(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_V4_cidr()
-
-    def test_create_subnet_bad_V6_cidr(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_V4_cidr()
-
-    def test_create_2_subnets_overlapping_cidr_allowed_returns_200(self):
-        pass
-
-    def test_create_2_subnets_overlapping_cidr_not_allowed_returns_400(self):
-        pass
-
-    def test_create_subnets_bulk_native(self):
-        pass
-
-    def test_create_subnets_bulk_emulated(self):
-        pass
-
-    def test_create_subnets_bulk_emulated_plugin_failure(self):
-        pass
-
-    def test_create_subnets_bulk_native_plugin_failure(self):
-        pass
-
-    def test_delete_subnet(self):
-        _bridge, subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_delete_subnet()
-        subnet.delete.assert_called_once_with()
-
-    def test_delete_subnet_port_exists_owned_by_network(self):
-        _bridge, subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_delete_subnet_port_exists_owned_by_network()
-
-    def test_delete_subnet_port_exists_owned_by_other(self):
-        pass
-
-    def test_delete_network(self):
-        bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_delete_network()
-        bridge.delete.assert_called_once_with()
-
-    def test_create_subnet_bad_tenant(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_tenant()
-
-    def test_create_subnet_bad_ip_version(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_ip_version()
-
-    def test_create_subnet_bad_ip_version_null(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_bad_ip_version_null()
-
-    def test_create_subnet_bad_uuid(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_uuid()
-
-    def test_create_subnet_bad_boolean(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_boolean()
-
-    def test_create_subnet_bad_pools(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_pools()
-
-    def test_create_subnet_bad_nameserver(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_nameserver()
-
-    def test_create_subnet_bad_hostroutes(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_bad_hostroutes()
-
-    def test_create_subnet_defaults(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_defaults()
-
-    def test_create_subnet_gw_values(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_gw_values()
-
-    def test_create_subnet_gw_outside_cidr_force_on_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_gw_outside_cidr_force_on_returns_400()
-
-    def test_create_subnet_gw_of_network_force_on_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_gw_of_network_force_on_returns_400()
-
-    def test_create_subnet_gw_bcast_force_on_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_gw_bcast_force_on_returns_400()
-
-    def test_create_subnet_with_allocation_pool(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_allocation_pool()
-
-    def test_create_subnet_with_none_gateway(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_none_gateway()
-
-    def test_create_subnet_with_none_gateway_fully_allocated(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_none_gateway_fully_allocated()
-
-    def test_subnet_with_allocation_range(self):
-        pass
-
-    def test_create_subnet_with_none_gateway_allocation_pool(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_none_gateway_allocation_pool()
-
-    def test_create_subnet_with_v6_allocation_pool(self):
-        pass
-
-    def test_create_subnet_with_large_allocation_pool(self):
-        pass
-
-    def test_create_subnet_multiple_allocation_pools(self):
-        pass
-
-    def test_create_subnet_with_dhcp_disabled(self):
-        pass
-
-    def test_create_subnet_default_gw_conflict_allocation_pool_returns_409(
-        self):
-        pass
-
-    def test_create_subnet_gateway_in_allocation_pool_returns_409(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self)\
-            .test_create_subnet_gateway_in_allocation_pool_returns_409()
-
-    def test_create_subnet_overlapping_allocation_pools_returns_409(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self)\
-            .test_create_subnet_overlapping_allocation_pools_returns_409()
-
-    def test_create_subnet_invalid_allocation_pool_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_invalid_allocation_pool_returns_400()
-
-    def test_create_subnet_out_of_range_allocation_pool_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self)\
-            .test_create_subnet_out_of_range_allocation_pool_returns_400()
-
-    def test_create_subnet_shared_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_shared_returns_400()
-
+    # IPv6 is not supported by MidoNet yet.  Ignore tests that attempt to
+    # create IPv6 subnet.
     def test_create_subnet_inconsistent_ipv6_cidrv4(self):
         pass
 
-    def test_create_subnet_inconsistent_ipv4_cidrv6(self):
-        pass
-
-    def test_create_subnet_inconsistent_ipv4_gatewayv6(self):
-        pass
-
-    def test_create_subnet_inconsistent_ipv6_gatewayv4(self):
-        pass
-
     def test_create_subnet_inconsistent_ipv6_dns_v4(self):
         pass
 
-    def test_create_subnet_inconsistent_ipv4_hostroute_dst_v6(self):
-        pass
-
-    def test_create_subnet_inconsistent_ipv4_hostroute_np_v6(self):
-        pass
-
-    def test_update_subnet(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_update_subnet()
-
-    def test_update_subnet_shared_returns_400(self):
-        self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_update_subnet_shared_returns_400()
-
-    def test_update_subnet_gw_outside_cidr_force_on_returns_400(self):
-        pass
-
-    def test_update_subnet_inconsistent_ipv4_gatewayv6(self):
+    def test_create_subnet_with_v6_allocation_pool(self):
         pass
 
     def test_update_subnet_inconsistent_ipv6_gatewayv4(self):
         pass
 
-    def test_update_subnet_inconsistent_ipv4_dns_v6(self):
-        pass
-
     def test_update_subnet_inconsistent_ipv6_hostroute_dst_v4(self):
         pass
 
     def test_update_subnet_inconsistent_ipv6_hostroute_np_v4(self):
         pass
 
-    def test_show_subnet(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_show_subnet()
-
-    def test_list_subnets(self):
+    def test_create_subnet_inconsistent_ipv6_gatewayv4(self):
         pass
 
-    def test_list_subnets_shared(self):
+    # Multiple subnets in a network is not supported by MidoNet yet.  Ignore
+    # tests that attempt to create them.
+
+    def test_create_subnets_bulk_emulated(self):
         pass
 
-    def test_list_subnets_with_parameter(self):
+    def test_create_two_subnets(self):
         pass
 
-    def test_invalid_ip_version(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_invalid_ip_version()
-
-    def test_invalid_subnet(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_invalid_subnet()
-
-    def test_invalid_ip_address(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_invalid_ip_address()
-
-    def test_invalid_uuid(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_invalid_uuid()
-
-    def test_create_subnet_with_one_dns(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_with_one_dns()
-
-    def test_create_subnet_with_two_dns(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_create_subnet_with_two_dns()
-
-    def test_create_subnet_with_too_many_dns(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_too_many_dns()
-
-    def test_create_subnet_with_one_host_route(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_one_host_route()
-
-    def test_create_subnet_with_two_host_routes(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_two_host_routes()
-
-    def test_create_subnet_with_too_many_routes(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_create_subnet_with_too_many_routes()
-
-    def test_update_subnet_dns(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_update_subnet_dns()
-
-    def test_update_subnet_dns_to_None(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_update_subnet_dns_to_None()
-
-    def test_update_subnet_dns_with_too_many_entries(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_update_subnet_dns_with_too_many_entries()
-
-    def test_update_subnet_route(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_update_subnet_route()
-
-    def test_update_subnet_route_to_None(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_update_subnet_route_to_None()
-
-    def test_update_subnet_route_with_too_many_entries(self):
-        _bridge, _subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_update_subnet_route_with_too_many_entries()
-
-    def test_delete_subnet_with_dns(self):
-        _bridge, subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_delete_subnet_with_dns()
-        subnet.delete.assert_called_once_with()
-
-    def test_delete_subnet_with_route(self):
-        _bridge, subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2, self).test_delete_subnet_with_route()
-        subnet.delete.assert_called_once_with()
-
-    def test_delete_subnet_with_dns_and_route(self):
-        _bridge, subnet = self._setup_subnet_mocks()
-        super(TestMidonetSubnetsV2,
-              self).test_delete_subnet_with_dns_and_route()
-        subnet.delete.assert_called_once_with()
-
-    def test_update_subnet_gateway_in_allocation_pool_returns_409(self):
-        self._setup_port_mocks()
-        super(TestMidonetSubnetsV2, self)\
-            .test_update_subnet_gateway_in_allocation_pool_returns_409()
-
-    def test_list_subnets_with_pagination_emulated(self):
+    def test_list_subnets(self):
         pass
 
-    def test_list_subnets_with_pagination_reverse_emulated(self):
+    def test_list_subnets_with_parameter(self):
         pass
 
-    def test_list_subnets_with_sort_emulated(self):
+    def test_create_two_subnets_same_cidr_returns_400(self):
         pass
 
 
 class TestMidonetPortsV2(test_plugin.TestPortsV2,
                          MidonetPluginV2TestCase):
 
-    def test_create_port_json(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_create_port_json()
-
-    def test_create_port_bad_tenant(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_create_port_bad_tenant()
-
-    def test_create_port_public_network(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_create_port_public_network()
-
-    def test_create_port_public_network_with_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2,
-              self).test_create_port_public_network_with_ip()
-
-    def test_create_ports_bulk_native(self):
-        pass
-
-    def test_create_ports_bulk_emulated(self):
-        pass
-
-    def test_create_ports_bulk_wrong_input(self):
-        pass
-
-    def test_create_ports_bulk_emulated_plugin_failure(self):
-        pass
-
-    def test_create_ports_bulk_native_plugin_failure(self):
-        pass
-
-    def test_list_ports(self):
-        pass
-
-    def test_list_ports_filtered_by_fixed_ip(self):
-        pass
-
-    def test_list_ports_public_network(self):
-        pass
-
-    def test_show_port(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_show_port()
-
-    def test_delete_port(self):
-        _bridge, _subnet, port, _dhcp = self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_delete_port()
-        port.delete.assert_called_once_with()
-
-    def test_delete_port_public_network(self):
-        _bridge, _subnet, port, _dhcp = self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_delete_port_public_network()
-        port.delete.assert_called_once_with()
-
-    def test_update_port(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_update_port()
-
-    def test_update_device_id_null(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_update_device_id_null()
-
-    def test_delete_network_if_port_exists(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_delete_network_if_port_exists()
-
-    def test_delete_network_port_exists_owned_by_network(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2,
-              self).test_delete_network_port_exists_owned_by_network()
-
-    def test_update_port_delete_ip(self):
-        pass
-
-    def test_no_more_port_exception(self):
-        pass
-
-    def test_update_port_update_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_update_port_update_ip()
-
-    def test_update_port_update_ip_address_only(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2,
-              self).test_update_port_update_ip_address_only()
-
-    def test_update_port_update_ips(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_update_port_update_ips()
-
-    def test_update_port_add_additional_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_update_port_add_additional_ip()
-
-    def test_requested_duplicate_mac(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_requested_duplicate_mac()
-
-    def test_mac_generation(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_mac_generation()
-
-    def test_mac_generation_4octet(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_mac_generation_4octet()
-
-    def test_bad_mac_format(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_bad_mac_format()
-
-    def test_mac_exhaustion(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_mac_exhaustion()
-
-    def test_requested_duplicate_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_requested_duplicate_ip()
-
-    def test_requested_subnet_delete(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_requested_subnet_delete()
-
-    def test_requested_subnet_id(self):
-        pass
-
-    def test_requested_subnet_id_not_on_network(self):
-        pass
-
-    def test_overlapping_subnets(self):
-        pass
+    # IPv6 is not supported by MidoNet yet.  Ignore tests that attempt to
+    # create IPv6 subnet.
 
     def test_requested_subnet_id_v4_and_v6(self):
         pass
 
-    def test_range_allocation(self):
-        pass
-
-    def test_requested_invalid_fixed_ips(self):
-        pass
-
-    def test_invalid_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_invalid_ip()
-
-    def test_requested_split(self):
-        pass
-
-    def test_duplicate_ips(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_duplicate_ips()
-
-    def test_fixed_ip_invalid_subnet_id(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_fixed_ip_invalid_subnet_id()
-
-    def test_fixed_ip_invalid_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_fixed_ip_invalid_ip()
-
-    def test_requested_ips_only(self):
-        pass
-
-    def test_recycling(self):
-        pass
-
-    def test_invalid_admin_state(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_invalid_admin_state()
-
-    def test_invalid_mac_address(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_invalid_mac_address()
-
-    def test_default_allocation_expiration(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_default_allocation_expiration()
-
-    def test_update_fixed_ip_lease_expiration(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2,
-              self).test_update_fixed_ip_lease_expiration()
-
-    def test_port_delete_holds_ip(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_port_delete_holds_ip()
-
-    def test_update_fixed_ip_lease_expiration_invalid_address(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2,
-              self).test_update_fixed_ip_lease_expiration_invalid_address()
-
-    def test_hold_ip_address(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_hold_ip_address()
-
-    def test_recycle_held_ip_address(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_recycle_held_ip_address()
-
-    def test_recycle_expired_previously_run_within_context(self):
-        pass
-
-    def test_update_port_not_admin(self):
-        self._setup_port_mocks()
-        super(TestMidonetPortsV2, self).test_update_port_not_admin()
-
-    def test_list_ports_with_pagination_emulated(self):
-        pass
-
-    def test_list_ports_with_pagination_reverse_emulated(self):
-        pass
+    # Multiple subnets in a network is not supported by MidoNet yet.  Ignore
+    # tests that attempt to create them.
 
-    def test_list_ports_with_sort_emulated(self):
-        pass
-
-    def test_max_fixed_ips_exceeded(self):
-        pass
-
-    def test_update_max_fixed_ips_exceeded(self):
-        pass
+    def test_overlapping_subnets(self):
+            pass