Initial proposal with db layer, API extension and client extension

This initial commit is tightly integrated with current neutron
master.
It extends the Neutron API;
It extends python-neutronclient accordingly;
It adds an alembic scripts to create tables in the neutron
database;
A dummy driver is also proposed.
Change-Id: Idaecac54bbbec0d978f2e889faed5e35fc00bc76
This commit is contained in:
mathieu-rohon
2015年03月12日 17:35:11 +01:00
parent 8cf13efde8
commit a3e7270d29

View File

@@ -12,4 +12,24 @@ API and Framework to interconnect bgpvpn to neutron networks
Features
--------
* TODO
to be able to test this framework, you have to :
-clone this repo and install the python package :
#git clone http://git.openstack.org/cgit/stackforge/networking-bgpvpn
#sudo python setup.py develop
-run the latest devstack (and let it fetch latest openstack code)
with the following options :
Q_SERVICE_PLUGIN_CLASSES=networking_bgpvpn.neutron.services.bgpvpn.plugin.BGPVPNPlugin
[[post-config|/$NEUTRON_CONF]]
[service_providers]
service_provider=BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.bgpvpn.service_drivers.dummy.dummyBGPVPNDriver:default
-update the db with :
#/usr/local/bin/bgpvpn-db-manage --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head
-bgpvpn-connection-create/update/delete/show/list commands will be available with the neutron client
for example :
#. openrc admin admin
#neutron bgpvpn-connection-create --route-targets 64512:1
#neutron bgpvpn-connection-list

View File

View File

View File

