Adds Astara BYONF API extension

Adds the BYONF portion of the BYONF functionality.
This thin layer simply stores some data in the Neutron DB. A in-tree
migration repo has been setup to migrate the neutron DB to add the
necessary table.

Partially-implements: blueprint astara-sfc

Co-authored by: Mark McClain <mark@mcclain.xyz>

Change-Id: I6b6f98e8ae89c704f45b05f87f17ebed5a70fc1d
This commit is contained in:
Adam Gandelman 2016-03-14 17:08:06 -07:00
parent 8111a7bd61
commit 3e4e9d6795
16 changed files with 362 additions and 5 deletions

View File

View File

View File

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

View File

@ -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()

View File

@ -0,0 +1,36 @@
# Copyright ${create_date.year} <PUT YOUR NAME/COMPANY HERE>
#
# 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"}

View File

@ -0,0 +1 @@
a999bcf20008

View File

@ -0,0 +1,44 @@
# Copyright 2016 <PUT YOUR NAME/COMPANY HERE>
#
# 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'),
)

View File

@ -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

View File

View File

@ -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'),
)

View File

@ -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 []

View File

@ -68,6 +68,7 @@ cfg.CONF.register_opts(astara_opts)
SUPPORTED_EXTENSIONS = [
'dhrouterstatus',
'byonf'
]

View File

@ -35,7 +35,7 @@ class Ml2Plugin(plugin.Ml2Plugin):
_supported_extension_aliases = (
plugin.Ml2Plugin._supported_extension_aliases +
["dhrouterstatus"]
["dhrouterstatus", "byonf"]
)
disabled_extensions = [

View File

@ -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``.

View File

@ -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 =