381 lines
17 KiB
Python
381 lines
17 KiB
Python
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
|
#
|
|
# 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 oslo_db import exception as oslo_db_exc
|
|
from oslo_log import log as logging
|
|
from oslo_utils import uuidutils
|
|
import sqlalchemy as sa
|
|
from sqlalchemy import orm
|
|
from sqlalchemy.orm import exc as sa_exc
|
|
|
|
from neutron.api.v2 import attributes as attr
|
|
from neutron.common import exceptions as n_exc
|
|
from neutron.db import common_db_mixin as common_db
|
|
from neutron.db import model_base
|
|
from neutron.db import models_v2
|
|
from neutron.extensions import bgp as bgp_ext
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class BgpSpeakerPeerBinding(model_base.BASEV2):
|
|
|
|
"""Represents a mapping between BGP speaker and BGP peer"""
|
|
|
|
__tablename__ = 'bgp_speaker_peer_bindings'
|
|
|
|
bgp_speaker_id = sa.Column(sa.String(length=36),
|
|
sa.ForeignKey('bgp_speakers.id',
|
|
ondelete='CASCADE'),
|
|
nullable=False,
|
|
primary_key=True)
|
|
bgp_peer_id = sa.Column(sa.String(length=36),
|
|
sa.ForeignKey('bgp_peers.id',
|
|
ondelete='CASCADE'),
|
|
nullable=False,
|
|
primary_key=True)
|
|
|
|
|
|
class BgpSpeakerNetworkBinding(model_base.BASEV2):
|
|
|
|
"""Represents a mapping between a network and BGP speaker"""
|
|
|
|
__tablename__ = 'bgp_speaker_network_bindings'
|
|
|
|
bgp_speaker_id = sa.Column(sa.String(length=36),
|
|
sa.ForeignKey('bgp_speakers.id',
|
|
ondelete='CASCADE'),
|
|
nullable=False,
|
|
primary_key=True)
|
|
network_id = sa.Column(sa.String(length=36),
|
|
sa.ForeignKey('networks.id',
|
|
ondelete='CASCADE'),
|
|
nullable=False,
|
|
primary_key=True)
|
|
ip_version = sa.Column(sa.Integer, nullable=False, autoincrement=False,
|
|
primary_key=True)
|
|
|
|
|
|
class BgpSpeaker(model_base.BASEV2,
|
|
model_base.HasId,
|
|
model_base.HasTenant):
|
|
|
|
"""Represents a BGP speaker"""
|
|
|
|
__tablename__ = 'bgp_speakers'
|
|
|
|
name = sa.Column(sa.String(attr.NAME_MAX_LEN), nullable=False)
|
|
local_as = sa.Column(sa.Integer, nullable=False, autoincrement=False)
|
|
advertise_floating_ip_host_routes = sa.Column(sa.Boolean, nullable=False)
|
|
advertise_tenant_networks = sa.Column(sa.Boolean, nullable=False)
|
|
peers = orm.relationship(BgpSpeakerPeerBinding,
|
|
backref='bgp_speaker_peer_bindings',
|
|
cascade='all, delete, delete-orphan',
|
|
lazy='joined')
|
|
networks = orm.relationship(BgpSpeakerNetworkBinding,
|
|
backref='bgp_speaker_network_bindings',
|
|
cascade='all, delete, delete-orphan',
|
|
lazy='joined')
|
|
ip_version = sa.Column(sa.Integer, nullable=False, autoincrement=False)
|
|
|
|
|
|
class BgpPeer(model_base.BASEV2,
|
|
model_base.HasId,
|
|
model_base.HasTenant):
|
|
|
|
"""Represents a BGP routing peer."""
|
|
|
|
__tablename__ = 'bgp_peers'
|
|
|
|
name = sa.Column(sa.String(attr.NAME_MAX_LEN), nullable=False)
|
|
peer_ip = sa.Column(sa.String(64),
|
|
nullable=False)
|
|
remote_as = sa.Column(sa.Integer, nullable=False, autoincrement=False)
|
|
auth_type = sa.Column(sa.String(16), nullable=False)
|
|
password = sa.Column(sa.String(255), nullable=True)
|
|
|
|
|
|
class BgpDbMixin(common_db.CommonDbMixin):
|
|
|
|
def create_bgp_speaker(self, context, bgp_speaker):
|
|
uuid = uuidutils.generate_uuid()
|
|
self._save_bgp_speaker(context, bgp_speaker, uuid)
|
|
return self.get_bgp_speaker(context, uuid)
|
|
|
|
def get_bgp_speakers(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
with context.session.begin(subtransactions=True):
|
|
return self._get_collection(context, BgpSpeaker,
|
|
self._make_bgp_speaker_dict,
|
|
filters=filters, fields=fields,
|
|
sorts=sorts, limit=limit,
|
|
page_reverse=page_reverse)
|
|
|
|
def get_bgp_speaker(self, context, bgp_speaker_id, fields=None):
|
|
with context.session.begin(subtransactions=True):
|
|
bgp_speaker = self._get_bgp_speaker(context, bgp_speaker_id)
|
|
return self._make_bgp_speaker_dict(bgp_speaker, fields)
|
|
|
|
def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker):
|
|
bp = bgp_speaker[bgp_ext.BGP_SPEAKER_BODY_KEY_NAME]
|
|
with context.session.begin(subtransactions=True):
|
|
bgp_speaker_db = self._get_bgp_speaker(context, bgp_speaker_id)
|
|
bgp_speaker_db.update(bp)
|
|
|
|
bgp_speaker_dict = self._make_bgp_speaker_dict(bgp_speaker_db)
|
|
return bgp_speaker_dict
|
|
|
|
def _save_bgp_speaker(self, context, bgp_speaker, uuid):
|
|
ri = bgp_speaker[bgp_ext.BGP_SPEAKER_BODY_KEY_NAME]
|
|
ri['tenant_id'] = context.tenant_id
|
|
with context.session.begin(subtransactions=True):
|
|
res_keys = ['local_as', 'tenant_id', 'name', 'ip_version',
|
|
'advertise_floating_ip_host_routes',
|
|
'advertise_tenant_networks']
|
|
res = dict((k, ri[k]) for k in res_keys)
|
|
res['id'] = uuid
|
|
bgp_speaker_db = BgpSpeaker(**res)
|
|
context.session.add(bgp_speaker_db)
|
|
|
|
def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
|
|
bgp_peer_id = self._get_id_for(bgp_peer_info, 'bgp_peer_id')
|
|
self._save_bgp_speaker_peer_binding(context,
|
|
bgp_speaker_id,
|
|
bgp_peer_id)
|
|
return {'bgp_peer_id': bgp_peer_id}
|
|
|
|
def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
|
|
bgp_peer_id = self._get_id_for(bgp_peer_info, 'bgp_peer_id')
|
|
self._remove_bgp_speaker_peer_binding(context,
|
|
bgp_speaker_id,
|
|
bgp_peer_id)
|
|
return {'bgp_peer_id': bgp_peer_id}
|
|
|
|
def add_gateway_network(self, context, bgp_speaker_id, network_info):
|
|
network_id = self._get_id_for(network_info, 'network_id')
|
|
with context.session.begin(subtransactions=True):
|
|
try:
|
|
self._save_bgp_speaker_network_binding(context,
|
|
bgp_speaker_id,
|
|
network_id)
|
|
except oslo_db_exc.DBDuplicateEntry:
|
|
raise bgp_ext.BgpSpeakerNetworkBindingError(
|
|
network_id=network_id,
|
|
bgp_speaker_id=bgp_speaker_id)
|
|
return {'network_id': network_id}
|
|
|
|
def remove_gateway_network(self, context, bgp_speaker_id, network_info):
|
|
with context.session.begin(subtransactions=True):
|
|
network_id = self._get_id_for(network_info, 'network_id')
|
|
self._remove_bgp_speaker_network_binding(context,
|
|
bgp_speaker_id,
|
|
network_id)
|
|
return {'network_id': network_id}
|
|
|
|
def delete_bgp_speaker(self, context, bgp_speaker_id):
|
|
with context.session.begin(subtransactions=True):
|
|
bgp_speaker_db = self._get_bgp_speaker(context, bgp_speaker_id)
|
|
context.session.delete(bgp_speaker_db)
|
|
|
|
def create_bgp_peer(self, context, bgp_peer):
|
|
ri = bgp_peer[bgp_ext.BGP_PEER_BODY_KEY_NAME]
|
|
with context.session.begin(subtransactions=True):
|
|
res_keys = ['tenant_id', 'name', 'remote_as', 'peer_ip',
|
|
'auth_type', 'password']
|
|
res = dict((k, ri[k]) for k in res_keys)
|
|
res['id'] = uuidutils.generate_uuid()
|
|
bgp_peer_db = BgpPeer(**res)
|
|
context.session.add(bgp_peer_db)
|
|
peer = self._make_bgp_peer_dict(bgp_peer_db)
|
|
peer.pop('password')
|
|
return peer
|
|
|
|
def get_bgp_peers(self, context, fields=None, filters=None, sorts=None,
|
|
limit=None, marker=None, page_reverse=False):
|
|
return self._get_collection(context, BgpPeer,
|
|
self._make_bgp_peer_dict,
|
|
filters=filters, fields=fields,
|
|
sorts=sorts, limit=limit,
|
|
page_reverse=page_reverse)
|
|
|
|
def get_bgp_peer(self, context, bgp_peer_id, fields=None):
|
|
bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id)
|
|
return self._make_bgp_peer_dict(bgp_peer_db, fields=fields)
|
|
|
|
def delete_bgp_peer(self, context, bgp_peer_id):
|
|
with context.session.begin(subtransactions=True):
|
|
bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id)
|
|
context.session.delete(bgp_peer_db)
|
|
|
|
def update_bgp_peer(self, context, bgp_peer_id, bgp_peer):
|
|
bp = bgp_peer[bgp_ext.BGP_PEER_BODY_KEY_NAME]
|
|
with context.session.begin(subtransactions=True):
|
|
bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id)
|
|
if ((bp['password'] is not None) and
|
|
(bgp_peer_db['auth_type'] == 'none')):
|
|
raise bgp_ext.BgpPeerNotAuthenticated(bgp_peer_id=bgp_peer_id)
|
|
bgp_peer_db.update(bp)
|
|
|
|
bgp_peer_dict = self._make_bgp_peer_dict(bgp_peer_db)
|
|
return bgp_peer_dict
|
|
|
|
def _get_bgp_speaker(self, context, bgp_speaker_id):
|
|
try:
|
|
return self._get_by_id(context, BgpSpeaker,
|
|
bgp_speaker_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id)
|
|
|
|
def get_advertised_routes(self, context, bgp_speaker_id):
|
|
return self._make_advertised_routes_dict([])
|
|
|
|
def _get_id_for(self, resource, id_name):
|
|
try:
|
|
return resource.get(id_name)
|
|
except AttributeError:
|
|
msg = _("%s must be specified") % id_name
|
|
raise n_exc.BadRequest(resource=bgp_ext.BGP_SPEAKER_RESOURCE_NAME,
|
|
msg=msg)
|
|
|
|
def _get_bgp_peers_by_bgp_speaker_binding(self, context, bgp_speaker_id):
|
|
with context.session.begin(subtransactions=True):
|
|
query = context.session.query(BgpPeer)
|
|
query = query.filter(
|
|
BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id,
|
|
BgpSpeakerPeerBinding.bgp_peer_id == BgpPeer.id)
|
|
return query.all()
|
|
|
|
def _save_bgp_speaker_peer_binding(self, context, bgp_speaker_id,
|
|
bgp_peer_id):
|
|
with context.session.begin(subtransactions=True):
|
|
try:
|
|
bgp_speaker = self._get_by_id(context, BgpSpeaker,
|
|
bgp_speaker_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id)
|
|
|
|
try:
|
|
bgp_peer = self._get_by_id(context, BgpPeer,
|
|
bgp_peer_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpPeerNotFound(id=bgp_peer_id)
|
|
|
|
peers = self._get_bgp_peers_by_bgp_speaker_binding(context,
|
|
bgp_speaker_id)
|
|
self._validate_peer_ips(bgp_speaker_id, peers, bgp_peer)
|
|
binding = BgpSpeakerPeerBinding(bgp_speaker_id=bgp_speaker.id,
|
|
bgp_peer_id=bgp_peer.id)
|
|
context.session.add(binding)
|
|
|
|
def _validate_peer_ips(self, bgp_speaker_id, current_peers, new_peer):
|
|
for peer in current_peers:
|
|
if peer.peer_ip == new_peer.peer_ip:
|
|
raise bgp_ext.DuplicateBgpPeerIpException(
|
|
bgp_peer_id=new_peer.id,
|
|
peer_ip=new_peer.peer_ip,
|
|
bgp_speaker_id=bgp_speaker_id)
|
|
|
|
def _remove_bgp_speaker_peer_binding(self, context, bgp_speaker_id,
|
|
bgp_peer_id):
|
|
with context.session.begin(subtransactions=True):
|
|
|
|
try:
|
|
binding = self._get_bgp_speaker_peer_binding(context,
|
|
bgp_speaker_id,
|
|
bgp_peer_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpSpeakerPeerNotAssociated(
|
|
bgp_peer_id=bgp_peer_id,
|
|
bgp_speaker_id=bgp_speaker_id)
|
|
context.session.delete(binding)
|
|
|
|
def _save_bgp_speaker_network_binding(self,
|
|
context,
|
|
bgp_speaker_id,
|
|
network_id):
|
|
with context.session.begin(subtransactions=True):
|
|
try:
|
|
bgp_speaker = self._get_by_id(context, BgpSpeaker,
|
|
bgp_speaker_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id)
|
|
|
|
try:
|
|
network = self._get_by_id(context, models_v2.Network,
|
|
network_id)
|
|
except sa_exc.NoResultFound:
|
|
raise n_exc.NetworkNotFound(net_id=network_id)
|
|
|
|
binding = BgpSpeakerNetworkBinding(
|
|
bgp_speaker_id=bgp_speaker.id,
|
|
network_id=network.id,
|
|
ip_version=bgp_speaker.ip_version)
|
|
context.session.add(binding)
|
|
|
|
def _remove_bgp_speaker_network_binding(self, context,
|
|
bgp_speaker_id, network_id):
|
|
with context.session.begin(subtransactions=True):
|
|
|
|
try:
|
|
binding = self._get_bgp_speaker_network_binding(
|
|
context,
|
|
bgp_speaker_id,
|
|
network_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpSpeakerNetworkNotAssociated(
|
|
network_id=network_id,
|
|
bgp_speaker_id=bgp_speaker_id)
|
|
context.session.delete(binding)
|
|
|
|
def _make_bgp_speaker_dict(self, bgp_speaker, fields=None):
|
|
attrs = {'id', 'local_as', 'tenant_id', 'name', 'ip_version',
|
|
'advertise_floating_ip_host_routes',
|
|
'advertise_tenant_networks'}
|
|
peer_bindings = bgp_speaker['peers']
|
|
network_bindings = bgp_speaker['networks']
|
|
res = dict((k, bgp_speaker[k]) for k in attrs)
|
|
res['peers'] = [x.bgp_peer_id for x in peer_bindings]
|
|
res['networks'] = [x.network_id for x in network_bindings]
|
|
return self._fields(res, fields)
|
|
|
|
def _make_advertised_routes_dict(self, routes):
|
|
return {'advertised_routes': list(routes)}
|
|
|
|
def _get_bgp_peer(self, context, bgp_peer_id):
|
|
try:
|
|
return self._get_by_id(context, BgpPeer, bgp_peer_id)
|
|
except sa_exc.NoResultFound:
|
|
raise bgp_ext.BgpPeerNotFound(id=bgp_peer_id)
|
|
|
|
def _get_bgp_speaker_peer_binding(self, context,
|
|
bgp_speaker_id, bgp_peer_id):
|
|
query = self._model_query(context, BgpSpeakerPeerBinding)
|
|
return query.filter(
|
|
BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id,
|
|
BgpSpeakerPeerBinding.bgp_peer_id == bgp_peer_id).one()
|
|
|
|
def _get_bgp_speaker_network_binding(self, context,
|
|
bgp_speaker_id, network_id):
|
|
query = self._model_query(context, BgpSpeakerNetworkBinding)
|
|
return query.filter(bgp_speaker_id == bgp_speaker_id,
|
|
network_id == network_id).one()
|
|
|
|
def _make_bgp_peer_dict(self, bgp_peer, fields=None):
|
|
attrs = ['tenant_id', 'id', 'name', 'peer_ip', 'remote_as',
|
|
'auth_type', 'password']
|
|
res = dict((k, bgp_peer[k]) for k in attrs)
|
|
return self._fields(res, fields)
|