@@ -0,0 +1,248 @@
# Copyright (c) 2015 Orange.
# 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.
import sqlalchemy as sa
from neutron.common import exceptions as q_exc
from neutron.db import common_db_mixin
from neutron.db import model_base
from neutron.db import models_v2
from neutron.openstack.common import uuidutils
from oslo_log import log
from sqlalchemy import orm
from sqlalchemy.orm import exc
from networking_bgpvpn.neutron.extensions.bgpvpn.bgpvpn import BGPVPNPluginBase
LOG = log.getLogger(__name__)
class BGPVPNConnection(model_base.BASEV2,
models_v2.HasId,
models_v2.HasTenant):
"""Represents a BGPVPNConnection Object."""
__tablename__ = 'bgpvpn_connections'
name = sa.Column(sa.String(255))
type = sa.Column(sa.Enum("l2", "l3",
name="bgpvpn_type"),
nullable=False)
route_targets = sa.Column(sa.String(255), nullable=False)
import_targets = sa.Column(sa.String(255), nullable=False)
export_targets = sa.Column(sa.String(255), nullable=False)
auto_aggregate = sa.Column(sa.Boolean(), nullable=False)
network_id = sa.Column(sa.String(36),
sa.ForeignKey('networks.id'))
network = orm.relationship(models_v2.Network)
class BGPVPNConnectionNotFound(q_exc.NotFound):
message = _("BgpVpnConnection %(conn_id)s could not be found")
class BGPVPNConnectionMissingRouteTarget(q_exc.BadRequest):
message = _("BgpVpnConnection could not be created. Missing one of"
" route_targets, import_targets or export_targets attribute")
class BGPVPNNetworkInUse(q_exc.NetworkInUse):
message = _("Unable to complete operation on network %(network_id)s. "
"There are one or more BGP VPN connections associated"
" to the network.")
class BGPVPNPluginDb(BGPVPNPluginBase,
common_db_mixin.CommonDbMixin):
"""BGP VPN service plugin database class using SQLAlchemy models."""
USER_READABLE_ATTRIBUTES = ['id', 'name', 'type',
'network_id', 'tenant_id']
USER_WRITABLE_ATTRIBUTES = ['name', 'network_id']
def _get_resource(self, context, model, id):
return self._get_by_id(context, model, id)
def _rt_list2str(self, list):
"""Format Route Target list to string"""
if not list:
return ''
return ','.join(list)
def _rt_str2list(self, str):
"""Format Route Target string to list"""
if not str:
return []
return str.split(',')
def _get_bgpvpn_connections_for_tenant(self, session, tenant_id, fields):
try:
qry = session.query(BGPVPNConnection)
bgpvpn_connections = qry.filter_by(tenant_id=tenant_id)
except exc.NoResultFound:
return
return [self._make_bgpvpn_connection_dict(bvc, fields=fields)
for bvc in bgpvpn_connections]
def _get_user_readable_fields(self, fields):
if fields is not None and fields:
return list(set(fields) & set(self.USER_READABLE_ATTRIBUTES))
else:
return self.USER_READABLE_ATTRIBUTES
def _make_bgpvpn_connection_dict(self,
bgpvpn_connection,
fields=None):
res = {
'id': bgpvpn_connection['id'],
'tenant_id': bgpvpn_connection['tenant_id'],
'network_id': bgpvpn_connection['network_id'],
'name': bgpvpn_connection['name'],
'type': bgpvpn_connection['type'],
'route_targets':
self._rt_str2list(bgpvpn_connection['route_targets']),
'import_targets':
self._rt_str2list(bgpvpn_connection['import_targets']),
'export_targets':
self._rt_str2list(bgpvpn_connection['export_targets']),
'auto_aggregate': bgpvpn_connection['auto_aggregate']
}
return self._fields(res, fields)
def create_bgpvpn_connection(self, context, bgpvpn_connection):
bgpvpn_conn = bgpvpn_connection['bgpvpn_connection']
# Check that route_targets is not empty
if (not bgpvpn_conn['route_targets']):
raise BGPVPNConnectionMissingRouteTarget
else:
rt = self._rt_list2str(bgpvpn_conn['route_targets'])
i_rt = self._rt_list2str(bgpvpn_conn['import_targets'])
e_rt = self._rt_list2str(bgpvpn_conn['export_targets'])
tenant_id = self._get_tenant_id_for_create(context, bgpvpn_conn)
with context.session.begin(subtransactions=True):
bgpvpn_conn_db = BGPVPNConnection(
id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=bgpvpn_conn['name'],
type=bgpvpn_conn['type'],
route_targets=rt,
import_targets=i_rt,
export_targets=e_rt,
network_id=bgpvpn_conn['network_id'],
auto_aggregate=bgpvpn_conn['auto_aggregate']
)
context.session.add(bgpvpn_conn_db)
return self._make_bgpvpn_connection_dict(bgpvpn_conn_db)
def get_bgpvpn_connections(self, context, filters=None, fields=None):
if context.is_admin:
return self._get_collection(context, BGPVPNConnection,
self._make_bgpvpn_connection_dict,
filters=filters, fields=fields)
else:
readable_fields = self._get_user_readable_fields(fields)
LOG.debug("get_bgpvpn_connections called for user, "
"readable fields = %s" % readable_fields)
return self._get_bgpvpn_connections_for_tenant(context.session,
context.tenant_id,
readable_fields)
def _get_bgpvpn_connection(self, context, id):
try:
if context.is_admin:
return self._get_resource(context, BGPVPNConnection, id)
else:
qry = context.session.query(BGPVPNConnection)
return qry.filter_by(id=id, tenant_id=context.tenant_id).one()
except exc.NoResultFound:
raise BGPVPNConnectionNotFound(conn_id=id)
def get_bgpvpn_connection(self, context, id, fields=None):
bgpvpn_connection_db = self._get_bgpvpn_connection(context, id)
LOG.debug("get_bgpvpn_connection called with fields = %s" % fields)
if not context.is_admin:
fields = self._get_user_readable_fields(fields)
LOG.debug("get_bgpvpn_connection called for user,"
"readable fields = %s" % fields)
return self._make_bgpvpn_connection_dict(bgpvpn_connection_db, fields)
def update_bgpvpn_connection(self, context, id, bgpvpn_connection):
bgpvpn_conn = bgpvpn_connection['bgpvpn_connection']
fields = None
LOG.debug("update_bgpvpn_connection called with %s for %s"
% (bgpvpn_connection, id))
with context.session.begin(subtransactions=True):
bgpvpn_connection_db = self._get_bgpvpn_connection(context, id)
if bgpvpn_conn:
# Filter only user writable attributes
if not context.is_admin:
bgpvpn_conn = {
user_attr: bgpvpn_conn[user_attr]
for user_attr in self.USER_WRITABLE_ATTRIBUTES
if user_attr in bgpvpn_conn}
fields = self.USER_READABLE_ATTRIBUTES
else:
# Format Route Target list to string
rt = self._rt_list2str(bgpvpn_conn['route_targets'])
if 'route_targets' in bgpvpn_conn:
bgpvpn_conn['route_targets'] = rt
if 'import_targets' in bgpvpn_conn:
i_rt = self._rt_list2str(bgpvpn_conn['import_targets'])
bgpvpn_conn['import_targets'] = i_rt
if 'export_targets' in bgpvpn_conn:
e_rt = self._rt_list2str(bgpvpn_conn['export_targets'])
bgpvpn_conn['export_targets'] = e_rt
bgpvpn_connection_db.update(bgpvpn_conn)
return self._make_bgpvpn_connection_dict(bgpvpn_connection_db, fields)
def delete_bgpvpn_connection(self, context, id):
with context.session.begin(subtransactions=True):
bgpvpn_connection_db = self._get_resource(
context,
BGPVPNConnection,
id)
context.session.delete(bgpvpn_connection_db)
return bgpvpn_connection_db
def find_bgpvpn_connections_for_network(self, context, network_id):
LOG.debug("get_bgpvpn_connections_for_network() called for "
"network %s" %
network_id)
try:
bgpvpn_connections = (context.session.query(BGPVPNConnection).
filter(BGPVPNConnection.network_id ==
network_id).
all())
except exc.NoResultFound:
return
return [self._make_bgpvpn_connection_dict(bvc)
for bvc in bgpvpn_connections]

