]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
OVS plugin support for v2 Quantum API
authorAaron Rosen <arosen@nicira.com>
Fri, 29 Jun 2012 01:17:16 +0000 (18:17 -0700)
committerAaron Rosen <arosen@nicira.com>
Mon, 2 Jul 2012 19:30:38 +0000 (12:30 -0700)
blueprint: ovs-api-v2-support

This commit allows the ovs_quantum_plugin to work with the v2 api.

change-Id: I9e332a799f6bee8a90755f961fbb9711a1ecdaca

etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
quantum/common/utils.py
quantum/db/model_base.py
quantum/db/models_v2.py
quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
quantum/plugins/openvswitch/common/config.py
quantum/plugins/openvswitch/ovs_db_v2.py [new file with mode: 0644]
quantum/plugins/openvswitch/ovs_models_v2.py [new file with mode: 0644]
quantum/plugins/openvswitch/ovs_quantum_plugin.py

index 32625712063219ea5698c403f7ccdaed5fb48119..44f3fca58d38159470dd90c8c66991b3860a6f9f 100644 (file)
@@ -38,6 +38,8 @@ polling_interval = 2
 # Change to "sudo quantum-rootwrap" to limit commands that can be run
 # as root.
 root_helper = sudo
+# Use Quantumv2 API
+target_v2_api = False
 
 #-----------------------------------------------------------------------------
 # Sample Configurations.
@@ -53,6 +55,8 @@ root_helper = sudo
 # root_helper = sudo
 # Add the following setting, if you want to log to a file
 # log_file = /var/log/quantum/ovs_quantum_agent.log
+# Use Quantumv2 API
+# target_v2_api = False
 #
 # 2. With tunneling.
 # [DATABASE]
index 4ac2c328bedf647cd02efa23d94681f663220954..0d30d327935950cb3fa1770f98c492b3edf3ab55 100644 (file)
@@ -26,6 +26,7 @@ import inspect
 import logging
 import os
 import subprocess
+import uuid
 
 from quantum.common import exceptions as exception
 from quantum.common import flags
@@ -153,3 +154,8 @@ def find_config_file(options, config_file):
         cfg_file = os.path.join(cfg_dir, config_file)
         if os.path.exists(cfg_file):
             return cfg_file
+
+
+def str_uuid():
+    """Return a uuid as a string"""
+    return str(uuid.uuid4())
index 54842b81573d32571fd61f83c38f83bfd86b4dbb..d93d14867efcfe8aaac44b541a6ba58d203f26d1 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import uuid
-
 import sqlalchemy as sa
 from sqlalchemy.ext import declarative
 from sqlalchemy import orm
 
 
-def str_uuid():
-    return str(uuid.uuid4())
-
-
 class QuantumBase(object):
     """Base class for Quantum Models."""
 
@@ -60,7 +54,6 @@ class QuantumBase(object):
 
 
 class QuantumBaseV2(QuantumBase):
-    id = sa.Column(sa.String(36), primary_key=True, default=str_uuid)
 
     @declarative.declared_attr
     def __tablename__(cls):
index 9425ef6d4412bb58160d65c0d64aba8cc98d489e..72c737239a3a6732a8ea40ce16aaa190c6b9b907 100644 (file)
@@ -16,6 +16,7 @@
 import sqlalchemy as sa
 from sqlalchemy import orm
 
+from quantum.common import utils
 from quantum.db import model_base
 
 
@@ -25,7 +26,12 @@ class HasTenant(object):
     tenant_id = sa.Column(sa.String(255))
 
 
