You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pr...@apache.org on 2013/04/05 23:03:12 UTC

[17/50] [abbrv] git commit: updated refs/heads/affinity_groups to ca1a794

MidoNet Networking Plugin

- Supports DHCP, Source NAT, Static NAT, Firewall rules, Port Forwarding
- Renamed MidokuraMidonet to MidoNet
- Related Jira ticket is CLOUDSTACK-996

Signed-off-by: Dave Cahill <dc...@midokura.com>
Signed-off-by: Hugo Trippaers <ht...@schubergphilis.com>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/eddf7b93
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/eddf7b93
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/eddf7b93

Branch: refs/heads/affinity_groups
Commit: eddf7b9357bc18497b8cb16a6c6f3382ac52f61c
Parents: d392445
Author: Dave Cahill <dc...@midokura.com>
Authored: Mon Mar 25 10:56:13 2013 +0900
Committer: Hugo Trippaers <ht...@schubergphilis.com>
Committed: Wed Apr 3 12:03:11 2013 +0200

----------------------------------------------------------------------
 api/src/com/cloud/network/Network.java             |    3 +-
 api/src/com/cloud/network/Networks.java            |    1 +
 api/src/com/cloud/network/PhysicalNetwork.java     |    3 +-
 .../network/ExternalNetworkDeviceManager.java      |    1 -
 client/pom.xml                                     |    5 +
 .../kvm/resource/LibvirtDomainXMLParser.java       |    3 +
 .../hypervisor/kvm/resource/LibvirtVMDef.java      |   11 +-
 plugins/network-elements/midokura-midonet/pom.xml  |   30 -
 plugins/network-elements/midonet/pom.xml           |   66 +
 .../com/cloud/network/element/MidoNetElement.java  | 1622 ++++++++++++++-
 .../cloud/network/element/SimpleFirewallRule.java  |  192 ++
 .../network/guru/MidoNetGuestNetworkGuru.java      |  144 ++-
 .../network/guru/MidoNetPublicNetworkGuru.java     |  223 ++
 .../cloud/network/resource/MidoNetVifDriver.java   |  179 ++
 .../cloud/network/element/MidoNetElementTest.java  |  178 ++
 plugins/pom.xml                                    |    1 +
 server/src/com/cloud/configuration/Config.java     |    4 +
 .../src/com/cloud/network/NetworkManagerImpl.java  |   20 +
 ui/scripts/system.js                               |  114 +
 19 files changed, 2720 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/api/src/com/cloud/network/Network.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java
index c2ab655..c0b0117 100644
--- a/api/src/com/cloud/network/Network.java
+++ b/api/src/com/cloud/network/Network.java
@@ -136,8 +136,7 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
         public static final Provider VPCVirtualRouter = new Provider("VpcVirtualRouter", false);
         public static final Provider None = new Provider("None", false);
         // NiciraNvp is not an "External" provider, otherwise we get in trouble with NetworkServiceImpl.providersConfiguredForExternalNetworking 
-        public static final Provider NiciraNvp = new Provider("NiciraNvp", false);  
-        public static final Provider MidokuraMidonet = new Provider("MidokuraMidonet", true);
+        public static final Provider NiciraNvp = new Provider("NiciraNvp", false);
 
         private String name;
         private boolean isExternal;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/api/src/com/cloud/network/Networks.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java