View File

@@ -0,0 +1,59 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic_migrations
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
sqlalchemy.url =
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@@ -0,0 +1 @@
Generic single-database configuration.

View File

@@ -0,0 +1,86 @@
# Copyright 2014 OpenStack Foundation
#
# 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 logging import config as logging_config
from alembic import context
from neutron.db import model_base
from oslo_config import cfg
from oslo_db.sqlalchemy import session
import sqlalchemy as sa
from sqlalchemy import event
MYSQL_ENGINE = None
BGPVPN_VERSION_TABLE = 'alembic_version_bgpvpn'
config = context.config
neutron_config = config.neutron_config
logging_config.fileConfig(config.config_file_name)
target_metadata = model_base.BASEV2.metadata
def set_mysql_engine():
try:
mysql_engine = neutron_config.command.mysql_engine
except cfg.NoSuchOptError:
mysql_engine = None
global MYSQL_ENGINE
MYSQL_ENGINE = (mysql_engine or
model_base.BASEV2.__table_args__['mysql_engine'])
def run_migrations_offline():
set_mysql_engine()
kwargs = dict()
if neutron_config.database.connection:
kwargs['url'] = neutron_config.database.connection
else:
kwargs['dialect_name'] = neutron_config.database.engine
kwargs['version_table'] = BGPVPN_VERSION_TABLE
context.configure(**kwargs)
with context.begin_transaction():
context.run_migrations()
@event.listens_for(sa.Table, 'after_parent_attach')
def set_storage_engine(target, parent):
if MYSQL_ENGINE:
target.kwargs['mysql_engine'] = MYSQL_ENGINE
def run_migrations_online():
set_mysql_engine()
engine = session.create_engine(neutron_config.database.connection)
connection = engine.connect()
context.configure(
connection=connection,
target_metadata=target_metadata,
version_table=BGPVPN_VERSION_TABLE
)
try:
with context.begin_transaction():
context.run_migrations()
finally:
connection.close()
engine.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@@ -0,0 +1,22 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -0,0 +1 @@
start_networking_bgpvpn