-class IPAllocationRange(model_base.BASEV2):
+class HasId(object):
+    """id mixin, add to subclasses that have an id."""
+    id = sa.Column(sa.String(36), primary_key=True, default=utils.str_uuid)
+
+
+class IPAllocationRange(model_base.BASEV2, HasId):
     """Internal representation of a free IP address range in a Quantum
     subnet. The range of available ips is [first_ip..last_ip]. The
     allocation retrieves the first entry from the range. If the first
@@ -53,7 +59,7 @@ class IPAllocation(model_base.BASEV2):
                            nullable=False, primary_key=True)
 
 
-class Port(model_base.BASEV2, HasTenant):
+class Port(model_base.BASEV2, HasId, HasTenant):
     """Represents a port on a quantum v2 network."""
     network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
                            nullable=False)
@@ -64,7 +70,7 @@ class Port(model_base.BASEV2, HasTenant):
     device_id = sa.Column(sa.String(255), nullable=False)
 
 
-class Subnet(model_base.BASEV2):
+class Subnet(model_base.BASEV2, HasId):
     """Represents a quantum subnet.
 
     When a subnet is created the first and last entries will be created. These
@@ -81,7 +87,7 @@ class Subnet(model_base.BASEV2):
     # - additional_routes
 
 
-class Network(model_base.BASEV2, HasTenant):
+class Network(model_base.BASEV2, HasId, HasTenant):
     """Represents a v2 quantum network."""
     name = sa.Column(sa.String(255))
     ports = orm.relationship(Port, backref='networks')
index a11f43e069e058c199e6eff04673f8bbd56d961e..be46942d820e4432f854eae795e6d46b678e0e61 100755 (executable)
@@ -18,6 +18,7 @@
 # @author: Brad Hall, Nicira Networks, Inc.
 # @author: Dan Wendlandt, Nicira Networks, Inc.
 # @author: Dave Lapsley, Nicira Networks, Inc.
+# @author: Aaron Rosen, Nicira Networks, Inc.
 
 import logging
 from optparse import OptionParser
@@ -59,21 +60,21 @@ class LocalVLANMapping:
 
 
 class Port(object):
-    '''class stores port data in an ORM-free way,
-       so attributes are still available even if a
-       row has been deleted.
-    '''
+    """Represents a quantum port.
+
+    Class stores port data in a ORM-free way, so attributres are
+    still available even if a row has been deleted.
+    """
 
     def __init__(self, p):
         self.uuid = p.uuid
         self.network_id = p.network_id
         self.interface_id = p.interface_id
         self.state = p.state
-        self.op_status = p.op_status
+        self.status = p.op_status
 
     def __eq__(self, other):
