OVS plugin support for v2 Quantum API
blueprint: ovs-api-v2-support This commit allows the ovs_quantum_plugin to work with the v2 api. change-Id: I9e332a799f6bee8a90755f961fbb9711a1ecdaca
This commit is contained in:
parent
6d9ddf1c37
commit
804c936667
@ -38,6 +38,8 @@ polling_interval = 2
|
|||||||
# Change to "sudo quantum-rootwrap" to limit commands that can be run
|
# Change to "sudo quantum-rootwrap" to limit commands that can be run
|
||||||
# as root.
|
# as root.
|
||||||
root_helper = sudo
|
root_helper = sudo
|
||||||
|
# Use Quantumv2 API
|
||||||
|
target_v2_api = False
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# Sample Configurations.
|
# Sample Configurations.
|
||||||
@ -53,6 +55,8 @@ root_helper = sudo
|
|||||||
# root_helper = sudo
|
# root_helper = sudo
|
||||||
# Add the following setting, if you want to log to a file
|
# Add the following setting, if you want to log to a file
|
||||||
# log_file = /var/log/quantum/ovs_quantum_agent.log
|
# log_file = /var/log/quantum/ovs_quantum_agent.log
|
||||||
|
# Use Quantumv2 API
|
||||||
|
# target_v2_api = False
|
||||||
#
|
#
|
||||||
# 2. With tunneling.
|
# 2. With tunneling.
|
||||||
# [DATABASE]
|
# [DATABASE]
|
||||||
|
@ -26,6 +26,7 @@ import inspect
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import uuid
|
||||||
|
|
||||||
from quantum.common import exceptions as exception
|
from quantum.common import exceptions as exception
|
||||||
from quantum.common import flags
|
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)
|
cfg_file = os.path.join(cfg_dir, config_file)
|
||||||
if os.path.exists(cfg_file):
|
if os.path.exists(cfg_file):
|
||||||
return cfg_file
|
return cfg_file
|
||||||
|
|
||||||
|
|
||||||
|
def str_uuid():
|
||||||
|
"""Return a uuid as a string"""
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
@ -13,17 +13,11 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy.ext import declarative
|
from sqlalchemy.ext import declarative
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
|
||||||
def str_uuid():
|
|
||||||
return str(uuid.uuid4())
|
|
||||||
|
|
||||||
|
|
||||||
class QuantumBase(object):
|
class QuantumBase(object):
|
||||||
"""Base class for Quantum Models."""
|
"""Base class for Quantum Models."""
|
||||||
|
|
||||||
@ -60,7 +54,6 @@ class QuantumBase(object):
|
|||||||
|
|
||||||
|
|
||||||
class QuantumBaseV2(QuantumBase):
|
class QuantumBaseV2(QuantumBase):
|
||||||
id = sa.Column(sa.String(36), primary_key=True, default=str_uuid)
|
|
||||||
|
|
||||||
@declarative.declared_attr
|
@declarative.declared_attr
|
||||||
def __tablename__(cls):
|
def __tablename__(cls):
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
from quantum.common import utils
|
||||||
from quantum.db import model_base
|
from quantum.db import model_base
|
||||||
|
|
||||||
|
|
||||||
@ -25,7 +26,12 @@ class HasTenant(object):
|
|||||||
tenant_id = sa.Column(sa.String(255))
|
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
|
"""Internal representation of a free IP address range in a Quantum
|
||||||
subnet. The range of available ips is [first_ip..last_ip]. The
|
subnet. The range of available ips is [first_ip..last_ip]. The
|
||||||
allocation retrieves the first entry from the range. If the first
|
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)
|
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."""
|
"""Represents a port on a quantum v2 network."""
|
||||||
network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
|
network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
@ -64,7 +70,7 @@ class Port(model_base.BASEV2, HasTenant):
|
|||||||
device_id = sa.Column(sa.String(255), nullable=False)
|
device_id = sa.Column(sa.String(255), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Subnet(model_base.BASEV2):
|
class Subnet(model_base.BASEV2, HasId):
|
||||||
"""Represents a quantum subnet.
|
"""Represents a quantum subnet.
|
||||||
|
|
||||||
When a subnet is created the first and last entries will be created. These
|
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
|
# - additional_routes
|
||||||
|
|
||||||
|
|
||||||
class Network(model_base.BASEV2, HasTenant):
|
class Network(model_base.BASEV2, HasId, HasTenant):
|
||||||
"""Represents a v2 quantum network."""
|
"""Represents a v2 quantum network."""
|
||||||
name = sa.Column(sa.String(255))
|
name = sa.Column(sa.String(255))
|
||||||
ports = orm.relationship(Port, backref='networks')
|
ports = orm.relationship(Port, backref='networks')
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
# @author: Brad Hall, Nicira Networks, Inc.
|
# @author: Brad Hall, Nicira Networks, Inc.
|
||||||
# @author: Dan Wendlandt, Nicira Networks, Inc.
|
# @author: Dan Wendlandt, Nicira Networks, Inc.
|
||||||
# @author: Dave Lapsley, Nicira Networks, Inc.
|
# @author: Dave Lapsley, Nicira Networks, Inc.
|
||||||
|
# @author: Aaron Rosen, Nicira Networks, Inc.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
@ -59,21 +60,21 @@ class LocalVLANMapping:
|
|||||||
|
|
||||||
|
|
||||||
class Port(object):
|
class Port(object):
|
||||||
'''class stores port data in an ORM-free way,
|
"""Represents a quantum port.
|
||||||
so attributes are still available even if a
|
|
||||||
row has been deleted.
|
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):
|
def __init__(self, p):
|
||||||
self.uuid = p.uuid
|
self.uuid = p.uuid
|
||||||
self.network_id = p.network_id
|
self.network_id = p.network_id
|
||||||
self.interface_id = p.interface_id
|
self.interface_id = p.interface_id
|
||||||
self.state = p.state
|
self.state = p.state
|
||||||
self.op_status = p.op_status
|
self.status = p.op_status
|
||||||
|
|
||||||
def __eq__(self, other):
|
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:
|
try:
|
||||||
return (self and other
|
return (self and other
|
||||||
and self.interface_id == other.interface_id
|
and self.interface_id == other.interface_id
|
||||||
@ -88,14 +89,45 @@ class Port(object):
|
|||||||
return hash(self.uuid)
|
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):
|
class OVSQuantumAgent(object):
|
||||||
|
|
||||||
def __init__(self, integ_br, root_helper,
|
def __init__(self, integ_br, root_helper, polling_interval,
|
||||||
polling_interval, reconnect_interval):
|
reconnect_interval, target_v2_api=False):
|
||||||
self.root_helper = root_helper
|
self.root_helper = root_helper
|
||||||
self.setup_integration_br(integ_br)
|
self.setup_integration_br(integ_br)
|
||||||
self.polling_interval = polling_interval
|
self.polling_interval = polling_interval
|
||||||
self.reconnect_interval = reconnect_interval
|
self.reconnect_interval = reconnect_interval
|
||||||
|
self.target_v2_api = target_v2_api
|
||||||
|
|
||||||
def port_bound(self, port, vlan_id):
|
def port_bound(self, port, vlan_id):
|
||||||
self.int_br.set_db_attribute("Port", port.port_name,
|
self.int_br.set_db_attribute("Port", port.port_name,
|
||||||
@ -139,7 +171,10 @@ class OVSQuantumAgent(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for port in ports:
|
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 = {}
|
vlan_bindings = {}
|
||||||
try:
|
try:
|
||||||
@ -177,7 +212,7 @@ class OVSQuantumAgent(object):
|
|||||||
% (old_b, str(p)))
|
% (old_b, str(p)))
|
||||||
self.port_unbound(p, True)
|
self.port_unbound(p, True)
|
||||||
if p.vif_id in all_bindings:
|
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 new_b is not None:
|
||||||
# If we don't have a binding we have to stick it on
|
# If we don't have a binding we have to stick it on
|
||||||
# the dead vlan
|
# the dead vlan
|
||||||
@ -185,7 +220,7 @@ class OVSQuantumAgent(object):
|
|||||||
vlan_id = vlan_bindings.get(net_id, DEAD_VLAN_TAG)
|
vlan_id = vlan_bindings.get(net_id, DEAD_VLAN_TAG)
|
||||||
self.port_bound(p, vlan_id)
|
self.port_bound(p, vlan_id)
|
||||||
if p.vif_id in all_bindings:
|
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 "
|
LOG.info(("Adding binding to net-id = %s "
|
||||||
"for %s on vlan %s") %
|
"for %s on vlan %s") %
|
||||||
(new_b, str(p), vlan_id))
|
(new_b, str(p), vlan_id))
|
||||||
@ -197,7 +232,7 @@ class OVSQuantumAgent(object):
|
|||||||
old_b = old_local_bindings[vif_id]
|
old_b = old_local_bindings[vif_id]
|
||||||
self.port_unbound(old_vif_ports[vif_id], False)
|
self.port_unbound(old_vif_ports[vif_id], False)
|
||||||
if vif_id in all_bindings:
|
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_vif_ports = new_vif_ports
|
||||||
old_local_bindings = new_local_bindings
|
old_local_bindings = new_local_bindings
|
||||||
@ -237,7 +272,7 @@ class OVSQuantumTunnelAgent(object):
|
|||||||
MAX_VLAN_TAG = 4094
|
MAX_VLAN_TAG = 4094
|
||||||
|
|
||||||
def __init__(self, integ_br, tun_br, local_ip, root_helper,
|
def __init__(self, integ_br, tun_br, local_ip, root_helper,
|
||||||
polling_interval, reconnect_interval):
|
polling_interval, reconnect_interval, target_v2_api=False):
|
||||||
'''Constructor.
|
'''Constructor.
|
||||||
|
|
||||||
:param integ_br: name of the integration bridge.
|
: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 local_ip: local IP address of this hypervisor.
|
||||||
:param root_helper: utility to use when running shell cmds.
|
:param root_helper: utility to use when running shell cmds.
|
||||||
:param polling_interval: interval (secs) to poll DB.
|
: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.root_helper = root_helper
|
||||||
self.available_local_vlans = set(
|
self.available_local_vlans = set(
|
||||||
xrange(OVSQuantumTunnelAgent.MIN_VLAN_TAG,
|
xrange(OVSQuantumTunnelAgent.MIN_VLAN_TAG,
|
||||||
@ -259,6 +296,7 @@ class OVSQuantumTunnelAgent(object):
|
|||||||
self.local_ip = local_ip
|
self.local_ip = local_ip
|
||||||
self.tunnel_count = 0
|
self.tunnel_count = 0
|
||||||
self.setup_tunnel_br(tun_br)
|
self.setup_tunnel_br(tun_br)
|
||||||
|
self.target_v2_api = target_v2_api
|
||||||
|
|
||||||
def provision_local_vlan(self, net_uuid, lsw_id):
|
def provision_local_vlan(self, net_uuid, lsw_id):
|
||||||
'''Provisions a local VLAN.
|
'''Provisions a local VLAN.
|
||||||
@ -294,7 +332,8 @@ class OVSQuantumTunnelAgent(object):
|
|||||||
self.available_local_vlans.add(lvm.vlan)
|
self.available_local_vlans.add(lvm.vlan)
|
||||||
|
|
||||||
def port_bound(self, port, net_uuid, lsw_id):
|
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 port: a ovslib.VifPort object.
|
||||||
:param net_uuid: the net_uuid this port is to be associated with.
|
:param net_uuid: the net_uuid this port is to be associated with.
|
||||||
@ -405,8 +444,12 @@ class OVSQuantumTunnelAgent(object):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
all_bindings = dict((p.interface_id, Port(p))
|
if self.target_v2_api:
|
||||||
for p in db.ports.all())
|
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)
|
all_bindings_vif_port_ids = set(all_bindings)
|
||||||
lsw_id_bindings = dict((bind.network_id, bind.vlan_id)
|
lsw_id_bindings = dict((bind.network_id, bind.vlan_id)
|
||||||
for bind in db.vlan_bindings.all())
|
for bind in db.vlan_bindings.all())
|
||||||
@ -461,7 +504,8 @@ class OVSQuantumTunnelAgent(object):
|
|||||||
old_net_uuid + " for " + str(p)
|
old_net_uuid + " for " + str(p)
|
||||||
+ " added to dead vlan")
|
+ " added to dead vlan")
|
||||||
self.port_unbound(p, old_net_uuid)
|
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:
|
if not new_port:
|
||||||
self.port_dead(p)
|
self.port_dead(p)
|
||||||
|
|
||||||
@ -474,7 +518,7 @@ class OVSQuantumTunnelAgent(object):
|
|||||||
|
|
||||||
lsw_id = lsw_id_bindings[new_net_uuid]
|
lsw_id = lsw_id_bindings[new_net_uuid]
|
||||||
self.port_bound(p, new_net_uuid, lsw_id)
|
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 " % (
|
LOG.info("Port %s on net-id = %s bound to %s " % (
|
||||||
str(p), new_net_uuid,
|
str(p), new_net_uuid,
|
||||||
str(self.local_vlan_map[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:
|
for vif_id in disappeared_vif_ports_ids:
|
||||||
LOG.info("Port Disappeared: " + vif_id)
|
LOG.info("Port Disappeared: " + vif_id)
|
||||||
if vif_id in all_bindings:
|
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)
|
old_port = old_local_bindings.get(vif_id)
|
||||||
if old_port:
|
if old_port:
|
||||||
self.port_unbound(old_vif_ports[vif_id],
|
self.port_unbound(old_vif_ports[vif_id],
|
||||||
@ -540,17 +584,21 @@ def main():
|
|||||||
reconnect_interval = conf.DATABASE.reconnect_interval
|
reconnect_interval = conf.DATABASE.reconnect_interval
|
||||||
root_helper = conf.AGENT.root_helper
|
root_helper = conf.AGENT.root_helper
|
||||||
|
|
||||||
|
# Determine API Version to use
|
||||||
|
target_v2_api = conf.AGENT.target_v2_api
|
||||||
|
|
||||||
if enable_tunneling:
|
if enable_tunneling:
|
||||||
# Get parameters for OVSQuantumTunnelAgent
|
# Get parameters for OVSQuantumTunnelAgent
|
||||||
tun_br = conf.OVS.tunnel_bridge
|
tun_br = conf.OVS.tunnel_bridge
|
||||||
# Mandatory parameter.
|
# Mandatory parameter.
|
||||||
local_ip = conf.OVS.local_ip
|
local_ip = conf.OVS.local_ip
|
||||||
plugin = OVSQuantumTunnelAgent(integ_br, tun_br, local_ip, root_helper,
|
plugin = OVSQuantumTunnelAgent(integ_br, tun_br, local_ip, root_helper,
|
||||||
polling_interval, reconnect_interval)
|
polling_interval, reconnect_interval,
|
||||||
|
target_v2_api)
|
||||||
else:
|
else:
|
||||||
# Get parameters for OVSQuantumAgent.
|
# Get parameters for OVSQuantumAgent.
|
||||||
plugin = OVSQuantumAgent(integ_br, root_helper,
|
plugin = OVSQuantumAgent(integ_br, root_helper, polling_interval,
|
||||||
polling_interval, reconnect_interval)
|
reconnect_interval, target_v2_api)
|
||||||
|
|
||||||
# Start everything.
|
# Start everything.
|
||||||
plugin.daemon_loop(db_connection_url)
|
plugin.daemon_loop(db_connection_url)
|
||||||
|
@ -32,6 +32,7 @@ ovs_opts = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
agent_opts = [
|
agent_opts = [
|
||||||
|
cfg.BoolOpt('target_v2_api', default=True),
|
||||||
cfg.IntOpt('polling_interval', default=2),
|
cfg.IntOpt('polling_interval', default=2),
|
||||||
cfg.StrOpt('root_helper', default='sudo'),
|
cfg.StrOpt('root_helper', default='sudo'),
|
||||||
cfg.StrOpt('log_file', default=None),
|
cfg.StrOpt('log_file', default=None),
|
||||||
|
51
quantum/plugins/openvswitch/ovs_db_v2.py
Normal file
51
quantum/plugins/openvswitch/ovs_db_v2.py
Normal file
@ -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()
|
49
quantum/plugins/openvswitch/ovs_models_v2.py
Normal file
49
quantum/plugins/openvswitch/ovs_models_v2.py
Normal file
@ -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)
|
@ -17,6 +17,7 @@
|
|||||||
# @author: Brad Hall, Nicira Networks, Inc.
|
# @author: Brad Hall, Nicira Networks, Inc.
|
||||||
# @author: Dan Wendlandt, Nicira Networks, Inc.
|
# @author: Dan Wendlandt, Nicira Networks, Inc.
|
||||||
# @author: Dave Lapsley, Nicira Networks, Inc.
|
# @author: Dave Lapsley, Nicira Networks, Inc.
|
||||||
|
# @author: Aaron Rosen, Nicira Networks, Inc.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -24,14 +25,16 @@ import os
|
|||||||
from quantum.api.api_common import OperationalStatus
|
from quantum.api.api_common import OperationalStatus
|
||||||
from quantum.common import exceptions as q_exc
|
from quantum.common import exceptions as q_exc
|
||||||
from quantum.common.utils import find_config_file
|
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.common import config
|
||||||
from quantum.plugins.openvswitch import ovs_db
|
from quantum.plugins.openvswitch import ovs_db
|
||||||
|
from quantum.plugins.openvswitch import ovs_db_v2
|
||||||
from quantum.quantum_plugin_base import QuantumPluginBase
|
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"},
|
CONF_FILE = find_config_file({"plugin": "openvswitch"},
|
||||||
"ovs_quantum_plugin.ini")
|
"ovs_quantum_plugin.ini")
|
||||||
|
|
||||||
@ -47,6 +50,12 @@ class VlanMap(object):
|
|||||||
free_vlans = set()
|
free_vlans = set()
|
||||||
|
|
||||||
def __init__(self, vlan_min=1, vlan_max=4094):
|
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_min = vlan_min
|
||||||
self.vlan_max = vlan_max
|
self.vlan_max = vlan_max
|
||||||
self.vlans.clear()
|
self.vlans.clear()
|
||||||
@ -82,44 +91,26 @@ class VlanMap(object):
|
|||||||
else:
|
else:
|
||||||
LOG.error("No vlan found with network \"%s\"", network_id)
|
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):
|
class OVSQuantumPlugin(QuantumPluginBase):
|
||||||
|
|
||||||
def __init__(self, configfile=None):
|
def __init__(self, configfile=None):
|
||||||
if configfile is None:
|
conf = config.parse(CONF_FILE)
|
||||||
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)
|
|
||||||
options = {"sql_connection": conf.DATABASE.sql_connection}
|
options = {"sql_connection": conf.DATABASE.sql_connection}
|
||||||
reconnect_interval = conf.DATABASE.reconnect_interval
|
reconnect_interval = conf.DATABASE.reconnect_interval
|
||||||
options.update({"reconnect_interval": reconnect_interval})
|
options.update({"reconnect_interval": reconnect_interval})
|
||||||
db.configure_db(options)
|
db.configure_db(options)
|
||||||
|
|
||||||
vlan_min = conf.OVS.vlan_min
|
self.vmap = VlanMap(conf.OVS.vlan_min, conf.OVS.vlan_max)
|
||||||
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)
|
|
||||||
# Populate the map with anything that is already present in the
|
# Populate the map with anything that is already present in the
|
||||||
# database
|
# database
|
||||||
vlans = ovs_db.get_vlans()
|
self.vmap.populate_already_used(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)
|
|
||||||
|
|
||||||
def get_all_networks(self, tenant_id, **kwargs):
|
def get_all_networks(self, tenant_id, **kwargs):
|
||||||
nets = []
|
nets = []
|
||||||
@ -142,8 +133,13 @@ class OVSQuantumPlugin(QuantumPluginBase):
|
|||||||
def create_network(self, tenant_id, net_name, **kwargs):
|
def create_network(self, tenant_id, net_name, **kwargs):
|
||||||
net = db.network_create(tenant_id, net_name,
|
net = db.network_create(tenant_id, net_name,
|
||||||
op_status=OperationalStatus.UP)
|
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)
|
LOG.debug("Created network: %s" % net)
|
||||||
vlan_id = self.vmap.acquire(str(net.uuid))
|
|
||||||
ovs_db.add_vlan_binding(vlan_id, 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)
|
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)
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
||||||
res = db.port_get(port_id, net_id)
|
res = db.port_get(port_id, net_id)
|
||||||
return res.interface_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)
|
||||||
|
Loading…
Reference in New Issue
Block a user