58d42b6558
blueprint scalable-agent-comms This is the first stage of the blueprint. This adds support to the linux bridge plugin. The development followed the design described in: https://docs.google.com/document/d/1MbcBA2Os4b98ybdgAw2qe_68R1NG6KMh8zdZKgOlpvg/edit?pli=1 Change-Id: I4004c05a63ce49f020c2016c8763e73238b465a7
314 lines
9.9 KiB
Python
314 lines
9.9 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2012, Cisco Systems, Inc.
|
|
#
|
|
# 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: Rohit Agarwalla, Cisco Systems, Inc.
|
|
|
|
import logging
|
|
|
|
from sqlalchemy import func
|
|
from sqlalchemy.orm import exc
|
|
|
|
from quantum.api import api_common
|
|
from quantum.common import exceptions as q_exc
|
|
import quantum.db.api as db
|
|
from quantum.db import models_v2
|
|
from quantum.openstack.common import cfg
|
|
from quantum.plugins.linuxbridge.common import config
|
|
from quantum.plugins.linuxbridge.common import exceptions as c_exc
|
|
from quantum.plugins.linuxbridge.db import l2network_models
|
|
from quantum.plugins.linuxbridge.db import l2network_models_v2
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
# The global variable for the database version model
|
|
L2_MODEL = l2network_models
|
|
|
|
|
|
def initialize(base=None):
|
|
global L2_MODEL
|
|
options = {"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection}
|
|
options.update({"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries})
|
|
options.update({"reconnect_interval":
|
|
cfg.CONF.DATABASE.reconnect_interval})
|
|
if base:
|
|
options.update({"base": base})
|
|
L2_MODEL = l2network_models_v2
|
|
db.configure_db(options)
|
|
create_vlanids()
|
|
|
|
|
|
def create_vlanids():
|
|
"""Prepopulate the vlan_bindings table"""
|
|
LOG.debug("create_vlanids() called")
|
|
session = db.get_session()
|
|
start = cfg.CONF.VLANS.vlan_start
|
|
end = cfg.CONF.VLANS.vlan_end
|
|
try:
|
|
vlanid = session.query(L2_MODEL.VlanID).one()
|
|
except exc.MultipleResultsFound:
|
|
"""
|
|
TODO (Sumit): Salvatore rightly points out that this will not handle
|
|
change in VLAN ID range across server reboots. This is currently not
|
|
a supported feature. This logic will need to change if this feature
|
|
has to be supported.
|
|
Per Dan's suggestion we just throw a server exception for now.
|
|
"""
|
|
current_start = (
|
|
int(session.query(func.min(L2_MODEL.VlanID.vlan_id)).
|
|
one()[0]))
|
|
current_end = (
|
|
int(session.query(func.max(L2_MODEL.VlanID.vlan_id)).
|
|
one()[0]))
|
|
if current_start != start or current_end != end:
|
|
LOG.debug("Old VLAN range %s-%s" % (current_start, current_end))
|
|
LOG.debug("New VLAN range %s-%s" % (start, end))
|
|
raise c_exc.UnableToChangeVlanRange(range_start=current_start,
|
|
range_end=current_end)
|
|
except exc.NoResultFound:
|
|
LOG.debug("Setting VLAN range to %s-%s" % (start, end))
|
|
while start <= end:
|
|
vlanid = L2_MODEL.VlanID(start)
|
|
session.add(vlanid)
|
|
start += 1
|
|
session.flush()
|
|
return
|
|
|
|
|
|
def get_all_vlanids():
|
|
"""Get all the vlanids"""
|
|
LOG.debug("get_all_vlanids() called")
|
|
session = db.get_session()
|
|
try:
|
|
vlanids = (session.query(L2_MODEL.VlanID).
|
|
all())
|
|
return vlanids
|
|
except exc.NoResultFound:
|
|
return []
|
|
|
|
|
|
def is_vlanid_used(vlan_id):
|
|
"""Check if a vlanid is in use"""
|
|
LOG.debug("is_vlanid_used() called")
|
|
session = db.get_session()
|
|
try:
|
|
vlanid = (session.query(L2_MODEL.VlanID).
|
|
filter_by(vlan_id=vlan_id).
|
|
one())
|
|
return vlanid["vlan_used"]
|
|
except exc.NoResultFound:
|
|
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
|
|
|
|
|
|
def release_vlanid(vlan_id):
|
|
"""Set the vlanid state to be unused, and delete if not in range"""
|
|
LOG.debug("release_vlanid() called")
|
|
session = db.get_session()
|
|
try:
|
|
vlanid = (session.query(L2_MODEL.VlanID).
|
|
filter_by(vlan_id=vlan_id).
|
|
one())
|
|
vlanid["vlan_used"] = False
|
|
if (vlan_id >= cfg.CONF.VLANS.vlan_start and
|
|
vlan_id <= cfg.CONF.VLANS.vlan_end):
|
|
session.merge(vlanid)
|
|
else:
|
|
session.delete(vlanid)
|
|
session.flush()
|
|
return vlanid["vlan_used"]
|
|
except exc.NoResultFound:
|
|
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
|
|
return
|
|
|
|
|
|
def delete_vlanid(vlan_id):
|
|
"""Delete a vlanid entry from db"""
|
|
LOG.debug("delete_vlanid() called")
|
|
session = db.get_session()
|
|
try:
|
|
vlanid = (session.query(L2_MODEL.VlanID).
|
|
filter_by(vlan_id=vlan_id).
|
|
one())
|
|
session.delete(vlanid)
|
|
session.flush()
|
|
return vlanid
|
|
except exc.NoResultFound:
|
|
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
|
|
|
|
|
|
def reserve_vlanid():
|
|
"""Reserve the first unused vlanid"""
|
|
LOG.debug("reserve_vlanid() called")
|
|
session = db.get_session()
|
|
try:
|
|
rvlan = (session.query(L2_MODEL.VlanID).
|
|
first())
|
|
if not rvlan:
|
|
create_vlanids()
|
|
|
|
rvlan = (session.query(L2_MODEL.VlanID).
|
|
filter_by(vlan_used=False).
|
|
first())
|
|
if not rvlan:
|
|
raise c_exc.VlanIDNotAvailable()
|
|
|
|
rvlanid = (session.query(L2_MODEL.VlanID).
|
|
filter_by(vlan_id=rvlan["vlan_id"]).
|
|
one())
|
|
rvlanid["vlan_used"] = True
|
|
session.merge(rvlanid)
|
|
session.flush()
|
|
return rvlan["vlan_id"]
|
|
except exc.NoResultFound:
|
|
raise c_exc.VlanIDNotAvailable()
|
|
|
|
|
|
def reserve_specific_vlanid(vlan_id):
|
|
"""Reserve a specific vlanid"""
|
|
LOG.debug("reserve_specific_vlanid() called")
|
|
if vlan_id < 1 or vlan_id > 4094:
|
|
msg = _("Specified VLAN %s outside legal range (1-4094)") % vlan_id
|
|
raise q_exc.InvalidInput(error_message=msg)
|
|
session = db.get_session()
|
|
try:
|
|
rvlanid = (session.query(l2network_models.VlanID).
|
|
filter_by(vlan_id=vlan_id).
|
|
one())
|
|
if rvlanid["vlan_used"]:
|
|
raise q_exc.VlanIdInUse(vlan_id=vlan_id)
|
|
LOG.debug("reserving dynamic vlanid %s" % vlan_id)
|
|
rvlanid["vlan_used"] = True
|
|
session.merge(rvlanid)
|
|
except exc.NoResultFound:
|
|
rvlanid = l2network_models.VlanID(vlan_id)
|
|
LOG.debug("reserving non-dynamic vlanid %s" % vlan_id)
|
|
rvlanid["vlan_used"] = True
|
|
session.add(rvlanid)
|
|
session.flush()
|
|
|
|
|
|
def get_all_vlanids_used():
|
|
"""Get all the vlanids used"""
|
|
LOG.debug("get_all_vlanids() called")
|
|
session = db.get_session()
|
|
try:
|
|
vlanids = (session.query(L2_MODEL.VlanID).
|
|
filter_by(vlan_used=True).
|
|
all())
|
|
return vlanids
|
|
except exc.NoResultFound:
|
|
return []
|
|
|
|
|
|
def get_all_vlan_bindings():
|
|
"""List all the vlan to network associations"""
|
|
LOG.debug("get_all_vlan_bindings() called")
|
|
session = db.get_session()
|
|
try:
|
|
bindings = (session.query(L2_MODEL.VlanBinding).
|
|
all())
|
|
return bindings
|
|
except exc.NoResultFound:
|
|
return []
|
|
|
|
|
|
def get_vlan_binding(netid):
|
|
"""List the vlan given a network_id"""
|
|
LOG.debug("get_vlan_binding() called")
|
|
session = db.get_session()
|
|
try:
|
|
binding = (session.query(L2_MODEL.VlanBinding).
|
|
filter_by(network_id=netid).
|
|
one())
|
|
return binding
|
|
except exc.NoResultFound:
|
|
raise c_exc.NetworkVlanBindingNotFound(network_id=netid)
|
|
|
|
|
|
def add_vlan_binding(vlanid, netid):
|
|
"""Add a vlan to network association"""
|
|
LOG.debug("add_vlan_binding() called")
|
|
session = db.get_session()
|
|
try:
|
|
binding = (session.query(L2_MODEL.VlanBinding).
|
|
filter_by(vlan_id=vlanid).
|
|
one())
|
|
raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
|
|
network_id=netid)
|
|
except exc.NoResultFound:
|
|
binding = L2_MODEL.VlanBinding(vlanid, netid)
|
|
session.add(binding)
|
|
session.flush()
|
|
return binding
|
|
|
|
|
|
def remove_vlan_binding(netid):
|
|
"""Remove a vlan to network association"""
|
|
LOG.debug("remove_vlan_binding() called")
|
|
session = db.get_session()
|
|
try:
|
|
binding = (session.query(L2_MODEL.VlanBinding).
|
|
filter_by(network_id=netid).
|
|
one())
|
|
session.delete(binding)
|
|
session.flush()
|
|
return binding
|
|
except exc.NoResultFound:
|
|
pass
|
|
|
|
|
|
def update_vlan_binding(netid, newvlanid=None):
|
|
"""Update a vlan to network association"""
|
|
LOG.debug("update_vlan_binding() called")
|
|
session = db.get_session()
|
|
try:
|
|
binding = (session.query(L2_MODEL.VlanBinding).
|
|
filter_by(network_id=netid).
|
|
one())
|
|
if newvlanid:
|
|
binding["vlan_id"] = newvlanid
|
|
session.merge(binding)
|
|
session.flush()
|
|
return binding
|
|
except exc.NoResultFound:
|
|
raise q_exc.NetworkNotFound(net_id=netid)
|
|
|
|
|
|
def get_port_from_device(device):
|
|
"""Get port from database"""
|
|
LOG.debug("get_port_from_device() called")
|
|
session = db.get_session()
|
|
ports = session.query(models_v2.Port).all()
|
|
if not ports:
|
|
return
|
|
for port in ports:
|
|
if port['id'].startswith(device):
|
|
return port
|
|
return
|
|
|
|
|
|
def set_port_status(port_id, status):
|
|
"""Set the port status"""
|
|
LOG.debug("set_port_status as %s called", status)
|
|
session = db.get_session()
|
|
try:
|
|
port = session.query(models_v2.Port).filter_by(id=port_id).one()
|
|
port['status'] = status
|
|
if status == api_common.OperationalStatus.DOWN:
|
|
port['device_id'] = ''
|
|
session.merge(port)
|
|
session.flush()
|
|
except exc.NoResultFound:
|
|
raise q_exc.PortNotFound(port_id=port_id)
|