-        '''compare only fields that will cause us to re-wire
-        '''
+        '''Compare only fields that will cause us to re-wire.'''
         try:
             return (self and other
                     and self.interface_id == other.interface_id
@@ -88,14 +89,45 @@ class Port(object):
         return hash(self.uuid)
 
 
+class Portv2(object):
+    """Represents a quantumv2 port.
+
+    Class stores port data in a ORM-free way, so attributres are
+    still available even if a row has been deleted.
+    """
+
+    def __init__(self, p):
+        self.id = p.id
+        self.network_id = p.network_id
+        self.device_id = p.device_id
+        self.admin_state_up = p.admin_state_up
+        self.status = p.status
+
+    def __eq__(self, other):
+        '''Compare only fields that will cause us to re-wire.'''
+        try:
+            return (self and other
+                    and self.id == other.id
+                    and self.admin_state_up == other.admin_state_up)
+        except:
+            return False
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __hash__(self):
+        return hash(self.id)
+
+
 class OVSQuantumAgent(object):
 
-    def __init__(self, integ_br, root_helper,
-                 polling_interval, reconnect_interval):
+    def __init__(self, integ_br, root_helper, polling_interval,
+                 reconnect_interval, target_v2_api=False):
         self.root_helper = root_helper
         self.setup_integration_br(integ_br)
         self.polling_interval = polling_interval
         self.reconnect_interval = reconnect_interval
+        self.target_v2_api = target_v2_api
 
     def port_bound(self, port, vlan_id):
         self.int_br.set_db_attribute("Port", port.port_name,
@@ -139,7 +171,10 @@ class OVSQuantumAgent(object):
                 continue
 
             for port in ports:
-                all_bindings[port.interface_id] = port
+                if self.target_v2_api:
+                    all_bindings[port.id] = port
+                else:
+                    all_bindings[port.interface_id] = port
 
             vlan_bindings = {}
             try:
@@ -177,7 +212,7 @@ class OVSQuantumAgent(object):
                                  % (old_b, str(p)))
                         self.port_unbound(p, True)
                         if p.vif_id in all_bindings:
-                            all_bindings[p.vif_id].op_status = OP_STATUS_DOWN
+                            all_bindings[p.vif_id].status = OP_STATUS_DOWN
                     if new_b is not None:
                         # If we don't have a binding we have to stick it on
                         # the dead vlan
@@ -185,7 +220,7 @@ class OVSQuantumAgent(object):
                         vlan_id = vlan_bindings.get(net_id, DEAD_VLAN_TAG)
                         self.port_bound(p, vlan_id)
                         if p.vif_id in all_bindings:
-                            all_bindings[p.vif_id].op_status = OP_STATUS_UP
+                            all_bindings[p.vif_id].status = OP_STATUS_UP
                         LOG.info(("Adding binding to net-id = %s "
                                   "for %s on vlan %s") %
                                  (new_b, str(p), vlan_id))
@@ -197,7 +232,7 @@ class OVSQuantumAgent(object):
                         old_b = old_local_bindings[vif_id]
                         self.port_unbound(old_vif_ports[vif_id], False)
                     if vif_id in all_bindings:
-                        all_bindings[vif_id].op_status = OP_STATUS_DOWN
+                        all_bindings[vif_id].status = OP_STATUS_DOWN
 
             old_vif_ports = new_vif_ports
             old_local_bindings = new_local_bindings
@@ -237,7 +272,7 @@ class OVSQuantumTunnelAgent(object):
     MAX_VLAN_TAG = 4094
 
     def __init__(self, integ_br, tun_br, local_ip, root_helper,
-                 polling_interval, reconnect_interval):
+                 polling_interval, reconnect_interval, target_v2_api=False):
         '''Constructor.
 
         :param integ_br: name of the integration bridge.
@@ -245,7 +280,9 @@ class OVSQuantumTunnelAgent(object):
         :param local_ip: local IP address of this hypervisor.
         :param root_helper: utility to use when running shell cmds.
         :param polling_interval: interval (secs) to poll DB.
-        :param reconnect_internal: retry interval (secs) on DB error.'''
+        :param reconnect_internal: retry interval (secs) on DB error.
+        :param target_v2_api: if True  use v2 api.
+        '''
         self.root_helper = root_helper
         self.available_local_vlans = set(
             xrange(OVSQuantumTunnelAgent.MIN_VLAN_TAG,
@@ -259,6 +296,7 @@ class OVSQuantumTunnelAgent(object):
         self.local_ip = local_ip
         self.tunnel_count = 0
         self.setup_tunnel_br(tun_br)
+        self.target_v2_api = target_v2_api
 
     def provision_local_vlan(self, net_uuid, lsw_id):
         '''Provisions a local VLAN.
@@ -294,7 +332,8 @@ class OVSQuantumTunnelAgent(object):
         self.available_local_vlans.add(lvm.vlan)
 
     def port_bound(self, port, net_uuid, lsw_id):
-        '''Bind port to net_uuid/lsw_id.
+        '''Bind port to net_uuid/lsw_id and install flow for inbound traffic
+        to vm.
 
         :param port: a ovslib.VifPort object.
         :param net_uuid: the net_uuid this port is to be associated with.
@@ -405,8 +444,12 @@ class OVSQuantumTunnelAgent(object):
 
         while True:
             try:
-                all_bindings = dict((p.interface_id, Port(p))
-                                    for p in db.ports.all())
+                if self.target_v2_api:
+                    all_bindings = dict((p.id, Portv2(p))
+                                        for p in db.ports.all())
+                else:
+                    all_bindings = dict((p.interface_id, Port(p))
+                                        for p in db.ports.all())
                 all_bindings_vif_port_ids = set(all_bindings)
                 lsw_id_bindings = dict((bind.network_id, bind.vlan_id)
                                        for bind in db.vlan_bindings.all())