View File

@@ -0,0 +1,48 @@
# Copyright 2014 OpenStack Foundation
#
# 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 alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'start_networking_bgpvpn'
down_revision = None
vpn_types = sa.Enum("l2", "l3", name="vpn_types")
def upgrade(active_plugins=None, options=None):
op.create_table(
u'bgpvpn_connections',
sa.Column(u'name', sa.String(255), nullable=True),
sa.Column(u'id', sa.String(length=36), nullable=False),
sa.Column(u'tenant_id', sa.String(length=255), nullable=True),
sa.Column(u'type', vpn_types, nullable=False),
sa.Column(u'route_targets', sa.String(255), nullable=False),
sa.Column(u'import_targets', sa.String(255), nullable=True),
sa.Column(u'export_targets', sa.String(255), nullable=True),
sa.Column(u'auto_aggregate', sa.Boolean(), nullable=False),
sa.Column(u'network_id', sa.String(36), nullable=True),
sa.ForeignKeyConstraint(['network_id'], [u'networks.id'], ),
sa.PrimaryKeyConstraint(u'id'),
mysql_default_charset=u'utf8',
mysql_engine='InnoDB'
)
def downgrade(active_plugins=None, options=None):
op.drop_table(u'bgpvpn_connections')
vpn_types.drop(op.get_bind(), checkfirst=False)

View File

@@ -0,0 +1,24 @@
# 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 neutron.db.migration.cli import * # noqa
def main():
config = alembic_config.Config(
os.path.join(os.path.dirname(__file__), 'alembic.ini'))
config.set_main_option('script_location',
('networking_bgpvpn.neutron.db.migration:'
'alembic_migrations'))
config.neutron_config = CONF
CONF()
CONF.command.func(config, CONF.command.name)

View File