index e3d2158..f085e9f 100755
--- a/api/src/com/cloud/network/Networks.java
+++ b/api/src/com/cloud/network/Networks.java
@@ -62,6 +62,7 @@ public class Networks {
         Vnet("vnet", Long.class),
         Storage("storage", Integer.class),
         Lswitch("lswitch", String.class),
+        Mido("mido", String.class),
         UnDecided(null, null);
 
         private String scheme;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/api/src/com/cloud/network/PhysicalNetwork.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/network/PhysicalNetwork.java b/api/src/com/cloud/network/PhysicalNetwork.java
index 343a2b1..a2044a6 100644
--- a/api/src/com/cloud/network/PhysicalNetwork.java
+++ b/api/src/com/cloud/network/PhysicalNetwork.java
@@ -36,7 +36,8 @@ public interface PhysicalNetwork extends Identity, InternalIdentity {
         L3,
         GRE,
         STT,
-        VNS;
+        VNS,
+        MIDO;
     }
 
     public enum BroadcastDomainRange {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java
index bc22804..aeed81d 100644
--- a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java
+++ b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java
@@ -43,7 +43,6 @@ public interface ExternalNetworkDeviceManager extends Manager {
         public static final NetworkDevice F5BigIpLoadBalancer = new NetworkDevice("F5BigIpLoadBalancer", Network.Provider.F5BigIp.getName());
         public static final NetworkDevice JuniperSRXFirewall = new NetworkDevice("JuniperSRXFirewall", Network.Provider.JuniperSRX.getName());
         public static final NetworkDevice NiciraNvp = new NetworkDevice("NiciraNvp", Network.Provider.NiciraNvp.getName());
-        public static final NetworkDevice MidokuraMidonet = new NetworkDevice("MidokuraMidonet", Network.Provider.MidokuraMidonet.getName());
 
         public NetworkDevice(String deviceName, String ntwkServiceprovider) {
             _name = deviceName;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index 05934f4..ff861b7 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -82,6 +82,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-network-midonet</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
       <artifactId>cloud-plugin-hypervisor-xen</artifactId>
       <version>${project.version}</version>
     </dependency>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
index 403ed37..ac4baf1 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
@@ -111,6 +111,9 @@ public class LibvirtDomainXMLParser {
                     String bridge = getAttrValue("source", "bridge", nic);
                     def.defBridgeNet(bridge, dev, mac,
                             nicModel.valueOf(model.toUpperCase()));
+                } else if (type.equalsIgnoreCase("ethernet"))  {
+                    String scriptPath = getAttrValue("script", "path", nic);
+                    def.defEthernet(dev, mac, nicModel.valueOf(model.toUpperCase()), scriptPath);
                 }
                 interfaces.add(def);
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index 63133a8..9cddb2e 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -711,13 +711,19 @@ public class LibvirtVMDef {
             _model = model;
         }
 
-        public void defEthernet(String targetName, String macAddr,  nicModel model) {
+        public void defEthernet(String targetName, String macAddr, nicModel model, String scriptPath) {
             _netType = guestNetType.ETHERNET;
             _networkName = targetName;
+            _sourceName = targetName;
             _macAddr = macAddr;
             _model = model;
+            _scriptPath = scriptPath;
          }
 
+        public void defEthernet(String targetName, String macAddr, nicModel model) {
+            defEthernet(targetName, macAddr, model, null);
+        }
+
         public void setHostNetType(hostNicType hostNetType) {
             _hostNetType = hostNetType;
         }
@@ -790,6 +796,9 @@ public class LibvirtVMDef {
             if (_model != null) {
                 netBuilder.append("<model type='" + _model + "'/>\n");
             }
+            if (_scriptPath != null) {
+                netBuilder.append("<script path='" + _scriptPath + "'/>\n");
+            }
             if (_virtualPortType != null) {
                 netBuilder.append("<virtualport type='" + _virtualPortType + "'>\n");
                 if (_virtualPortInterfaceId != null) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/plugins/network-elements/midokura-midonet/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/network-elements/midokura-midonet/pom.xml b/plugins/network-elements/midokura-midonet/pom.xml
deleted file mode 100644
index 7f2e2d3..0000000
--- a/plugins/network-elements/midokura-midonet/pom.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements. See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership. The ASF licenses this file
-  to you 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>cloud-plugin-network-midonet</artifactId>
-  <name>Apache CloudStack Plugin - Midokura Midonet</name>
-  <parent>
-    <groupId>org.apache.cloudstack</groupId>
-    <artifactId>cloudstack-plugins</artifactId>
-    <version>4.0.0-SNAPSHOT</version>
-    <relativePath>../../pom.xml</relativePath>
-  </parent>
-</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/plugins/network-elements/midonet/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/network-elements/midonet/pom.xml b/plugins/network-elements/midonet/pom.xml
new file mode 100644
index 0000000..bfd59a9
--- /dev/null
+++ b/plugins/network-elements/midonet/pom.xml
@@ -0,0 +1,66 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-plugin-network-midonet</artifactId>
+  <name>Apache CloudStack Plugin - Midokura Midonet</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack-plugins</artifactId>
+    <version>4.2.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+<repositories>
+    <repository>
+        <id>mido-maven-public-releases</id>
+        <name>mido-maven-public-releases</name>
+        <url>https://googledrive.com/host/0B7iVfAZ_5GmJTk9PUDFNLTl5MVk/releases</url>
+    </repository>
+</repositories>
+  <dependencies>
+    <dependency>
+      <groupId>com.midokura</groupId>
+      <artifactId>midonet-client</artifactId>
+      <version>12.12.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <version>1.9.5</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-hypervisor-kvm</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-core-asl</artifactId>
+        <version>1.9.3</version>
+        <scope>runtime</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-mapper-asl</artifactId>
+        <version>1.9.3</version>
+        <scope>runtime</scope>
+    </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eddf7b93/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java
----------------------------------------------------------------------
diff --git a/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java b/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java
index 48833b3..804e4a6 100644
--- a/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java
+++ b/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java
@@ -19,113 +19,1667 @@
 
 package com.cloud.network.element;
 
+import com.cloud.network.*;
+import com.cloud.network.element.SimpleFirewallRule;
+import com.cloud.agent.api.to.FirewallRuleTO;
+import com.cloud.agent.api.to.NetworkACLTO;
+import com.cloud.agent.api.to.PortForwardingRuleTO;
+import com.cloud.configuration.Config;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.network.dao.NetworkServiceMapDao;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
-import com.cloud.network.PhysicalNetworkServiceProvider;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.rules.StaticNat;
+import com.cloud.network.rules.PortForwardingRule;
+import com.cloud.network.addr.PublicIp;
 import com.cloud.offering.NetworkOffering;
+import com.cloud.utils.Pair;
 import com.cloud.utils.component.AdapterBase;
 import com.cloud.utils.component.PluggableService;
-import com.cloud.vm.NicProfile;
-import com.cloud.vm.ReservationContext;
-import com.cloud.vm.VirtualMachine;
-import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.utils.net.NetUtils;
+import com.cloud.vm.*;
+import com.cloud.vm.dao.NicDao;
+import com.google.common.collect.*;
+import com.cloud.user.AccountManager;
+import com.midokura.midonet.client.MidonetApi;
+import com.midokura.midonet.client.dto.DtoRule;
+import com.midokura.midonet.client.resource.*;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
 import org.apache.log4j.Logger;
+import com.cloud.network.vpc.PrivateGateway;
+import com.cloud.network.vpc.StaticRouteProfile;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcGateway;
+import com.cloud.network.vpc.VpcManager;
 import org.springframework.stereotype.Component;
 
 import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.inject.Inject;
+import java.util.*;
 import java.lang.Class;
 import java.util.Map;
 import java.util.Set;
 
 
 @Component
-@Local(value = NetworkElement.class)
-public class MidokuraMidonetElement extends AdapterBase implements ConnectivityProvider, PluggableService {
-    private static final Logger s_logger = Logger.getLogger(MidokuraMidonetElement.class);
+@Local(value = {NetworkElement.class,
+                ConnectivityProvider.class,
+                FirewallServiceProvider.class,
+                SourceNatServiceProvider.class,
+                DhcpServiceProvider.class,
+                StaticNatServiceProvider.class,
+                PortForwardingServiceProvider.class,
+                IpDeployer.class} )
+public class MidoNetElement extends AdapterBase implements
+                                        ConnectivityProvider,
+                                        DhcpServiceProvider,
+                                        SourceNatServiceProvider,
+                                        StaticNatServiceProvider,
+                                        IpDeployer,
+                                        PortForwardingServiceProvider,
+                                        FirewallServiceProvider,
+                                        PluggableService {
+
+    private static final Logger s_logger = Logger.getLogger(MidoNetElement.class);
+
+    private static final Map<Service, Map<Capability, String>> capabilities = setCapabilities();
+
+    protected UUID _providerRouterId = null;
+
+    protected MidonetApi api;
+
+    private static final Provider MidoNet = new Provider("MidoNet", false);
+
+    public enum RuleChainCode {
+        TR_PRE,
+        TR_PRENAT,
+        TR_PREFILTER,
+        TR_POST,
+        ACL_INGRESS,
+        ACL_EGRESS
+    }
+
+    @Inject
+    ConfigurationDao _configDao;
+    @Inject
+    protected NicDao _nicDao;
+    @Inject
+    NetworkModel _networkModel;
+    @Inject
+    VpcManager _vpcMgr;
+    @Inject
+    AccountManager _accountMgr;
+    @Inject
+    NetworkServiceMapDao _ntwkSrvcDao;
+
+    public void setMidonetApi(MidonetApi api) {
+        this.api = api;
+    }
+
+    public void setNtwkSrvcDao(NetworkServiceMapDao ntwkSrvcDao){
+        this._ntwkSrvcDao = ntwkSrvcDao;
+    }
 
     @Override
     public Map<Service, Map<Capability, String>> getCapabilities() {
-        // TODO: implement this.
-        return null;
+        return capabilities;
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params)
+            throws ConfigurationException {
+        super.configure(name, params);
+
+        String routerIdValue = (String) _configDao.getValue(Config.MidoNetProviderRouterId.key());
+        if(routerIdValue != null)
+            _providerRouterId = UUID.fromString(routerIdValue);
+
+        String value = (String) _configDao.getValue(Config.MidoNetAPIServerAddress.key());
+
+        if (value == null) {
+            throw new ConfigurationException(
+                "Could not find midonet API location in config");
+        }
+
+        if (this.api == null) {
+            s_logger.info("midonet API server address is  " + value);
+            setMidonetApi(new MidonetApi(value));
+            this.api.enableLogging();
+        }
+
+        return true;
+    }
+
+    public boolean midoInNetwork(Network network) {
+        for (String pname : _ntwkSrvcDao.getDistinctProviders(network.getId())) {
+            if (pname.equals(getProvider().getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected boolean canHandle(Network network, Service service) {
+        Long physicalNetworkId = _networkModel.getPhysicalNetworkId(network);
+        if (physicalNetworkId == null) {
+            return false;
+        }
+
+        if (!_networkModel.isProviderEnabledInPhysicalNetwork(physicalNetworkId, getProvider().getName())) {
+            return false;
+        }
+
+        if (service == null) {
+            if (!_networkModel.isProviderForNetwork(getProvider(), network.getId())) {
+                s_logger.trace("Element " + getProvider().getName() + " is not a provider for the network " + network);
+                return false;
+            }
+        } else {
+            if (!_networkModel.isProviderSupportServiceInNetwork(network.getId(), service, getProvider())) {
+                s_logger.trace("Element " + getProvider().getName() + " doesn't support service " + service.getName()
+                        + " in the network " + network);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public void applySourceNat(Router tenantRouter, Router providerRouter,
+                               RouterPort tenantUplink, RouterPort providerDownlink,
+                               RuleChain pre, RuleChain post, PublicIpAddress addr) {
+
+        boolean needAdd = true;
+        String SNtag = "/SourceNat";
+        String SNkey = "CS_nat";
+
+        String snatIp = addr.getAddress().addr();
+
+        String CsDesc = snatIp + SNtag;
+
+        //determine, by use of the properties, if we already
+        //added this rule. If we did, then we can skip it
+        //by setting needAdd = false
+        for (Rule rule : pre.getRules()) {
+            Map<String, String> props = rule.getProperties();
+            if (props == null) {
+                continue;
+            }
+            String snatTag = props.get(SNkey);
+            if (snatTag == null) {
+                continue;
+            }
+
+            if (!snatTag.equals(CsDesc)) {
+                continue;
+            } else {
+                needAdd = false;
+                break;
+            }
+        }
+
+        if (needAdd == false) {
+            //we found that this rule exists already,
+            // so lets skip adding it
+            return;
+        }
+
+        Map<String, String> ruleProps = new HashMap<String, String>();
+        ruleProps.put(SNkey, CsDesc);
+
+        DtoRule.DtoNatTarget[] targets = new DtoRule.DtoNatTarget[]{
+            new DtoRule.DtoNatTarget(snatIp,
+                    snatIp, 1, 65535)};
+
+        // Set inbound (reverse SNAT) rule
+        pre.addRule().type(DtoRule.RevSNAT).flowAction(DtoRule.Accept)
+            .nwDstAddress(snatIp).nwDstLength(32)
+            .inPorts(new UUID[] {tenantUplink.getId()}).position(1)
+            .properties(ruleProps).create();
+
+        // Set outbound (SNAT) rule
+        post.addRule().type(DtoRule.SNAT).flowAction(DtoRule.Accept)
+            .outPorts(new UUID[]{tenantUplink.getId()})
+            .natTargets(targets).position(1)
+            .properties(ruleProps).create();
+
+        // Set up default route from tenant router to provider router
+        tenantRouter.addRoute().type("Normal").weight(100)
+            .srcNetworkAddr("0.0.0.0").srcNetworkLength(0)
+            .dstNetworkAddr("0.0.0.0").dstNetworkLength(0)
+            .nextHopPort(tenantUplink.getId()).create();
+
+        // Set routes for traffic to the SNAT IP to come back from provider router
+        providerRouter.addRoute().type("Normal").weight(100)
+            .srcNetworkAddr("0.0.0.0").srcNetworkLength(0)
+            .dstNetworkAddr(snatIp).dstNetworkLength(32)
+            .nextHopPort(providerDownlink.getId()).create();
+
+        // Default rule to accept traffic that has been DNATed
+        post.addRule().type(DtoRule.RevDNAT).flowAction(DtoRule.Accept).create();
+    }
+
+    public boolean associatePublicIP(Network network, final List<? extends PublicIpAddress> ipAddress)
+        throws ResourceUnavailableException {
+
+        s_logger.debug("associatePublicIP called with network: " + network.toString());
+        /*
+         * Get Mido Router for this network and set source rules
+         * These should only be allocated inside the for loop, because
+         * this function could be called as a part of network cleanup. In
+         * that case, we do not want to recreate the guest network or
+         * any ports.
+         */
+        boolean resources = false;
+        Router tenantRouter = null;
+        Router providerRouter = null;
+        RouterPort[] ports = null;
+
+        RouterPort tenantUplink = null;
+        RouterPort providerDownlink = null;
+
+        RuleChain preNat = null;
+        RuleChain post = null;
+        String accountIdStr = null;
+        String routerName = null;
+
+        // Set Source NAT rules on router
+        for (PublicIpAddress ip : ipAddress) {
+            // ip is the external one we sourcenat to
+            if(ip.isSourceNat()){
+                if (resources == false) {
+                    tenantRouter = getOrCreateGuestNetworkRouter(network);
+                    providerRouter = api.getRouter(_providerRouterId);
+                    ports = getOrCreateProviderRouterPorts(tenantRouter, providerRouter);
+
+                    tenantUplink = ports[0];
+                    providerDownlink = ports[1];
+
+                    accountIdStr = String.valueOf(network.getAccountId());
+                    boolean isVpc = getIsVpc(network);
+                    long id = getRouterId(network, isVpc);
+                    routerName = getRouterName(isVpc, id);
+
+                    preNat = getChain(accountIdStr, routerName, RuleChainCode.TR_PRENAT);
+                    post = api.getChain(tenantRouter.getOutboundFilterId());
+                    resources = true;
+                }
+
+                applySourceNat(tenantRouter, providerRouter,    // Routers
+                               tenantUplink, providerDownlink,  // Ports
+                               preNat, post,                       // Chains
+                               ip);                             // The IP
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * From interface IpDeployer
+     *
+     * @param network
+     * @param ipAddress
+     * @param services
+     * @return
+     * @throws ResourceUnavailableException
+     */
+    @Override
+    public boolean applyIps(Network network,
+                            List<? extends PublicIpAddress> ipAddress, Set<Service> services)
+            throws ResourceUnavailableException {
+
+        s_logger.debug("applyIps called with network: " + network.toString());
+        if (!this.midoInNetwork(network)) {
+            return false;
+        }
+
+        boolean canHandle = true;
+        for (Service service : services) {
+            if (!canHandle(network, service)) {
+                canHandle = false;
+                break;
+            }
+        }
+        if(canHandle) {
+            return associatePublicIP(network, ipAddress);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * From interface SourceNatServiceProvider
+     */
+    @Override
+    public IpDeployer getIpDeployer(Network network) {
+        s_logger.debug("getIpDeployer called with network " + network.toString());
+        return this;
+    }
+
+    /**
+     * From interface DHCPServiceProvider
+     */
+    @Override
+    public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm,
+                                DeployDestination dest, ReservationContext context)
+        throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+
+        s_logger.debug("addDhcpEntry called with network: " + network.toString() + " nic: " + nic.toString() + " vm: " + vm.toString());
+        if (!this.midoInNetwork(network)) {
+            return false;
+        }
+        if (vm.getType() != VirtualMachine.Type.User) {
+            return false;
+        }
+
+        // Get MidoNet bridge
+        Bridge netBridge = getOrCreateNetworkBridge(network);
+
+        // On bridge, get DHCP subnet (ensure it exists)
+        ResourceCollection res = netBridge.getDhcpSubnets();
+
+        DhcpSubnet sub = null;
+
+        if(!res.isEmpty()){
+            sub = (DhcpSubnet) res.get(0);
+        } else {
+            Pair<String,Integer> cidrInfo = NetUtils.getCidr(network.getCidr());
+            sub = netBridge.addDhcpSubnet();
+
+            sub.subnetLength(cidrInfo.second());
+            sub.subnetPrefix(cidrInfo.first());
+            sub.defaultGateway(network.getGateway());
+            sub.dnsServerAddr(dest.getDataCenter().getDns1());
+
+            sub.create();
+        }
+
+        // On DHCP subnet, add host using host details
+        if(sub == null){
+            s_logger.error("Failed to create DHCP subnet on Midonet bridge");
+            return false;
+        } else {
+            // Check if the host already exists - we may just be restarting an existing VM
+            boolean isNewDhcpHost = true;
+
+            for (DhcpHost dhcpHost : sub.getDhcpHosts()) {
+                if(dhcpHost.getIpAddr().equals(nic.getIp4Address())){
+                    isNewDhcpHost = false;
+                    break;
+                }
+            }
+
+            if(isNewDhcpHost){
+                DhcpHost host = sub.addDhcpHost();
+                host.ipAddr(nic.getIp4Address());
+                host.macAddr(nic.getMacAddress());
+                // This only sets the cloudstack internal name
+                host.name(vm.getHostName());
+
+                host.create();
+            }
+        }
+
+        return true;
+    }
+
+    private void removeMidonetStaticNAT(RuleChain preFilter, RuleChain preNat, RuleChain postNat,
+                                        String floatingIp, String fixedIp,
+                                        Router providerRouter) {
+
+        // Delete filter (firewall) rules for this IP
+        for (Rule rule : preFilter.getRules()) {
+            String destAddr = rule.getNwDstAddress();
+            if (destAddr != null && destAddr.equals(floatingIp)) {
+                rule.delete();
+            }
+        }
+
+        // Delete DNAT rules for this IP
+        for (Rule rule : preNat.getRules()) {
+            String destAddr = rule.getNwDstAddress();
+            if (destAddr != null && destAddr.equals(floatingIp)) {
+                rule.delete();
+            }
+        }
+        // Delete SNAT rules for this IP
+        for (Rule rule : postNat.getRules()) {
+            String srcAddr = rule.getNwSrcAddress();
+            if (srcAddr != null && srcAddr.equals(fixedIp)) {
+                rule.delete();
+            }
+        }
+
+        //we also created a route to go with this rule. That needs to be
+        //deleted as well.
+        for (Route route : providerRouter.getRoutes(new MultivaluedMapImpl())) {
+            String routeDstAddr = route.getDstNetworkAddr();
+            if (routeDstAddr != null && routeDstAddr.equals(floatingIp)) {
+                route.delete();
+            }
+        }
+    }
+
+    private void addMidonetStaticNAT(RuleChain preFilter, RuleChain preNat, RuleChain postNat,
+                                     String floatingIp, String fixedIp,
+                                     RouterPort tenantUplink,
+                                     RouterPort providerDownlink,
+                                     Router providerRouter,
+                                     Network network) {
+
+        DtoRule.DtoNatTarget[] preTargets = new DtoRule.DtoNatTarget[]{
+            new DtoRule.DtoNatTarget(fixedIp, fixedIp, 0, 0)};
+
+        // Set inbound filter rule (allow return traffic to this IP)
+        //     Implemented as "jump to NAT chain" instead of ACCEPT;
+        //     this is to enforce that filter / firewall rules are evaluated
+        //     before NAT rules.
+        preFilter.addRule().type(DtoRule.Jump)
+                .jumpChainId(preNat.getId())
+                .nwDstAddress(floatingIp)
+                .nwDstLength(32)
+                .matchReturnFlow(true)
+                .position(1)
+                .create();
+
+        // Allow ICMP replies (ICMP type 0, code 0 is ICMP reply)
+        preFilter.addRule().type(DtoRule.Jump)
+                .jumpChainId(preNat.getId())
+                .nwDstAddress(floatingIp)
+                .nwDstLength(32)
+                .nwProto(SimpleFirewallRule.stringToProtocolNumber("icmp"))
+                .tpSrcStart(0)
+                .tpSrcEnd(0)
+                .tpDstStart(0)
+                .tpDstEnd(0)
+                .position(2)
+                .create();
+
+        // We only want to set the default DROP rule for static NAT if
+        // Firewall is handled by the Midonet plugin.
+        // Set inbound filter rule (drop all traffic to this IP)
+        if (canHandle(network, Service.Firewall)) {
+            preFilter.addRule().type(DtoRule.Drop)
+                .nwDstAddress(floatingIp)
+                .nwDstLength(32)
+                .position(3)
+                .create();
+        }
+
+        // Set inbound (DNAT) rule
+        preNat.addRule().type(DtoRule.DNAT)
+            .flowAction(DtoRule.Accept)
+            .nwDstAddress(floatingIp)
+            .nwDstLength(32)
+            .inPorts(new UUID[] {tenantUplink.getId()})
+            .natTargets(preTargets)
+            .position(1)
+            .create();
+
+        DtoRule.DtoNatTarget[] postTargets = new DtoRule.DtoNatTarget[]{
+            new DtoRule.DtoNatTarget(floatingIp, floatingIp, 0, 0)};
+
+        // Set outbound (SNAT) rule
+        //    Match forward flow so that return traffic will be recognized
+        postNat.addRule().type(DtoRule.SNAT)
+            .flowAction(DtoRule.Accept)
+            .matchForwardFlow(true)
+            .nwSrcAddress(fixedIp)
+            .nwSrcLength(32)
+            .outPorts(new UUID[]{tenantUplink.getId()})
+            .natTargets(postTargets)
+            .position(1)
+            .create();
+
+        // Set outbound (SNAT) rule
+        //    Match return flow to also allow out traffic which was marked as forward flow on way in
+        postNat.addRule().type(DtoRule.SNAT)
+            .flowAction(DtoRule.Accept)
+            .matchReturnFlow(true)
+            .nwSrcAddress(fixedIp)
+            .nwSrcLength(32)
+            .outPorts(new UUID[]{tenantUplink.getId()})
+            .natTargets(postTargets)
+            .position(2)
+            .create();
+
+        // Set routes for traffic to the SNAT IP to come back from provider router
+        providerRouter.addRoute().type("Normal").weight(100)
+            .srcNetworkAddr("0.0.0.0").srcNetworkLength(0)
+            .dstNetworkAddr(floatingIp).dstNetworkLength(32)
+            .nextHopPort(providerDownlink.getId()).create();
+    }
+
+
+    /**
+     * From interface StaticNatServiceProvider
+     */
+    @Override
+    public boolean applyStaticNats(Network network,
+                                   List<? extends StaticNat> rules)
+            throws ResourceUnavailableException {
+        s_logger.debug("applyStaticNats called with network: " + network.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+        if (!canHandle(network, Service.StaticNat)) {
+            return false;
+        }
+
+        boolean resources = false;
+        Router tenantRouter = null;
+        Router providerRouter = null;
+
+        RouterPort[] ports = null;
+
+        RouterPort tenantUplink = null;
+        RouterPort providerDownlink = null;
+
+        RuleChain preFilter = null;
+        RuleChain preNat = null;
+        RuleChain post = null;
+
+        String accountIdStr = String.valueOf(network.getAccountId());
+        String networkUUIDStr = String.valueOf(network.getId());
+
+        for (StaticNat rule : rules) {
+            IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
+            String sourceIpAddr = sourceIp.getAddress().addr();
+
+            if (resources == false) {
+                tenantRouter = getOrCreateGuestNetworkRouter(network);
+                providerRouter = api.getRouter(_providerRouterId);
+
+                ports = getOrCreateProviderRouterPorts(tenantRouter, providerRouter);
+
+                tenantUplink = ports[0];
+                providerDownlink = ports[1];
+
+                boolean isVpc = getIsVpc(network);
+                long id = getRouterId(network, isVpc);
+                String routerName = getRouterName(isVpc, id);
+
+                preFilter = getChain(accountIdStr, routerName, RuleChainCode.TR_PREFILTER);
+                preNat = getChain(accountIdStr, routerName, RuleChainCode.TR_PRENAT);
+                post = api.getChain(tenantRouter.getOutboundFilterId());
+                resources = true;
+            }
+
+            if (rule.isForRevoke()) {
+                removeMidonetStaticNAT(preFilter, preNat, post,
+                                       sourceIpAddr, rule.getDestIpAddress(),
+                                       providerRouter);
+            } else {
+                addMidonetStaticNAT(preFilter, preNat, post,
+                                    sourceIpAddr, rule.getDestIpAddress(),
+                                    tenantUplink, providerDownlink,
+                                    providerRouter,
+                                    network);
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean applyFWRules(Network config, List<? extends FirewallRule> rulesToApply) throws ResourceUnavailableException {
+        if (!midoInNetwork(config)) {
+            return false;
+        }
+        if (canHandle(config, Service.Firewall)) {
+            String accountIdStr = String.valueOf(config.getAccountId());
+            String networkUUIDStr = String.valueOf(config.getId());
+            RuleChain preFilter = getChain(accountIdStr, networkUUIDStr, RuleChainCode.TR_PREFILTER);
+            RuleChain preNat = getChain(accountIdStr, networkUUIDStr, RuleChainCode.TR_PRENAT);
+
+            // Create a map of Rule description -> Rule for quicker lookups
+            Map<String, Rule> existingRules = new HashMap<String, Rule>();
+
+            for (Rule existingRule : preFilter.getRules()) {
+                // The "whitelist" rules we're interested in are the Jump rules where src address is specified
+                if(existingRule.getType().equals(DtoRule.Jump) && existingRule.getNwSrcAddress() != null){
+                    String ruleString = new SimpleFirewallRule(existingRule).toStringArray()[0];
+                    existingRules.put(ruleString, existingRule);
+                }
+            }
+
+            for (FirewallRule rule : rulesToApply) {
+                IpAddress dstIp = _networkModel.getIp(rule.getSourceIpAddressId());
+                FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, dstIp.getAddress().addr());
+
+                // Convert to string representation
+                SimpleFirewallRule fwRule = new SimpleFirewallRule(ruleTO);
+                String[] ruleStrings = fwRule.toStringArray();
+
+                if (rule.getState() == FirewallRule.State.Revoke) {
+                    // Lookup in existingRules, delete if present
+                    for(String revokeRuleString : ruleStrings){
+                        Rule foundRule = existingRules.get(revokeRuleString);
+                        if(foundRule != null){
+                            foundRule.delete();
+                        }
+                    }
+                } else if (rule.getState() == FirewallRule.State.Add) {
+                    // Lookup in existingRules, add if not present
+                    for(int i = 0; i < ruleStrings.length; i++){
+                        String ruleString = ruleStrings[i];
+                        Rule foundRule = existingRules.get(ruleString);
+                        if(foundRule == null){
+                            // Get the cidr for the related entry in the Source Cidrs list
+                            String relatedCidr = fwRule.sourceCidrs.get(i);
+                            Pair<String,Integer> cidrParts = NetUtils.getCidr(relatedCidr);
+
+                            // Create rule with correct proto, cidr, ACCEPT, dst IP
+                            Rule toApply = preFilter.addRule()
+                                    .type(DtoRule.Jump)
+                                    .jumpChainId(preNat.getId())
+                                    .position(1)
+                                    .nwSrcAddress(cidrParts.first())
+                                    .nwSrcLength(cidrParts.second())
+                                    .nwDstAddress(ruleTO.getSrcIp())
+                                    .nwDstLength(32)
+                                    .nwProto(SimpleFirewallRule.stringToProtocolNumber(rule.getProtocol()));
+
+                            if(rule.getProtocol().equals("icmp")){
+                                // ICMP rules - reuse port fields
+                                toApply.tpSrcStart(fwRule.icmpType).tpSrcEnd(fwRule.icmpType)
+                                    .tpDstStart(fwRule.icmpCode).tpDstEnd(fwRule.icmpCode);
+                            } else {
+                                toApply.tpDstStart(fwRule.dstPortStart)
+                                        .tpDstEnd(fwRule.dstPortEnd);
+                            }
+
+                            toApply.create();
+                        }
+                    }
+                }
+            }
+            return true;
+        } else {
+            return true;
+        }
     }
 
     @Override
     public Provider getProvider() {
-        // TODO: implement this.
-        return null;
+        return MidoNet;
     }
 
     @Override
     public boolean implement(Network network, NetworkOffering offering, DeployDestination dest,
                              ReservationContext context)
             throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
-        // TODO: implement this.
-        return false;
+        s_logger.debug("implement called with network: " + network.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+
+        if(network.getTrafficType() == Networks.TrafficType.Guest){
+            // Create the Midonet bridge for this network
+            Bridge netBridge = getOrCreateNetworkBridge(network);
+            Router tenantRouter = getOrCreateGuestNetworkRouter(network);
+
+            // connect router and bridge
+            ensureBridgeConnectedToRouter(network, netBridge, tenantRouter);
+        }
+
+        return true;
     }
 
     @Override
     public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm,
                            DeployDestination dest, ReservationContext context)
             throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
-        // TODO: implement this.
-        return false;
+        s_logger.debug("prepare called with network: " + network.toString() + " nic: " + nic.toString() + " vm: " + vm.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+
+        if (nic.getTrafficType() == Networks.TrafficType.Guest &&
+            !canHandle(network, Service.StaticNat)) {
+            return false;
+        }
+
+        if (nic.getTrafficType() == Networks.TrafficType.Guest ||
+                nic.getTrafficType() == Networks.TrafficType.Public &&
+                nic.getBroadcastType() == Networks.BroadcastDomainType.Mido){
+            Bridge netBridge = getOrCreateNetworkBridge(network);
+            if(nic.getTrafficType() == Networks.TrafficType.Public &&
+                vm.getVirtualMachine().getType() != VirtualMachine.Type.DomainRouter){
+                // Get provider router
+                Router providerRouter = api.getRouter(_providerRouterId);
+
+                Port[] ports = getOrCreatePublicBridgePorts(nic, netBridge, providerRouter);
+
+                RouterPort providerDownlink = (RouterPort) ports[1];
+
+                // Set route from router to bridge for this particular IP. Prepare
+                // is called in both starting a new VM and restarting a VM, so the
+                // NIC may
+                boolean routeExists = false;
+                for (Route route : providerRouter.getRoutes(new MultivaluedMapImpl())) {
+                    String ip4 = route.getDstNetworkAddr();
+                    if (ip4 != null && ip4.equals(nic.getIp4Address())) {
+                        routeExists = true;
+                        break;
+                    }
+                }
+
+                if (!routeExists) {
+                    providerRouter.addRoute()
+                                  .type("Normal")
+                                  .weight(100)
+                                  .srcNetworkAddr("0.0.0.0")
+                                  .srcNetworkLength(0)
+                                  .dstNetworkAddr(nic.getIp4Address())
+                                  .dstNetworkLength(32)
+                                  .nextHopPort(providerDownlink.getId())
+                                  .nextHopGateway(null)
+                                  .create();
+                }
+            }
+
+            // Add port on bridge
+            BridgePort newPort = netBridge.addExteriorPort().create(); // returns wrapper resource of port
+
+            // Set MidoNet port VIF ID to UUID of nic
+            UUID nicUUID = getNicUUID(nic);
+            newPort.vifId(nicUUID).update();
+        }
+
+        return true;
     }
 
     @Override
     public boolean release(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm,
                            ReservationContext context)
             throws ConcurrentOperationException, ResourceUnavailableException {
-        // TODO: implement this.
-        return false;
+        s_logger.debug("release called with network: " + network.toString() + " nic: " + nic.toString() + " vm: " + vm.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+
+        UUID nicUUID = getNicUUID(nic);
+        if(nic.getTrafficType() == Networks.TrafficType.Guest ||
+                (nic.getTrafficType() == Networks.TrafficType.Public &&
+                nic.getBroadcastType() == Networks.BroadcastDomainType.Mido)) {
+            // Seems like a good place to remove the port in midonet
+            Bridge netBridge = getOrCreateNetworkBridge(network);
+
+            Router providerRouter = api.getRouter(_providerRouterId);
+
+            //remove the routes associated with this IP address
+            for (Route route : providerRouter.getRoutes(new MultivaluedMapImpl())) {
+                String routeDstAddr = route.getDstNetworkAddr();
+                if (routeDstAddr != null && routeDstAddr.equals(nic.getIp4Address())) {
+                    route.delete();
+                }
+            }
+
+            for (BridgePort p : netBridge.getPorts()) {
+                UUID vifID = p.getVifId();
+                if(vifID != null && vifID.equals(nicUUID)){
+                    // This is the MidoNet port which corresponds to the NIC we are releasing
+
+                    // Set VIF ID to null
+                    p.vifId(null).update();
+
+                    // Delete port
+                    p.delete();
+                }
+            }
+        }
+
+        return true;
     }
 
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup)
             throws ConcurrentOperationException, ResourceUnavailableException {
-        // TODO: implement this.
-        return false;
+        s_logger.debug("shutdown called with network: " + network.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+
+        // Find Mido API server, remove ports from this network's bridge, remove bridge itself
+        deleteNetworkBridges(network);
+        if (network.getVpcId() == null) {
+            deleteGuestNetworkRouters(network);
+        }
+
+        return true;
     }
 
     @Override
-    public boolean destroy(Network network) throws ConcurrentOperationException, ResourceUnavailableException {
-        // TODO: implement this.
-        return false;
+    public boolean destroy(Network network, ReservationContext context)
+            throws ConcurrentOperationException, ResourceUnavailableException {
+        s_logger.debug("destroy called with network: " + network.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+
+        deleteNetworkBridges(network);
+        // if This is part of a VPC, then we do not want to delete the router.
+        // we only delete the router when the VPC is destroyed
+        if (network.getVpcId() == null) {
+            deleteGuestNetworkRouters(network);
+        }
+
+        return true;
     }
 
     @Override
     public boolean isReady(PhysicalNetworkServiceProvider provider) {
-        // TODO: implement this.
-        return false;
+        // We are always ready.
+        return true;
     }
 
     @Override
     public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context)
             throws ConcurrentOperationException, ResourceUnavailableException {
-        // TODO: implement this.
-        return false;
+        // Nothing to do here because the cleanup of the networks themselves clean up the resources.
+        return true;
     }
 
     @Override
     public boolean canEnableIndividualServices() {
-        // TODO: implement this.
-        return false;
+        // We can enable individual services, though this is still subject to
+        // "VerifyServicesCombination
+        return true;
     }
 
     @Override
     public boolean verifyServicesCombination(Set<Service> services) {
-        // TODO: implement this.
-        return false;
+        if (services.contains(Service.Vpn) ||
+            services.contains(Service.Dns) ||
+            services.contains(Service.Lb) ||
+            services.contains(Service.UserData) ||
+            services.contains(Service.SecurityGroup) ||
+            services.contains(Service.NetworkACL)) {
+                // We don't implement any of these services, and we don't
+                // want anyone else to do it for us. So if these services
+                // exist, we can't handle it.
+                return false;
+        }
+        return true;
     }
 
     @Override
-    public List<Class<?>> getCommands() {
-        // TODO: implement this.
+    public boolean applyPFRules(Network network, List<PortForwardingRule> rules)
+                    throws ResourceUnavailableException {
+        s_logger.debug("applyPFRules called with network " + network.toString());
+        if (!midoInNetwork(network)) {
+            return false;
+        }
+        if (!canHandle(network, Service.PortForwarding)) {
+            return false;
+        }
+
+        String accountIdStr = String.valueOf(network.getAccountId());
+        String networkUUIDStr = String.valueOf(network.getId());
+        RuleChain preNat = getChain(accountIdStr, networkUUIDStr, RuleChainCode.TR_PRENAT);
+        RuleChain postNat = getChain(accountIdStr, networkUUIDStr, RuleChainCode.TR_POST);
+        RuleChain preFilter = getChain(accountIdStr, networkUUIDStr, RuleChainCode.TR_PREFILTER);
+        Router providerRouter = api.getRouter(_providerRouterId);
+        Router tenantRouter = getOrCreateGuestNetworkRouter(network);
+        RouterPort[] ports = getOrCreateProviderRouterPorts(tenantRouter, providerRouter);
+        RouterPort providerDownlink = ports[1];
+
+        // Rules in the preNat table
+        Map<String, Rule> existingPreNatRules = new HashMap<String, Rule>();
+        for (Rule existingRule : preNat.getRules()) {
+            String ruleString = new SimpleFirewallRule(existingRule).toStringArray()[0];
+            existingPreNatRules.put(ruleString, existingRule);
+        }
+
+        /*
+         * Counts of rules associated with an IP address. Use this to check
+         * how many rules we have of a given IP address. When it reaches 0,
+         * we can delete the route associated with it.
+         */
+        Map<String, Integer> ipRuleCounts = new HashMap<String, Integer>();
+        for (Rule rule : preNat.getRules()) {
+            String ip = rule.getNwDstAddress();
+            if (ip != null && rule.getNwDstLength() == 32) {
+                if (ipRuleCounts.containsKey(ip)) {
+                    ipRuleCounts.put(ip, new Integer(ipRuleCounts.get(ip).intValue() + 1));
+                } else {
+                    ipRuleCounts.put(ip, new Integer(1));
+                }
+            }
+        }
+
+        /*
+         * Routes associated with IP. When we delete all the rules associated
+         * with a given IP, we can delete the route associated with it.
+         */
+        Map<String, Route> routes = new HashMap<String, Route>();
+        for (Route route : providerRouter.getRoutes(new MultivaluedMapImpl())) {
+            String ip = route.getDstNetworkAddr();
+            if (ip != null && route.getDstNetworkLength() == 32) {
+                routes.put(ip, route);
+            }
+        }
+
+        for (PortForwardingRule rule : rules) {
+            IpAddress dstIp = _networkModel.getIp(rule.getSourceIpAddressId());
+            PortForwardingRuleTO ruleTO = new PortForwardingRuleTO(rule, null, dstIp.getAddress().addr());
+            SimpleFirewallRule fwRule = new SimpleFirewallRule(ruleTO);
+            String[] ruleStrings = fwRule.toStringArray();
+
+            if (rule.getState() == FirewallRule.State.Revoke) {
+                /*
+                 * Lookup in existingRules, delete if present
+                 * We need to delete from both the preNat table and the
+                 * postNat table.
+                 */
+                for(String revokeRuleString : ruleStrings){
+                    Rule foundPreNatRule = existingPreNatRules.get(revokeRuleString);
+                    if(foundPreNatRule != null){
+                        String ip = foundPreNatRule.getNwDstAddress();
+                        // is this the last rule associated with this IP?
+                        Integer cnt = ipRuleCounts.get(ip);
+                        if (cnt != null) {
+                            if (cnt == 1) {
+                                ipRuleCounts.remove(ip);
+                                // no more rules for this IP. delete the route.
+                                Route route = routes.remove(ip);
+                                route.delete();
+                            } else {
+                                ipRuleCounts.put(ip, new Integer(ipRuleCounts.get(ip).intValue() - 1));
+                            }
+                        }
+                        foundPreNatRule.delete();
+                    }
+                }
+            } else if (rule.getState() == FirewallRule.State.Add) {
+                for(int i = 0; i < ruleStrings.length; i++){
+                    String ruleString = ruleStrings[i];
+                    Rule foundRule = existingPreNatRules.get(ruleString);
+                    if(foundRule == null){
+
+                        String vmIp = ruleTO.getDstIp();
+                        String publicIp = dstIp.getAddress().addr();
+                        int privPortStart = ruleTO.getDstPortRange()[0];
+                        int privPortEnd = ruleTO.getDstPortRange()[1];
+                        int pubPortStart = ruleTO.getSrcPortRange()[0];
+                        int pubPortEnd = ruleTO.getSrcPortRange()[1];
+
+                        DtoRule.DtoNatTarget[] preTargets = new DtoRule.DtoNatTarget[]{
+                            new DtoRule.DtoNatTarget(vmIp, vmIp, privPortStart, privPortEnd)};
+
+                        Rule preNatRule = preNat.addRule()
+                            .type(DtoRule.DNAT)
+                            .flowAction(DtoRule.Accept)
+                            .nwDstAddress(publicIp)
+                            .nwDstLength(32)
+                            .tpDstStart(pubPortStart)
+                            .tpDstEnd(pubPortEnd)
+                            .natTargets(preTargets)
+                            .nwProto(SimpleFirewallRule.stringToProtocolNumber(rule.getProtocol()))
+                            .position(1);
+
+                        Integer cnt = ipRuleCounts.get(publicIp);
+                        if (cnt != null) {
+                            ipRuleCounts.put(publicIp, new Integer(cnt.intValue() + 1));
+                        } else {
+                            ipRuleCounts.put(publicIp, new Integer(1));
+                        }
+                        String preNatRuleStr = new SimpleFirewallRule(preNatRule).toStringArray()[0];
+                        existingPreNatRules.put(preNatRuleStr, preNatRule);
+                        preNatRule.create();
+
+                        if (routes.get(publicIp) == null) {
+                            Route route = providerRouter.addRoute()
+                                            .type("Normal")
+                                            .weight(100)
+                                            .srcNetworkAddr("0.0.0.0")
+                                            .srcNetworkLength(0)
+                                            .dstNetworkAddr(publicIp)
+                                            .dstNetworkLength(32)
+                                            .nextHopPort(providerDownlink.getId());
+                            route.create();
+                            routes.put(publicIp, route);
+                        }
+
+                        // If Firewall is in our service offering, set up the
+                        // default firewall rule
+                        if (canHandle(network, Service.Firewall)) {
+                            boolean defaultBlock = false;
+                            for (Rule filterRule : preFilter.getRules()) {
+                                String pfDstIp = filterRule.getNwDstAddress();
+                                if (pfDstIp != null && filterRule.getNwDstAddress().equals(publicIp)) {
+                                    defaultBlock = true;
+                                    break;
+                                }
+                            }
+                            if (!defaultBlock) {
+                                preFilter.addRule().type(DtoRule.Drop)
+                                    .nwDstAddress(publicIp)
+                                    .nwDstLength(32)
+                                    .create();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+
+    private static Map<Service, Map<Capability, String>> setCapabilities() {
+        Map<Service, Map<Capability, String>> capabilities = new HashMap<Service, Map<Capability, String>>();
+
+        // L2 Support : SDN provisioning
+        capabilities.put(Service.Connectivity, null);
+
+        // L3 Support : Generic?
+        capabilities.put(Service.Gateway, null);
+
+        // L3 Support : DHCP
+        capabilities.put(Service.Dhcp, null);
+
+        // L3 Support : SourceNat
+        Map<Capability, String> sourceNatCapabilities = new HashMap<Capability, String>();
+        //sourceNatCapabilities.putAll(capabilities.get(Service.SourceNat));
+        sourceNatCapabilities.put(Capability.SupportedSourceNatTypes,
+                "peraccount");
+        //sourceNatCapabilities.putAll(capabilities.get(Service.SourceNat));
+        sourceNatCapabilities.put(Capability.RedundantRouter, "false");
+        capabilities.put(Service.SourceNat, sourceNatCapabilities);
+
+        // L3 Support : Port Forwarding
+        capabilities.put(Service.PortForwarding, null);
+
+        // L3 support : StaticNat
+        capabilities.put(Service.StaticNat, null);
+
+        // Set capabilities for Firewall service
+        Map<Capability, String> firewallCapabilities = new HashMap<Capability, String>();
+        firewallCapabilities.put(Capability.TrafficStatistics, "per public ip");
+        firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp");
+        firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress");
+        firewallCapabilities.put(Capability.MultipleIps, "true");
+        capabilities.put(Service.Firewall, firewallCapabilities);
+
+        return capabilities;
+    }
+
+    private String getChainName(String routerName, RuleChainCode chainCode){
+        return getChainName("", routerName, chainCode);
+    }
+
+    private String getChainName(String networkId, String routerName, RuleChainCode chainCode) {
+
+        String chain = "";
+
+        switch (chainCode){
+            case TR_PRE:
+                chain = "pre-routing";
+                break;
+            case TR_PREFILTER:
+                chain = "pre-filter";
+                break;
+            case TR_PRENAT:
+                chain = "pre-nat";
+                break;
+            case TR_POST:
+                chain = "post-routing";
+                break;
+            case ACL_INGRESS:
+                chain = "ACL-ingress-" + networkId;
+                break;
+            case ACL_EGRESS:
+                chain = "ACL-egress-" + networkId;
+                break;
+        }
+
+        return routerName + "-tenantrouter-" + chain;
+    }
+
+    protected RuleChain getChain(String accountID, String routerName, RuleChainCode chainCode){
+        return getChain("", accountID, routerName, chainCode);
+    }
+
+    protected RuleChain getChain(String networkId, String accountID,
+                               String routerName, RuleChainCode chainCode){
+        String chainName = getChainName(networkId, routerName, chainCode);
+
+        MultivaluedMap findChain = new MultivaluedMapImpl();
+        findChain.add("tenant_id", accountID);
+
+        ResourceCollection<RuleChain> ruleChains = api.getChains(findChain);
+
+        for(RuleChain chain : ruleChains){
+            if(chain.getName().equals(chainName)){
+                return chain;
+            }
+        }
+
         return null;
     }
+
+    protected RouterPort[] getOrCreateProviderRouterPorts(Router tenantRouter, Router providerRouter){
+        RouterPort[] ports = new RouterPort[2];
+
+        RouterPort tenantUplink = null;
+        RouterPort providerDownlink = null;
+
+        // Check if the ports and connection already exist
+        for(Port peerPort : tenantRouter.getPeerPorts((new MultivaluedMapImpl()))){
+            if(peerPort != null && peerPort instanceof RouterPort){
+                RouterPort checkPort = (RouterPort) peerPort;
+                if(checkPort.getDeviceId().compareTo(providerRouter.getId()) == 0){
+                    providerDownlink = checkPort;
+                    tenantUplink = (RouterPort) api.getPort(checkPort.getPeerId());
+                    break;
+                }
+            }
+        }
+
+        // Create the ports and connection if they don't exist
+        if(providerDownlink == null){
+            // Add interior port on router side, with network details
+            providerDownlink = providerRouter.addInteriorRouterPort().networkAddress("169.254.255.0").networkLength(32)
+                    .portAddress("169.254.255.1").create();
+            tenantUplink = tenantRouter.addInteriorRouterPort().networkAddress("169.254.255.0").networkLength(32)
+                    .portAddress("169.254.255.2").create();
+
+            // Link them up
+            providerDownlink.link(tenantUplink.getId()).update();
+        }
+
+        ports[0] = tenantUplink;
+        ports[1] = providerDownlink;
+
+        return ports;
+    }
+
+    private Port[] getOrCreatePublicBridgePorts(NicProfile nic, Bridge publicBridge, Router providerRouter){
+        Port[] ports = new Port[2];
+
+        BridgePort bridgeUplink = null;
+        RouterPort providerDownlink = null;
+
+        // Check if the ports and connection already exist
+        for(Port peerPort : publicBridge.getPeerPorts()){
+            if(peerPort != null && peerPort instanceof RouterPort){
+                RouterPort checkPort = (RouterPort) peerPort;
+                // Check it's a port on the providerRouter with the right gateway address
+                if(checkPort.getDeviceId().compareTo(providerRouter.getId()) == 0
+                        && checkPort.getPortAddress().equals(nic.getGateway())){
+                    providerDownlink = checkPort;
+                    bridgeUplink = (BridgePort) api.getPort(checkPort.getPeerId());
+                    break;
+                }
+            }
+        }
+
+        // Create the ports and connection if they don't exist
+        if(providerDownlink == null){
+            String cidr = NetUtils.ipAndNetMaskToCidr(nic.getGateway(), nic.getNetmask());
+            String cidrSubnet = NetUtils.getCidrSubNet(cidr);
+            int cidrSize = (int) NetUtils.getCidrSize(NetUtils.cidr2Netmask(cidr));
+            String gateway = nic.getGateway();
+
+
+            // Add interior port on router side, with network details
+            providerDownlink = providerRouter.addInteriorRouterPort().networkAddress(cidrSubnet).networkLength(cidrSize)
+                    .portAddress(gateway).create();
+            bridgeUplink = publicBridge.addInteriorPort().create();
+
+            // Link them up
+            providerDownlink.link(bridgeUplink.getId()).update();
+
+
+        }
+
+        ports[0] = bridgeUplink;
+        ports[1] = providerDownlink;
+
+        return ports;
+    }
+
+    private void ensureBridgeConnectedToRouter(Network network, Bridge netBridge, Router netRouter){
+        if(getBridgeToRouterPort(network, netBridge, netRouter) == null){
+            connectBridgeToRouter(network, netBridge, netRouter);
+        }
+    }
+
+    private BridgePort getBridgeToRouterPort(Network network, Bridge netBridge, Router netRouter){
+        for (Port p : netBridge.getPeerPorts()) {
+            if(p.getClass().equals(BridgePort.class)){
+                BridgePort bp = (BridgePort) p;
+                if(bp.getPeerId().compareTo(netRouter.getId()) == 0){
+                    return bp;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * resetEgressACLFilter sets the Egress ACL Filter back to its initial
+     * state - drop everything. This needs to be called when all Egress
+     * ACL rules are deleted, so we can start allowing all Egress traffic
+     * again
+     */
+    protected void resetEgressACLFilter(Network network) {
+        boolean isVpc = getIsVpc(network);
+        long id = getRouterId(network, isVpc);
+        String routerName = getRouterName(isVpc, id);
+
+        RuleChain egressChain = getChain(String.valueOf(network.getId()),
+                                         String.valueOf(network.getAccountId()),
+                                         routerName,
+                                         RuleChainCode.ACL_EGRESS);
+
+        // Clear all the rules out
+        for (Rule rule : egressChain.getRules()) {
+            rule.delete();
+        }
+
+        // Add a matchForwardFlow rule so that we can accept all return traffic
+        egressChain.addRule().type(DtoRule.Accept)
+            .matchForwardFlow(true)
+            .position(1)
+            .create();
+    }
+
+    protected RuleChain getOrInitEgressACLFilter(Network network) {
+        boolean isVpc = getIsVpc(network);
+        long id = getRouterId(network, isVpc);
+        String routerName = getRouterName(isVpc, id);
+
+        RuleChain egressChain = getChain(String.valueOf(network.getId()),
+                                         String.valueOf(network.getAccountId()),
+                                         routerName,
+                                         RuleChainCode.ACL_EGRESS);
+
+        // Rules set by the user will have a protocol, so we count the ACL
+        // rules by counting how much have the nwProto field set.
+        int totalRules = 0;
+        for (Rule rule : egressChain.getRules()) {
+            if (rule.getNwProto() != 0) {
+                totalRules++;
+            }
+        }
+
+        if (totalRules > 0) {
+            // There are already rules present, no need to init.
+            return egressChain;
+        } else {
+            // We need to delete any placeholder rules
+            for (Rule rule : egressChain.getRules()) {
+                rule.delete();
+            }
+        }
+
+        int pos = 1;
+        // If it is ARP, accept it
+        egressChain.addRule().type(DtoRule.Accept)
+            .dlType((short)0x0806)
+            .position(pos++)
+            .create();
+
+        // Everything else gets dropped
+        egressChain.addRule()
+            .type(DtoRule.Drop)
+            .position(pos)
+            .create();
+
+        return egressChain;
+    }
+
+    private void connectBridgeToRouter(Network network, Bridge netBridge, Router netRouter){
+
+        boolean isVpc = getIsVpc(network);
+        long id = getRouterId(network, isVpc);
+        String routerName = getRouterName(isVpc, id);
+        String accountIdStr = String.valueOf(network.getAccountId());
+
+        // Add interior port on bridge side
+        BridgePort bridgePort = netBridge.addInteriorPort().create();
+
+        // Add interior port on router side, with network details
+        RouterPort routerPort = netRouter.addInteriorRouterPort();
+        String cidr = network.getCidr();
+        String cidrSubnet = NetUtils.getCidrSubNet(cidr);
+        int cidrSize = (int) NetUtils.getCidrSize(NetUtils.cidr2Netmask(cidr));
+
+        routerPort.networkAddress(cidrSubnet);
+        routerPort.networkLength(cidrSize);
+        routerPort.portAddress(network.getGateway());
+
+
+        // If this is a VPC, then we will be using NetworkACLs, which is
+        // implemented via chains on the router port to that network.
+        if (getIsVpc(network)) {
+            // Create ACL filter chain for traffic coming INTO the network
+            // (outbound from the port
+            int pos = 1;
+
+            RuleChain inc = api.addChain()
+                .name(getChainName(String.valueOf(network.getId()),
+                                   routerName,
+                                   RuleChainCode.ACL_INGRESS))
+                .tenantId(accountIdStr)
+                .create();
+
+
+            // If it is ARP, accept it
+            inc.addRule().type(DtoRule.Accept)
+                         .dlType((short)0x0806)
+                         .position(pos++)
+                         .create();
+
+            // If it is connection tracked, accept that as well
+            inc.addRule().type(DtoRule.Accept)
+                         .matchReturnFlow(true)
+                         .position(pos++)
+                         .create();
+
+            inc.addRule().type(DtoRule.Drop)
+                         .position(pos)
+                         .create();
+
+            //
+            RuleChain out = api.addChain()
+                .name(getChainName(String.valueOf(network.getId()),
+                                   routerName,
+                                   RuleChainCode.ACL_EGRESS))
+                .tenantId(accountIdStr)
+                .create();
+
+            // Creating the first default rule here that does nothing
+            // but start connection tracking.
+            out.addRule().type(DtoRule.Accept)
+                         .matchForwardFlow(true)
+                         .position(1)
+                         .create();
+
+            routerPort.outboundFilterId(inc.getId());
+            routerPort.inboundFilterId(out.getId());
+        }
+
+        routerPort.create();
+
+        // Link them up
+        bridgePort.link(routerPort.getId()).update();
+
+        // Set up default route from router to subnet
+        netRouter.addRoute().type("Normal").weight(100)
+                .srcNetworkAddr("0.0.0.0").srcNetworkLength(0)
+                .dstNetworkAddr(cidrSubnet).dstNetworkLength(cidrSize)
+                .nextHopPort(routerPort.getId()).nextHopGateway(null).create();
+    }
+
+    private Bridge getOrCreateNetworkBridge(Network network){
+        // Find the single bridge for this network, create if doesn't exist
+        return getOrCreateNetworkBridge(network.getId(), network.getAccountId());
+    }
+
+    private Bridge getOrCreateNetworkBridge(long networkID, long accountID){
+        Bridge netBridge = getNetworkBridge(networkID, accountID);
+        if(netBridge == null){
+
+            String accountIdStr = String.valueOf(accountID);
+            String networkUUIDStr = String.valueOf(networkID);
+
+            netBridge = api.addBridge().tenantId(accountIdStr).name(networkUUIDStr).create();
+        }
+        return netBridge;
+    }
+
+    private Bridge getNetworkBridge(long networkID, long accountID){
+
+        MultivaluedMap qNetBridge = new MultivaluedMapImpl();
+        String accountIdStr = String.valueOf(accountID);
+        String networkUUIDStr = String.valueOf(networkID);
+        qNetBridge.add("tenant_id", accountIdStr);
+
+        for (Bridge b : this. api.getBridges(qNetBridge)) {
+            if(b.getName().equals(networkUUIDStr)){
+                return b;
+            }
+        }
+
+        return null;
+    }
+
+    protected boolean getIsVpc(Network network) {
+        return (network.getVpcId() != null);
+    }
+
+    protected long getRouterId(Network network, boolean isVpc) {
+        if (isVpc) {
+            return network.getVpcId();
+        } else {
+            return network.getId();
+        }
+    }
+
+    private Router getOrCreateGuestNetworkRouter(Network network){
+        // Find the single bridge for this (isolated) guest network, create if doesn't exist
+        boolean isVpc = getIsVpc(network);
+        long id = getRouterId(network, isVpc);
+
+        return getOrCreateGuestNetworkRouter(id, network.getAccountId(), isVpc);
+
+    }
+
+    protected String getRouterName(boolean isVpc, long id) {
+        if (isVpc) {
+            return "VPC" + String.valueOf(id);
+        } else {
+            return String.valueOf(id);
+        }
+    }
+
+    protected Router createRouter(long id, long accountID, boolean isVpc) {
+
+        String accountIdStr = String.valueOf(accountID);
+        String routerName = getRouterName(isVpc, id);
+
+        //Set up rule chains
+        RuleChain pre = api.addChain()
+                            .name(getChainName(routerName, RuleChainCode.TR_PRE))
+                            .tenantId(accountIdStr)
+                            .create();
+        RuleChain post = api.addChain()
+                            .name(getChainName(routerName, RuleChainCode.TR_POST))
+                            .tenantId(accountIdStr)
+                            .create();
+
+        // Set up NAT and filter chains for pre-routing
+        RuleChain preFilter = api.addChain()
+                                  .name(getChainName(routerName, RuleChainCode.TR_PREFILTER))
+                                  .tenantId(accountIdStr)
+                                  .create();
+        RuleChain preNat = api.addChain()
+                                  .name(getChainName(routerName, RuleChainCode.TR_PRENAT))
+                                  .tenantId(accountIdStr)
+                                  .create();
+
+        // Hook the chains in - first jump to Filter chain, then jump to Nat chain
+        pre.addRule().type(DtoRule.Jump)
+                     .jumpChainId(preFilter.getId())
+                     .position(1)
+                     .create();
+        pre.addRule().type(DtoRule.Jump)
+                     .jumpChainId(preNat.getId())
+                     .position(2)
+                     .create();
+
+        return api.addRouter()
+                   .tenantId(accountIdStr)
+                   .name(routerName)
+                   .inboundFilterId(pre.getId())
+                   .outboundFilterId(post.getId())
+                   .create();
+    }
+
+    private Router getOrCreateGuestNetworkRouter(long id, long accountID, boolean isVpc) {
+        Router tenantRouter = getGuestNetworkRouter(id, accountID, isVpc);
+        if(tenantRouter == null){
+            tenantRouter = createRouter(id, accountID, isVpc);
+        }
+        return tenantRouter;
+    }
+
+    private Router getGuestNetworkRouter(long id, long accountID, boolean isVpc){
+
+        MultivaluedMap qNetRouter = new MultivaluedMapImpl();
+        String accountIdStr = String.valueOf(accountID);
+        String routerName = getRouterName(isVpc, id);
+
+        qNetRouter.add("tenant_id", accountIdStr);
+
+        for (Router router : api.getRouters(qNetRouter)) {
+            if(router.getName().equals(routerName)){
+                return router;
+            }
+        }
+
+        return null;
+    }
+
+    private UUID getNicUUID(NicProfile nic){
+        NicVO nicvo = _nicDao.findById(nic.getId());
+        return UUID.fromString(nicvo.getUuid());
+    }
+
+    private void cleanBridge(Bridge br) {
+
+        for(Port peerPort : br.getPeerPorts()) {
+            if(peerPort != null && peerPort instanceof RouterPort){
+                RouterPort checkPort = (RouterPort) peerPort;
+                if(checkPort.getType().equals("ExteriorRouter")) {
+                    checkPort.vifId(null).update();
+                } else if (checkPort.getType().equals("InteriorRouter")) {
+                    checkPort.unlink();
+                }
+                checkPort.delete();
+            }
+        }
+
+        for(BridgePort p : br.getPorts()) {
+
+            if(p.getType().equals("ExteriorBridge")) {
+                // Set VIF ID to null
+                p.vifId(null).update();
+            }
+
+            if(p.getType().equals("InteriorBridge")) {
+                p.unlink();
+            }
+
+            // Delete port
+            p.delete();
+        }
+    }
+
+    private void deleteNetworkBridges(Network network){
+        long accountID = network.getAccountId();
+        long networkID = network.getId();
+
+        Bridge netBridge = getNetworkBridge(networkID, accountID);
+        if(netBridge != null){
+
+            cleanBridge(netBridge);
+
+            // Delete DHCP subnets
+            for(Object dhcpSubnet : netBridge.getDhcpSubnets()){
+                DhcpSubnet sub = (DhcpSubnet) dhcpSubnet;
+                sub.delete();
+            }
+
+            netBridge.delete();
+        }
+    }
+
+    private void deleteGuestNetworkRouters(Network network){
+        long accountID = network.getAccountId();
+        boolean isVpc = getIsVpc(network);
+        long id = getRouterId(network, isVpc);
+
+        Router tenantRouter = getGuestNetworkRouter(id, accountID, isVpc);
+
+        // Delete any peer ports corresponding to this router
+        for(Port peerPort : tenantRouter.getPeerPorts((new MultivaluedMapImpl()))){
+            if(peerPort != null && peerPort instanceof RouterPort){
+                RouterPort checkPort = (RouterPort) peerPort;
+                if(checkPort.getType().equals("ExteriorRouter")) {
+                    checkPort.vifId(null).update();
+                } else if (checkPort.getType().equals("InteriorRouter")) {
+                    checkPort.unlink();
+                }
+                checkPort.delete();
+            } else if (peerPort != null && peerPort instanceof BridgePort) {
+                BridgePort checkPort = (BridgePort) peerPort;
+                if(checkPort.getType().equals("ExteriorBridge")) {
+                    checkPort.vifId(null).update();
+                } else if (checkPort.getType().equals("InteriorBridge")) {
+                    checkPort.unlink();
+                }
+                checkPort.delete();
+            }
+        }
+
+        if(tenantRouter != null){
+            // Remove all peer ports if any exist
+            for(RouterPort p : tenantRouter.getPorts(new MultivaluedMapImpl())) {
+                if(p.getType().equals("ExteriorRouter")) {
+                    // Set VIF ID to null
+                    p.vifId(null).update();
+                    // the port might have some chains associated with it
+                }
+
+                if(p.getType().equals("InteriorRouter")) {
+                    p.unlink();
+                }
+
+                // Delete port
+                p.delete();
+            }
+
+            // Remove inbound and outbound filter chains
+            String accountIdStr = String.valueOf(accountID);
+            String routerName = getRouterName(isVpc, id);
+
+            RuleChain pre = api.getChain(tenantRouter.getInboundFilterId());
+            RuleChain preFilter = getChain(accountIdStr, routerName, RuleChainCode.TR_PREFILTER);
+            RuleChain preNat = getChain(accountIdStr, routerName, RuleChainCode.TR_PRENAT);
+            RuleChain post = api.getChain(tenantRouter.getOutboundFilterId());
+
+            pre.delete();
+            preFilter.delete();
+            preNat.delete();
+            post.delete();
+
+
+            // Remove routes
+            for(Route r : tenantRouter.getRoutes(new MultivaluedMapImpl())) {
+                r.delete();
+            }
+
+            tenantRouter.delete();
+        }
+    }
+
+    @Override
+    public List<Class<?>> getCommands() {
+        // MidoNet does not implement any commands, so we return an empty list.
+        return new ArrayList<Class<?>>();
+    }
 }