@@ -461,7 +504,8 @@ class OVSQuantumTunnelAgent(object):
                                  old_net_uuid + " for " + str(p)
                                  + " added to dead vlan")
                         self.port_unbound(p, old_net_uuid)
-                        all_bindings[p.vif_id].op_status = OP_STATUS_DOWN
+                        if p.vif_id in all_bindings:
+                            all_bindings[p.vif_id].status = OP_STATUS_DOWN
                         if not new_port:
                             self.port_dead(p)
 
@@ -474,7 +518,7 @@ class OVSQuantumTunnelAgent(object):
 
                         lsw_id = lsw_id_bindings[new_net_uuid]
                         self.port_bound(p, new_net_uuid, lsw_id)
-                        all_bindings[p.vif_id].op_status = OP_STATUS_UP
+                        all_bindings[p.vif_id].status = OP_STATUS_UP
                         LOG.info("Port %s on net-id = %s bound to %s " % (
                                  str(p), new_net_uuid,
                                  str(self.local_vlan_map[new_net_uuid])))
@@ -482,7 +526,7 @@ class OVSQuantumTunnelAgent(object):
                 for vif_id in disappeared_vif_ports_ids:
                     LOG.info("Port Disappeared: " + vif_id)
                     if vif_id in all_bindings:
-                        all_bindings[vif_id].op_status = OP_STATUS_DOWN
+                        all_bindings[vif_id].status = OP_STATUS_DOWN
                     old_port = old_local_bindings.get(vif_id)
                     if old_port:
                         self.port_unbound(old_vif_ports[vif_id],
@@ -540,17 +584,21 @@ def main():
     reconnect_interval = conf.DATABASE.reconnect_interval
     root_helper = conf.AGENT.root_helper
 
+    # Determine API Version to use
+    target_v2_api = conf.AGENT.target_v2_api
+
     if enable_tunneling:
         # Get parameters for OVSQuantumTunnelAgent
         tun_br = conf.OVS.tunnel_bridge
         # Mandatory parameter.
         local_ip = conf.OVS.local_ip
         plugin = OVSQuantumTunnelAgent(integ_br, tun_br, local_ip, root_helper,
-                                       polling_interval, reconnect_interval)
+                                       polling_interval, reconnect_interval,
+                                       target_v2_api)
     else:
         # Get parameters for OVSQuantumAgent.
-        plugin = OVSQuantumAgent(integ_br, root_helper,
-                                 polling_interval, reconnect_interval)
+        plugin = OVSQuantumAgent(integ_br, root_helper, polling_interval,
+                                 reconnect_interval, target_v2_api)
 
     # Start everything.
     plugin.daemon_loop(db_connection_url)
