diff --git a/astara_neutron/db/__init__.py b/astara_neutron/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astara_neutron/db/migration/__init__.py b/astara_neutron/db/migration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astara_neutron/db/migration/alembic_migrations/README b/astara_neutron/db/migration/alembic_migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/astara_neutron/db/migration/alembic_migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/astara_neutron/db/migration/alembic_migrations/__init__.py b/astara_neutron/db/migration/alembic_migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astara_neutron/db/migration/alembic_migrations/env.py b/astara_neutron/db/migration/alembic_migrations/env.py new file mode 100644 index 0000000..ae99ec8 --- /dev/null +++ b/astara_neutron/db/migration/alembic_migrations/env.py @@ -0,0 +1,85 @@ +# 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 +ASTARA_NEUTRON_VERSION_TABLE = 'alembic_version_astara_neutron' +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'] = ASTARA_NEUTRON_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=ASTARA_NEUTRON_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() diff --git a/astara_neutron/db/migration/alembic_migrations/script.py.mako b/astara_neutron/db/migration/alembic_migrations/script.py.mako new file mode 100644 index 0000000..9e0b2ce --- /dev/null +++ b/astara_neutron/db/migration/alembic_migrations/script.py.mako @@ -0,0 +1,36 @@ +# Copyright ${create_date.year} +# +# 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. +# + +"""${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)} +% if branch_labels: +branch_labels = ${repr(branch_labels)} +%endif + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +def upgrade(): + ${upgrades if upgrades else "pass"} diff --git a/astara_neutron/db/migration/alembic_migrations/versions/HEAD b/astara_neutron/db/migration/alembic_migrations/versions/HEAD new file mode 100644 index 0000000..52e620c --- /dev/null +++ b/astara_neutron/db/migration/alembic_migrations/versions/HEAD @@ -0,0 +1 @@ +a999bcf20008 \ No newline at end of file diff --git a/astara_neutron/db/migration/alembic_migrations/versions/a999bcf20008_initial_migration.py b/astara_neutron/db/migration/alembic_migrations/versions/a999bcf20008_initial_migration.py new file mode 100644 index 0000000..83b8de9 --- /dev/null +++ b/astara_neutron/db/migration/alembic_migrations/versions/a999bcf20008_initial_migration.py @@ -0,0 +1,44 @@ +# Copyright 2016 +# +# 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 + + +"""empty message + +Revision ID: a999bcf20008 +Revises: start_astara_neutron +Create Date: 2016-03-14 14:09:43.025886 + +""" + +# revision identifiers, used by Alembic. +revision = 'a999bcf20008' +down_revision = 'start_astara_neutron' + + +def upgrade(): + op.create_table( + 'astara_byonf', + sa.Column('tenant_id', sa.String(length=255), nullable=False), + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('function_type', sa.String(length=255), nullable=False), + sa.Column('driver', sa.String(length=36), nullable=False), + sa.Column('image_uuid', sa.String(length=36), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('tenant_id', 'function_type', + name='uix_tenant_id_function'), + ) diff --git a/astara_neutron/db/migration/alembic_migrations/versions/astara_astara_neutron.py b/astara_neutron/db/migration/alembic_migrations/versions/astara_astara_neutron.py new file mode 100644 index 0000000..e49794f --- /dev/null +++ b/astara_neutron/db/migration/alembic_migrations/versions/astara_astara_neutron.py @@ -0,0 +1,30 @@ +# 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. +# + +"""start astara-neutron chain + +Revision ID: start_astara_neutron +Revises: None +Create Date: 2015-03-14 11:06:18.196062 + +""" + +# revision identifiers, used by Alembic. +revision = 'start_astara_neutron' +down_revision = None + + +def upgrade(): + pass diff --git a/astara_neutron/db/models/__init__.py b/astara_neutron/db/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astara_neutron/db/models/models.py b/astara_neutron/db/models/models.py new file mode 100644 index 0000000..96026fe --- /dev/null +++ b/astara_neutron/db/models/models.py @@ -0,0 +1,28 @@ +# Copyright (c) 2016 Akanda, 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. + +import sqlalchemy as sa + +from neutron.db import model_base, models_v2 + + +class Byonf(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): + __tablename__ = 'astara_byonf' + function_type = sa.Column(sa.String(length=255), nullable=False) + driver = sa.Column(sa.String(length=36), nullable=False) + image_uuid = sa.Column(sa.String(length=36), nullable=False) + __table_args__ = ( + sa.UniqueConstraint( + 'tenant_id', 'function_type', name='uix_tenant_id_function'), + ) diff --git a/astara_neutron/extensions/byonf.py b/astara_neutron/extensions/byonf.py new file mode 100644 index 0000000..28cd30d --- /dev/null +++ b/astara_neutron/extensions/byonf.py @@ -0,0 +1,118 @@ +# Copyright 2014 DreamHost, LLC +# Author: DreamHost, LLC +# +# 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.api import extensions +from neutron.api.v2 import attributes as attr + +from astara_neutron.extensions import _authzbase +from astara_neutron.db.models import models + +import oslo_db.exception as db_exc +import webob.exc + + +class ByonfResource(_authzbase.ResourceDelegate): + """This resource is intended as a private API that allows the rug to chan + the supporting network function. + """ + model = models.Byonf + resource_name = 'byonf' + collection_name = 'byonfs' + + ATTRIBUTE_MAP = { + 'tenant_id': { + 'allow_post': True, + 'allow_put': True, + 'is_visible': True, + 'validate': {'type:string': attr.TENANT_ID_MAX_LEN}, + }, 'id': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True + }, + 'image_uuid': { + 'allow_post': True, + 'allow_put': True, + 'is_visible': True, + 'enforce_policy': True, + 'required_by_policy': True, + 'validate': {'type:uuid': None} + }, + 'function_type': { + 'allow_post': True, + 'allow_put': True, + 'is_visible': True, + 'enforce_policy': True, + 'required_by_policy': True + }, + 'driver': { + 'allow_post': True, + 'allow_put': True, + 'is_visible': True, + 'enforce_policy': True, + 'required_by_policy': True + } + } + + def create(self, context, tenant_id, resource_dict): + try: + return super(ByonfResource, self).create( + context, tenant_id, resource_dict) + except db_exc.DBDuplicateEntry: + raise webob.exc.HTTPConflict( + 'Tenant %s already has driver associatation for function: %s' % + (resource_dict['tenant_id'], resource_dict['function_type'])) + + def make_dict(self, byo): + """ + Convert a Byo model object to a dictionary. + """ + return { + 'tenant_id': byo['tenant_id'], + 'image_uuid': byo['image_uuid'], + 'function_type': byo['function_type'], + 'driver': byo['driver'], + 'id': byo['id'] + } + + +class Byonf(extensions.ExtensionDescriptor): + """ + """ + def get_name(self): + return "byonf" + + def get_alias(self): + return "byonf" + + def get_description(self): + return "A byonf extension" + + def get_namespace(self): + return 'http://docs.openstack.org/api/ext/v1.0' + + def get_updated(self): + return "2015-12-07T09:14:43-05:00" + + def get_resources(self): + return [extensions.ResourceExtension( + 'byonf', + _authzbase.create_extension(ByonfResource()))] + + def get_actions(self): + return [] + + def get_request_extensions(self): + return [] diff --git a/astara_neutron/plugins/decorators.py b/astara_neutron/plugins/decorators.py index 2617d57..1a1f3f7 100644 --- a/astara_neutron/plugins/decorators.py +++ b/astara_neutron/plugins/decorators.py @@ -68,6 +68,7 @@ cfg.CONF.register_opts(astara_opts) SUPPORTED_EXTENSIONS = [ 'dhrouterstatus', + 'byonf' ] diff --git a/astara_neutron/plugins/ml2_neutron_plugin.py b/astara_neutron/plugins/ml2_neutron_plugin.py index 134cd49..3629acc 100644 --- a/astara_neutron/plugins/ml2_neutron_plugin.py +++ b/astara_neutron/plugins/ml2_neutron_plugin.py @@ -35,7 +35,7 @@ class Ml2Plugin(plugin.Ml2Plugin): _supported_extension_aliases = ( plugin.Ml2Plugin._supported_extension_aliases + - ["dhrouterstatus"] + ["dhrouterstatus", "byonf"] ) disabled_extensions = [ diff --git a/releasenotes/notes/byonf-707f55a6aad7452a.yaml b/releasenotes/notes/byonf-707f55a6aad7452a.yaml new file mode 100644 index 0000000..5d156c7 --- /dev/null +++ b/releasenotes/notes/byonf-707f55a6aad7452a.yaml @@ -0,0 +1,12 @@ +--- +features: + - Adds a new BYONF API extension to Neutron which allows operators to override + the astara-orchestrator driver used to back a resource as well as the Glance + image id to be used for the virtual appliance, on a per tenant basis. This + requires leveraging the Neutron database to store these associations, so a + migration repository has been added to create the required tables there. + +other: + - In order to use the BYONF API, the Neutron database must be migrated to create + the required astara_byonf table. This can be accomplished by running + ``neutron-db-manage --subproject astara-neutron upgrade head``. diff --git a/setup.cfg b/setup.cfg index d024a99..72d1a41 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,11 +20,12 @@ classifier = [files] packages = - akanda - akanda.neutron astara_neutron -namespace_packages = - akanda + +[entry_points] +neutron.db.alembic_migrations = + astara-neutron = astara_neutron.db.migration:alembic_migrations + [global] setup-hooks =