@@ -0,0 +1,203 @@
# Copyright (c) 2015 Orange.
# 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.
import abc
import six
from networking_bgpvpn.neutron.extensions import bgpvpn as bgpvpn_ext
from neutron.api import extensions
from neutron.api.v2 import attributes as attr
from neutron.api.v2 import resource_helper
from neutron.plugins.common import constants
from neutron.services.service_base import ServicePluginBase
from oslo_log import log
LOG = log.getLogger(__name__)
BGPVPN_L3 = 'l3'
BGPVPN_L2 = 'l2'
BGPVPN_TYPES = [BGPVPN_L3, BGPVPN_L2]
# Regular expression to validate Route Target list format
# ["<asn1>:<nn1>","<asn2>:<nn2>", ...] with asn and nn in range 0-65535
RT_REGEX = ('^((?:0|[1-9]\d{0,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]'
'\d|6553[0-5]):(?:0|[1-9]\d{0,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d'
'{2}|655[0-2]\d|6553[0-5]))$')
extensions.append_api_extensions_path(bgpvpn_ext.__path__)
constants.BGPVPN = "BGPVPN"
constants.ALLOWED_SERVICES.append(constants.BGPVPN)
constants.COMMON_PREFIXES["BGPVPN"] = "/bgpvpn"
def _validate_rt_list(data, valid_values=None):
if not isinstance(data, list):
msg = _("'%s' is not a list") % data
LOG.debug(msg)
return msg
for item in data:
msg = attr._validate_regex(item, RT_REGEX)
if msg:
LOG.debug(msg)
return msg
if len(set(data)) != len(data):
msg = _("Duplicate items in the list: '%s'") % ', '.join(data)
LOG.debug(msg)
return msg
def _validate_rt_list_or_none(data, valid_values=None):
if not data:
return _validate_rt_list(data, valid_values=valid_values)
validators = {'type:route_target_list': _validate_rt_list,
'type:route_target_list_or_none': _validate_rt_list_or_none}
attr.validators.update(validators)
RESOURCE_ATTRIBUTE_MAP = {
'bgpvpn_connections': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'network_id': {'allow_post': True, 'allow_put': True,
'default': None,
'validate': {'type:uuid_or_none': None},
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'default': '',
'validate': {'type:string': None},
'is_visible': True},
'type': {'allow_post': True, 'allow_put': False,
'default': BGPVPN_L3,
'validate': {'type:values': BGPVPN_TYPES},
'is_visible': True},
'route_targets': {'allow_post': True, 'allow_put': True,
'default': [],
'convert_to': attr.convert_to_list,
'validate': {'type:route_target_list': None},
'is_visible': True},
'import_targets': {'allow_post': True, 'allow_put': True,
'default': None,
'convert_to': attr.convert_to_list,
'validate': {'type:route_target_list_or_none':
None},
'is_visible': True},
'export_targets': {'allow_post': True, 'allow_put': True,
'default': None,
'convert_to': attr.convert_to_list,
'validate': {'type:route_target_list_or_none':
None},
'is_visible': True},
'auto_aggregate': {'allow_post': True, 'allow_put': True,
'default': True,
'validate': {'type:boolean': None},
'convert_to': attr.convert_to_boolean,
'is_visible': True},
},
}
class Bgpvpn(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return "BGPVPN Connection extension"
@classmethod
def get_alias(cls):
return "bgpvpn"
@classmethod
def get_description(cls):
return "Extension for BGPVPN Connection service"
@classmethod
def get_namespace(cls):
return "http://wiki.openstack.org/Neutron/bgpvpn/API_1.0"
@classmethod
def get_updated(cls):
return "2014年06月10日T17:00:00-00:00"
@classmethod
def get_resources(cls):
plural_mappings = resource_helper.build_plural_mappings(
{}, RESOURCE_ATTRIBUTE_MAP)
plural_mappings['route_targets'] = 'route_target'
plural_mappings['import_targets'] = 'import_target'
plural_mappings['export_targets'] = 'export_target'
attr.PLURALS.update(plural_mappings)
return resource_helper.build_resource_info(plural_mappings,
RESOURCE_ATTRIBUTE_MAP,
constants.BGPVPN,
register_quota=True,
translate_name=True)
@classmethod
def get_plugin_interface(cls):
return BGPVPNPluginBase
def update_attributes_map(self, attributes):
super(Bgpvpn, self).update_attributes_map(
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
def get_extended_resources(self, version):
if version == "2.0":
return RESOURCE_ATTRIBUTE_MAP
else:
return {}
@six.add_metaclass(abc.ABCMeta)
class BGPVPNPluginBase(ServicePluginBase):
def get_plugin_name(self):
return constants.BGPVPN
def get_plugin_type(self):
return constants.BGPVPN
def get_plugin_description(self):
return 'BGP VPN service plugin'
@abc.abstractmethod
def create_bgpvpn_connection(self, context, bgpvpn_connection):
pass
@abc.abstractmethod
def get_bgpvpn_connections(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_bgpvpn_connection(self, context, id, fields=None):
pass
@abc.abstractmethod
def update_bgpvpn_connection(self, context, id, bgpvpn_connection):
pass
@abc.abstractmethod
def delete_bgpvpn_connection(self, context, id):
pass

View File

@@ -0,0 +1,89 @@
# Copyright (c) 2015 Orange.
# 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 networking_bgpvpn.neutron.db.bgpvpn import bgpvpn_db
from neutron.i18n import _LI
from neutron.plugins.common import constants
from neutron.services import service_base
from oslo_log import log
LOG = log.getLogger(__name__)
class BGPVPNPlugin(bgpvpn_db.BGPVPNPluginDb):
supported_extension_aliases = ["bgpvpn"]
def __init__(self):
super(BGPVPNPlugin, self).__init__()
# Load the service driver from neutron.conf.
drivers, default_provider = service_base.load_drivers(
constants.BGPVPN, self)
LOG.info(_LI("BGP VPN Service Plugin using Service Driver: %s"),
default_provider)
self.bgpvpn_driver = drivers[default_provider]
def get_plugin_type(self):
return constants.BGPVPN
def get_plugin_description(self):
return "Neutron BGP VPN connection Service Plugin"
def prevent_bgpvpn_network_deletion(self, context, network_id):
LOG.debug('Prevent BGP VPN network deletion')
if (super(BGPVPNPlugin, self).
get_bgpvpn_connections(context,
filters={'network_id': [network_id]})):
raise bgpvpn_db.BGPVPNNetworkInUse(network_id=network_id)
else:
LOG.debug('Network %(network_id)s can be deleted')
def create_bgpvpn_connection(self, context, bgpvpn_connection):
bgpvpn_connection = super(
BGPVPNPlugin, self).create_bgpvpn_connection(context,
bgpvpn_connection)
self.bgpvpn_driver.create_bgpvpn_connection(context,
bgpvpn_connection)
return bgpvpn_connection
def delete_bgpvpn_connection(self, context, bgpvpn_conn_id):
bgpvpn_connection = super(
BGPVPNPlugin, self).delete_bgpvpn_connection(context,
bgpvpn_conn_id)
self.bgpvpn_driver.delete_bgpvpn_connection(context,
bgpvpn_connection)
def update_bgpvpn_connection(self,
context, bgpvpn_conn_id,
bgpvpn_connection):
old_bgpvpn_connection = self.get_bgpvpn_connection(context,
bgpvpn_conn_id)
bgpvpn_connection = super(
BGPVPNPlugin, self).update_bgpvpn_connection(context,
bgpvpn_conn_id,
bgpvpn_connection)
self.bgpvpn_driver.update_bgpvpn_connection(context,
old_bgpvpn_connection,
bgpvpn_connection)
return bgpvpn_connection
def notify_port_updated(self, context, port):
self.bgpvpn_driver.notify_port_updated(context, port)
def remove_port_from_bgpvpn_agent(self, context, port):
self.bgpvpn_driver.remove_port_from_bgpvpn_agent(context, port)

View File

@@ -0,0 +1,49 @@
# Copyright (c) 2015 Orange.
# 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.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class BGPVPNDriver(object):
def __init__(self, service_plugin):
self.service_plugin = service_plugin
@property
def service_type(self):
pass
@abc.abstractmethod
def create_bgpvpn_connection(self, context, bgpvpn_connection):
pass
@abc.abstractmethod
def update_bgpvpn_connection(self, context, old_bgpvpn_connection,
bgpvpn_connection):
pass
@abc.abstractmethod
def delete_bgpvpn_connection(self, context, bgpvpn_connection):
pass
@abc.abstractmethod
def notify_port_updated(self, context, port):
pass
@abc.abstractmethod
def remove_port_from_bgpvpn_agent(self, context, port):
pass

View File

@@ -0,0 +1,43 @@
# Copyright (c) 2015 Orange.
# 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 networking_bgpvpn.neutron.services.bgpvpn import service_drivers
from oslo_log import log
LOG = log.getLogger(__name__)
class dummyBGPVPNDriver(service_drivers.BGPVPNDriver):
"""dummy BGP VPN connection Service Driver class."""
def __init__(self, service_plugin):
super(dummyBGPVPNDriver, self).__init__(service_plugin)
LOG.debug("dummyBGPVPNDriver service_plugin : %s", service_plugin)
def create_bgpvpn_connection(self, context, bgpvpn_connection):
pass
def update_bgpvpn_connection(self, context, old_bgpvpn_connection,
bgpvpn_connection):
pass
def delete_bgpvpn_connection(self, context, bgpvpn_connection):
pass
def notify_port_updated(self, context, port):
pass
def remove_port_from_bgpvpn_agent(self, context, port):
pass

View File

@@ -0,0 +1,110 @@
# Copyright (c) 2015 Orange.
# 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 neutronclient.common import extension
from neutronclient.i18n import _
from neutronclient.neutron import v2_0 as neutronv20
class BGPVPNConnection(extension.NeutronClientExtension):
resource = 'bgpvpn_connection'
path = 'bgpvpn-connections'
resource_plural = '%ss' % resource
object_path = '/bgpvpn/%s' % path
resource_path = '/bgpvpn/%s/%%s' % path
versions = ['2.0']
class BGPVPNConnectionCreate(extension.ClientExtensionCreate,
BGPVPNConnection):
shell_command = 'bgpvpn-connection-create'
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help=_('Name of the BGP VPN connection'))
parser.add_argument(
'--type',
default='l3', choices=['l2', 'l3'],
help=_('BGP VPN connection type selection between L3VPN (l3) and '
'EVPN (l2), default:l3'))
parser.add_argument(
'--route-targets',
help=_('Route Targets list to import/export for this BGP '
'VPN connection. Usage: -- --route-targets '
'list=true <asn1>:<nn1> <asn2>:<nn2> ...'))
parser.add_argument(
'--import-targets',
help=_('List of additional Route Targets to import from.'
' Usage: -- --import-targets list=true '
'<asn1>:<nn1> <asn2>:<nn2> ...'))
parser.add_argument(
'--export-targets',
help=_('List of additional Route Targets to export to. Usage: -- '
'--export-targets list=true <asn1>:<nn1> <asn2>:<nn2> ...'))
parser.add_argument(
'--network-id', metavar='NETWORK',
default=None,
help=_('Id of the network associated with this '
'BGP VPN connection'))
parser.add_argument(
'--no-aggregate',
dest='auto_aggregate', action='store_false',
help=_('Disable auto aggregation (only for '
'L3VPN connection type)'))
def args2body(self, parsed_args):
body = {
self.resource: {},
}
if parsed_args.network_id:
_network_id = neutronv20.find_resourceid_by_name_or_id(
self.get_client(), 'network',
parsed_args.network_id)
body[self.resource]['network_id'] = _network_id
neutronv20.update_dict(parsed_args, body[self.resource],
['name', 'tenant_id', 'type', 'route_targets',
'import_targets', 'export_targets',
'auto_aggregate'])
return body
class BGPVPNConnectionUpdate(extension.ClientExtensionUpdate,
BGPVPNConnection):
shell_command = 'bgpvpn-connection-update'
class BGPVPNConnectionDelete(extension.ClientExtensionDelete,
BGPVPNConnection):
shell_command = 'bgpvpn-connection-delete'
class BGPVPNConnectionList(extension.ClientExtensionList,
BGPVPNConnection):
shell_command = 'bgpvpn-connection-list'
list_columns = [
'id', 'name', 'type', 'route_targets', 'import_targets',
'export_targets', 'network_id', 'auto_aggregate', 'tenant_id']
pagination_support = True
sorting_support = True
class BGPVPNConnectionShow(extension.ClientExtensionShow,
BGPVPNConnection):
shell_command = 'bgpvpn-connection-show'

View File

@@ -24,6 +24,12 @@ classifier =
packages =
networking_bgpvpn
[entry_points]
console_scripts=
bgpvpn-db-manage = networking_bgpvpn.neutron.db.migration.cli:main
neutronclient.extension=
bgpvpn_connection = networking_bgpvpn.neutronclient.neutron.v2_0.bgpvpn.bgpvpn_connection
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
Reference in New Issue
openstack/networking-bgpvpn
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.

The note is not visible to the blocked user.