index 88a579880757a09d00734050dfe2a67cf936037e..9774b82de41dfdd1ef63188591e5499aa210b957 100644 (file)
@@ -32,6 +32,7 @@ ovs_opts = [
 ]
 
 agent_opts = [
+    cfg.BoolOpt('target_v2_api', default=True),
     cfg.IntOpt('polling_interval', default=2),
     cfg.StrOpt('root_helper', default='sudo'),
     cfg.StrOpt('log_file', default=None),
diff --git a/quantum/plugins/openvswitch/ovs_db_v2.py b/quantum/plugins/openvswitch/ovs_db_v2.py
new file mode 100644 (file)
index 0000000..9cdaff6
--- /dev/null
@@ -0,0 +1,51 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2011 Nicira Networks, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+# @author: Aaron Rosen, Nicira Networks, Inc.
+
+from sqlalchemy.orm import exc
+
+import quantum.db.api as db
+from quantum.plugins.openvswitch import ovs_models_v2
+
+
+def get_vlans():
+    session = db.get_session()
+    try:
+        bindings = (session.query(ovs_models_v2.VlanBinding).
+                    all())
+    except exc.NoResultFound:
+        return []
+    return [(binding.vlan_id, binding.network_id) for binding in bindings]
+
+
+def add_vlan_binding(vlan_id, net_id):
+    session = db.get_session()
+    binding = ovs_models_v2.VlanBinding(vlan_id, net_id)
+    session.add(binding)
+    session.flush()
+    return binding
+
+
+def remove_vlan_binding(net_id):
+    session = db.get_session()
+    try:
+        binding = (session.query(ovs_models_v2.VlanBinding).
+                   filter_by(network_id=net_id).
+                   one())
+        session.delete(binding)
+    except exc.NoResultFound:
+        pass
+    session.flush()
diff --git a/quantum/plugins/openvswitch/ovs_models_v2.py b/quantum/plugins/openvswitch/ovs_models_v2.py
new file mode 100644 (file)
index 0000000..cbdabdd
--- /dev/null
@@ -0,0 +1,49 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2011 Nicira Networks, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+# @author: Aaron Rosen, Nicira Networks, Inc.
+
+
+from sqlalchemy import Column, Integer, String
+
+from quantum.db import models_v2
+
+
+class VlanBinding(models_v2.model_base.BASEV2):
+    """Represents a binding of network_id to vlan_id."""
+    __tablename__ = 'vlan_bindings'
+
+    vlan_id = Column(Integer, primary_key=True)
+    network_id = Column(String(255))
+
+    def __init__(self, vlan_id, network_id):
+        self.network_id = network_id
+        self.vlan_id = vlan_id
+
+    def __repr__(self):
+        return "<VlanBinding(%s,%s)>" % (self.vlan_id, self.network_id)
+
+
+class TunnelIP(models_v2.model_base.BASEV2):
+    """Represents a remote IP in tunnel mode."""
+    __tablename__ = 'tunnel_ips'
+
+    ip_address = Column(String(255), primary_key=True)
+
+    def __init__(self, ip_address):
+        self.ip_address = ip_address
+
+    def __repr__(self):
+        return "<TunnelIP(%s)>" % (self.ip_address)
index 231584f747e71d2bf42870fe37d876aacd49a3de..31ce5c4fb8366dee4d77a3bb1d75602c7ff8b726 100644 (file)
@@ -17,6 +17,7 @@
 # @author: Brad Hall, Nicira Networks, Inc.
 # @author: Dan Wendlandt, Nicira Networks, Inc.
 # @author: Dave Lapsley, Nicira Networks, Inc.
+# @author: Aaron Rosen, Nicira Networks, Inc.
 
 import logging
 import os
@@ -24,14 +25,16 @@ import os
 from quantum.api.api_common import OperationalStatus
 from quantum.common import exceptions as q_exc
 from quantum.common.utils import find_config_file
-import quantum.db.api as db
+from quantum.db import api as db
+from quantum.db import db_base_plugin_v2
+from quantum.db import models_v2
 from quantum.plugins.openvswitch.common import config
 from quantum.plugins.openvswitch import ovs_db
+from quantum.plugins.openvswitch import ovs_db_v2
 from quantum.quantum_plugin_base import QuantumPluginBase
 
-LOG = logging.getLogger("ovs_quantum_plugin")
-
 
+LOG = logging.getLogger("ovs_quantum_plugin")
 CONF_FILE = find_config_file({"plugin": "openvswitch"},
                              "ovs_quantum_plugin.ini")
 
@@ -47,6 +50,12 @@ class VlanMap(object):
     free_vlans = set()
 
     def __init__(self, vlan_min=1, vlan_max=4094):
+        if vlan_min > vlan_max:
+            LOG.warn("Using default VLAN values! vlan_min = %s is larger"
+                     " than vlan_max = %s!" % (vlan_min, vlan_max))
+            vlan_min = 1
+            vlan_max = 4094
+
         self.vlan_min = vlan_min
         self.vlan_max = vlan_max
         self.vlans.clear()
@@ -82,44 +91,26 @@ class VlanMap(object):
         else:
             LOG.error("No vlan found with network \"%s\"", network_id)
 
+    def populate_already_used(self, vlans):
+        for vlan_id, network_id in vlans:
+            LOG.debug("Adding already populated vlan %s -> %s" %
+                      (vlan_id, network_id))
+            self.already_used(vlan_id, network_id)
+
 
 class OVSQuantumPlugin(QuantumPluginBase):
 
     def __init__(self, configfile=None):
-        if configfile is None:
-            if os.path.exists(CONF_FILE):
-                configfile = CONF_FILE
-            else:
-                configfile = find_config(os.path.abspath(
-                    os.path.dirname(__file__)))
-        if configfile is None:
-            raise Exception("Configuration file \"%s\" doesn't exist" %
-                            (configfile))
-        LOG.debug("Using configuration file: %s" % configfile)
-        conf = config.parse(configfile)
+        conf = config.parse(CONF_FILE)
         options = {"sql_connection": conf.DATABASE.sql_connection}
         reconnect_interval = conf.DATABASE.reconnect_interval
         options.update({"reconnect_interval": reconnect_interval})
         db.configure_db(options)
 
-        vlan_min = conf.OVS.vlan_min
-        vlan_max = conf.OVS.vlan_max
-
-        if vlan_min > vlan_max:
-            LOG.warn("Using default VLAN values! vlan_min = %s is larger"
-                     " than vlan_max = %s!" % (vlan_min, vlan_max))
-            vlan_min = 1
-            vlan_max = 4094
-
-        self.vmap = VlanMap(vlan_min, vlan_max)
+        self.vmap = VlanMap(conf.OVS.vlan_min, conf.OVS.vlan_max)
         # Populate the map with anything that is already present in the
         # database
-        vlans = ovs_db.get_vlans()
-        for x in vlans:
-            vlan_id, network_id = x
-            LOG.debug("Adding already populated vlan %s -> %s" %
-                      (vlan_id, network_id))
-            self.vmap.already_used(vlan_id, network_id)
+        self.vmap.populate_already_used(ovs_db.get_vlans())
 
     def get_all_networks(self, tenant_id, **kwargs):
         nets = []
@@ -142,8 +133,13 @@ class OVSQuantumPlugin(QuantumPluginBase):
     def create_network(self, tenant_id, net_name, **kwargs):
         net = db.network_create(tenant_id, net_name,
                                 op_status=OperationalStatus.UP)
+        try:
+            vlan_id = self.vmap.acquire(str(net.uuid))
+        except NoFreeVLANException:
+            db.network_destroy(net.uuid)
+            raise
+
         LOG.debug("Created network: %s" % net)
-        vlan_id = self.vmap.acquire(str(net.uuid))
         ovs_db.add_vlan_binding(vlan_id, str(net.uuid))
         return self._make_net_dict(str(net.uuid), net.name, [], net.op_status)
 
@@ -233,3 +229,33 @@ class OVSQuantumPlugin(QuantumPluginBase):
         db.validate_port_ownership(tenant_id, net_id, port_id)
         res = db.port_get(port_id, net_id)
         return res.interface_id
+
+
+class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
+    def __init__(self, configfile=None):
+        conf = config.parse(CONF_FILE)
+        options = {"sql_connection": conf.DATABASE.sql_connection}
+        options.update({'base': models_v2.model_base.BASEV2})
+        reconnect_interval = conf.DATABASE.reconnect_interval
+        options.update({"reconnect_interval": reconnect_interval})
+        db.configure_db(options)
+
+        self.vmap = VlanMap(conf.OVS.vlan_min, conf.OVS.vlan_max)
+        self.vmap.populate_already_used(ovs_db_v2.get_vlans())
+
+    def create_network(self, context, network):
+        net = super(OVSQuantumPluginV2, self).create_network(context, network)
+        try:
+            vlan_id = self.vmap.acquire(str(net['id']))
+        except NoFreeVLANException:
+            super(OVSQuantumPluginV2, self).delete_network(context, net['id'])
+            raise
+
+        LOG.debug("Created network: %s" % net['id'])
+        ovs_db_v2.add_vlan_binding(vlan_id, str(net['id']))
+        return net
+
+    def delete_network(self, context, id):
+        ovs_db_v2.remove_vlan_binding(id)
+        self.vmap.release(id)
+        return super(OVSQuantumPluginV2, self).delete_network(context, id)