1a116d24a9
H302 violation is reported by flake8 when importing separated objects from modules instead of importing the whole module. e.g. from package.module import function function() is changed to from package import module module.function() Change-Id: I83372124f4fba7b94bbfb4a56a0c0ef779ee237f Partial-Bug: #1291032
397 lines
16 KiB
Python
397 lines
16 KiB
Python
# Copyright 2011 VMware, 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.
|
|
|
|
from six import moves
|
|
from sqlalchemy import func
|
|
from sqlalchemy.orm import exc
|
|
|
|
from neutron.common import exceptions as n_exc
|
|
import neutron.db.api as db
|
|
from neutron.db import models_v2
|
|
from neutron.db import securitygroups_db as sg_db
|
|
from neutron.extensions import securitygroup as ext_sg
|
|
from neutron import manager
|
|
from neutron.openstack.common.db import exception as db_exc
|
|
from neutron.openstack.common import log as logging
|
|
from neutron.plugins.openvswitch.common import constants
|
|
from neutron.plugins.openvswitch import ovs_models_v2
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def get_network_binding(session, network_id):
|
|
session = session or db.get_session()
|
|
try:
|
|
binding = (session.query(ovs_models_v2.NetworkBinding).
|
|
filter_by(network_id=network_id).
|
|
one())
|
|
return binding
|
|
except exc.NoResultFound:
|
|
return
|
|
|
|
|
|
def add_network_binding(session, network_id, network_type,
|
|
physical_network, segmentation_id):
|
|
with session.begin(subtransactions=True):
|
|
binding = ovs_models_v2.NetworkBinding(network_id, network_type,
|
|
physical_network,
|
|
segmentation_id)
|
|
session.add(binding)
|
|
return binding
|
|
|
|
|
|
def sync_vlan_allocations(network_vlan_ranges):
|
|
"""Synchronize vlan_allocations table with configured VLAN ranges."""
|
|
|
|
session = db.get_session()
|
|
with session.begin():
|
|
# get existing allocations for all physical networks
|
|
allocations = dict()
|
|
allocs = (session.query(ovs_models_v2.VlanAllocation).
|
|
all())
|
|
for alloc in allocs:
|
|
if alloc.physical_network not in allocations:
|
|
allocations[alloc.physical_network] = set()
|
|
allocations[alloc.physical_network].add(alloc)
|
|
|
|
# process vlan ranges for each configured physical network
|
|
for physical_network, vlan_ranges in network_vlan_ranges.iteritems():
|
|
# determine current configured allocatable vlans for this
|
|
# physical network
|
|
vlan_ids = set()
|
|
for vlan_range in vlan_ranges:
|
|
vlan_ids |= set(moves.xrange(vlan_range[0], vlan_range[1] + 1))
|
|
|
|
# remove from table unallocated vlans not currently allocatable
|
|
if physical_network in allocations:
|
|
for alloc in allocations[physical_network]:
|
|
try:
|
|
# see if vlan is allocatable
|
|
vlan_ids.remove(alloc.vlan_id)
|
|
except KeyError:
|
|
# it's not allocatable, so check if its allocated
|
|
if not alloc.allocated:
|
|
# it's not, so remove it from table
|
|
LOG.debug(_("Removing vlan %(vlan_id)s on "
|
|
"physical network "
|
|
"%(physical_network)s from pool"),
|
|
{'vlan_id': alloc.vlan_id,
|
|
'physical_network': physical_network})
|
|
session.delete(alloc)
|
|
del allocations[physical_network]
|
|
|
|
# add missing allocatable vlans to table
|
|
for vlan_id in sorted(vlan_ids):
|
|
alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
|
|
session.add(alloc)
|
|
|
|
# remove from table unallocated vlans for any unconfigured physical
|
|
# networks
|
|
for allocs in allocations.itervalues():
|
|
for alloc in allocs:
|
|
if not alloc.allocated:
|
|
LOG.debug(_("Removing vlan %(vlan_id)s on physical "
|
|
"network %(physical_network)s from pool"),
|
|
{'vlan_id': alloc.vlan_id,
|
|
'physical_network': alloc.physical_network})
|
|
session.delete(alloc)
|
|
|
|
|
|
def get_vlan_allocation(physical_network, vlan_id):
|
|
session = db.get_session()
|
|
try:
|
|
alloc = (session.query(ovs_models_v2.VlanAllocation).
|
|
filter_by(physical_network=physical_network,
|
|
vlan_id=vlan_id).
|
|
one())
|
|
return alloc
|
|
except exc.NoResultFound:
|
|
return
|
|
|
|
|
|
def reserve_vlan(session):
|
|
with session.begin(subtransactions=True):
|
|
alloc = (session.query(ovs_models_v2.VlanAllocation).
|
|
filter_by(allocated=False).
|
|
with_lockmode('update').
|
|
first())
|
|
if alloc:
|
|
LOG.debug(_("Reserving vlan %(vlan_id)s on physical network "
|
|
"%(physical_network)s from pool"),
|
|
{'vlan_id': alloc.vlan_id,
|
|
'physical_network': alloc.physical_network})
|
|
alloc.allocated = True
|
|
return (alloc.physical_network, alloc.vlan_id)
|
|
raise n_exc.NoNetworkAvailable()
|
|
|
|
|
|
def reserve_specific_vlan(session, physical_network, vlan_id):
|
|
with session.begin(subtransactions=True):
|
|
try:
|
|
alloc = (session.query(ovs_models_v2.VlanAllocation).
|
|
filter_by(physical_network=physical_network,
|
|
vlan_id=vlan_id).
|
|
with_lockmode('update').
|
|
one())
|
|
if alloc.allocated:
|
|
if vlan_id == constants.FLAT_VLAN_ID:
|
|
raise n_exc.FlatNetworkInUse(
|
|
physical_network=physical_network)
|
|
else:
|
|
raise n_exc.VlanIdInUse(vlan_id=vlan_id,
|
|
physical_network=physical_network)
|
|
LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
|
|
"network %(physical_network)s from pool"),
|
|
{'vlan_id': vlan_id,
|
|
'physical_network': physical_network})
|
|
alloc.allocated = True
|
|
except exc.NoResultFound:
|
|
LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
|
|
"network %(physical_network)s outside pool"),
|
|
{'vlan_id': vlan_id,
|
|
'physical_network': physical_network})
|
|
alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
|
|
alloc.allocated = True
|
|
session.add(alloc)
|
|
|
|
|
|
def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
|
|
with session.begin(subtransactions=True):
|
|
try:
|
|
alloc = (session.query(ovs_models_v2.VlanAllocation).
|
|
filter_by(physical_network=physical_network,
|
|
vlan_id=vlan_id).
|
|
with_lockmode('update').
|
|
one())
|
|
alloc.allocated = False
|
|
inside = False
|
|
for vlan_range in network_vlan_ranges.get(physical_network, []):
|
|
if vlan_id >= vlan_range[0] and vlan_id <= vlan_range[1]:
|
|
inside = True
|
|
break
|
|
if not inside:
|
|
session.delete(alloc)
|
|
LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
|
|
"%(physical_network)s outside pool"),
|
|
{'vlan_id': vlan_id,
|
|
'physical_network': physical_network})
|
|
else:
|
|
LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
|
|
"%(physical_network)s to pool"),
|
|
{'vlan_id': vlan_id,
|
|
'physical_network': physical_network})
|
|
except exc.NoResultFound:
|
|
LOG.warning(_("vlan_id %(vlan_id)s on physical network "
|
|
"%(physical_network)s not found"),
|
|
{'vlan_id': vlan_id,
|
|
'physical_network': physical_network})
|
|
|
|
|
|
def sync_tunnel_allocations(tunnel_id_ranges):
|
|
"""Synchronize tunnel_allocations table with configured tunnel ranges."""
|
|
|
|
# determine current configured allocatable tunnels
|
|
tunnel_ids = set()
|
|
for tunnel_id_range in tunnel_id_ranges:
|
|
tun_min, tun_max = tunnel_id_range
|
|
if tun_max + 1 - tun_min > 1000000:
|
|
LOG.error(_("Skipping unreasonable tunnel ID range "
|
|
"%(tun_min)s:%(tun_max)s"),
|
|
{'tun_min': tun_min, 'tun_max': tun_max})
|
|
else:
|
|
tunnel_ids |= set(moves.xrange(tun_min, tun_max + 1))
|
|
|
|
session = db.get_session()
|
|
with session.begin():
|
|
# remove from table unallocated tunnels not currently allocatable
|
|
allocs = (session.query(ovs_models_v2.TunnelAllocation).
|
|
all())
|
|
for alloc in allocs:
|
|
try:
|
|
# see if tunnel is allocatable
|
|
tunnel_ids.remove(alloc.tunnel_id)
|
|
except KeyError:
|
|
# it's not allocatable, so check if its allocated
|
|
if not alloc.allocated:
|
|
# it's not, so remove it from table
|
|
LOG.debug(_("Removing tunnel %s from pool"),
|
|
alloc.tunnel_id)
|
|
session.delete(alloc)
|
|
|
|
# add missing allocatable tunnels to table
|
|
for tunnel_id in sorted(tunnel_ids):
|
|
alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
|
|
session.add(alloc)
|
|
|
|
|
|
def get_tunnel_allocation(tunnel_id):
|
|
session = db.get_session()
|
|
try:
|
|
alloc = (session.query(ovs_models_v2.TunnelAllocation).
|
|
filter_by(tunnel_id=tunnel_id).
|
|
with_lockmode('update').
|
|
one())
|
|
return alloc
|
|
except exc.NoResultFound:
|
|
return
|
|
|
|
|
|
def reserve_tunnel(session):
|
|
with session.begin(subtransactions=True):
|
|
alloc = (session.query(ovs_models_v2.TunnelAllocation).
|
|
filter_by(allocated=False).
|
|
with_lockmode('update').
|
|
first())
|
|
if alloc:
|
|
LOG.debug(_("Reserving tunnel %s from pool"), alloc.tunnel_id)
|
|
alloc.allocated = True
|
|
return alloc.tunnel_id
|
|
raise n_exc.NoNetworkAvailable()
|
|
|
|
|
|
def reserve_specific_tunnel(session, tunnel_id):
|
|
with session.begin(subtransactions=True):
|
|
try:
|
|
alloc = (session.query(ovs_models_v2.TunnelAllocation).
|
|
filter_by(tunnel_id=tunnel_id).
|
|
with_lockmode('update').
|
|
one())
|
|
if alloc.allocated:
|
|
raise n_exc.TunnelIdInUse(tunnel_id=tunnel_id)
|
|
LOG.debug(_("Reserving specific tunnel %s from pool"), tunnel_id)
|
|
alloc.allocated = True
|
|
except exc.NoResultFound:
|
|
LOG.debug(_("Reserving specific tunnel %s outside pool"),
|
|
tunnel_id)
|
|
alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
|
|
alloc.allocated = True
|
|
session.add(alloc)
|
|
|
|
|
|
def release_tunnel(session, tunnel_id, tunnel_id_ranges):
|
|
with session.begin(subtransactions=True):
|
|
try:
|
|
alloc = (session.query(ovs_models_v2.TunnelAllocation).
|
|
filter_by(tunnel_id=tunnel_id).
|
|
with_lockmode('update').
|
|
one())
|
|
alloc.allocated = False
|
|
inside = False
|
|
for tunnel_id_range in tunnel_id_ranges:
|
|
if (tunnel_id >= tunnel_id_range[0]
|
|
and tunnel_id <= tunnel_id_range[1]):
|
|
inside = True
|
|
break
|
|
if not inside:
|
|
session.delete(alloc)
|
|
LOG.debug(_("Releasing tunnel %s outside pool"), tunnel_id)
|
|
else:
|
|
LOG.debug(_("Releasing tunnel %s to pool"), tunnel_id)
|
|
except exc.NoResultFound:
|
|
LOG.warning(_("tunnel_id %s not found"), tunnel_id)
|
|
|
|
|
|
def get_port(port_id):
|
|
session = db.get_session()
|
|
try:
|
|
port = session.query(models_v2.Port).filter_by(id=port_id).one()
|
|
except exc.NoResultFound:
|
|
port = None
|
|
return port
|
|
|
|
|
|
def get_port_from_device(port_id):
|
|
"""Get port from database."""
|
|
LOG.debug(_("get_port_with_securitygroups() called:port_id=%s"), port_id)
|
|
session = db.get_session()
|
|
sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
|
|
|
|
query = session.query(models_v2.Port,
|
|
sg_db.SecurityGroupPortBinding.security_group_id)
|
|
query = query.outerjoin(sg_db.SecurityGroupPortBinding,
|
|
models_v2.Port.id == sg_binding_port)
|
|
query = query.filter(models_v2.Port.id == port_id)
|
|
port_and_sgs = query.all()
|
|
if not port_and_sgs:
|
|
return None
|
|
port = port_and_sgs[0][0]
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
port_dict = plugin._make_port_dict(port)
|
|
port_dict[ext_sg.SECURITYGROUPS] = [
|
|
sg_id for port_, sg_id in port_and_sgs if sg_id]
|
|
port_dict['security_group_rules'] = []
|
|
port_dict['security_group_source_groups'] = []
|
|
port_dict['fixed_ips'] = [ip['ip_address']
|
|
for ip in port['fixed_ips']]
|
|
return port_dict
|
|
|
|
|
|
def set_port_status(port_id, status):
|
|
session = db.get_session()
|
|
try:
|
|
port = session.query(models_v2.Port).filter_by(id=port_id).one()
|
|
port['status'] = status
|
|
session.merge(port)
|
|
session.flush()
|
|
except exc.NoResultFound:
|
|
raise n_exc.PortNotFound(port_id=port_id)
|
|
|
|
|
|
def get_tunnel_endpoints():
|
|
session = db.get_session()
|
|
|
|
tunnels = session.query(ovs_models_v2.TunnelEndpoint)
|
|
return [{'id': tunnel.id,
|
|
'ip_address': tunnel.ip_address} for tunnel in tunnels]
|
|
|
|
|
|
def _generate_tunnel_id(session):
|
|
max_tunnel_id = session.query(
|
|
func.max(ovs_models_v2.TunnelEndpoint.id)).scalar() or 0
|
|
return max_tunnel_id + 1
|
|
|
|
|
|
def add_tunnel_endpoint(ip, max_retries=10):
|
|
"""Return the endpoint of the given IP address or generate a new one."""
|
|
|
|
# NOTE(rpodolyaka): generation of a new tunnel endpoint must be put into a
|
|
# repeatedly executed transactional block to ensure it
|
|
# doesn't conflict with any other concurrently executed
|
|
# DB transactions in spite of the specified transactions
|
|
# isolation level value
|
|
for i in moves.xrange(max_retries):
|
|
LOG.debug(_('Adding a tunnel endpoint for %s'), ip)
|
|
try:
|
|
session = db.get_session()
|
|
with session.begin(subtransactions=True):
|
|
tunnel = (session.query(ovs_models_v2.TunnelEndpoint).
|
|
filter_by(ip_address=ip).with_lockmode('update').
|
|
first())
|
|
|
|
if tunnel is None:
|
|
tunnel_id = _generate_tunnel_id(session)
|
|
tunnel = ovs_models_v2.TunnelEndpoint(ip, tunnel_id)
|
|
session.add(tunnel)
|
|
|
|
return tunnel
|
|
except db_exc.DBDuplicateEntry:
|
|
# a concurrent transaction has been committed, try again
|
|
LOG.debug(_('Adding a tunnel endpoint failed due to a concurrent'
|
|
'transaction had been committed (%s attempts left)'),
|
|
max_retries - (i + 1))
|
|
|
|
raise n_exc.NeutronException(
|
|
message=_('Unable to generate a new tunnel id'))
|