Skip to content

Commit

Permalink
Add flavor/service provider support to routers
Browse files Browse the repository at this point in the history
This is the initial support for flavors and multiple service
providers with the built-in L3 service plugin.

This patch handles a few key components:

* Adds an optional flavor_id to the router data model
* Adds a new driver controller that performs the following tasks:
  * Loads up the configured drivers and 4 default drivers representing
    the current matrix of ha/dvr options (single node, ha, dvr, and ha+dvr)
  * Associates every router with a driver based on ha/dvr attributes
    or the flavor_id if specified

Note that the current drivers are very limited because they don't do anything.
All of the complex logic for the in-tree drivers is still tied up in the giant
mixin the service plugin inherits. Breaking that apart will be in follow-up
patches.

Partially-Implements: blueprint multi-l3-backends
Change-Id: Idce75bf0fc1375dcbbff9b9803fd2fe97d158cff
  • Loading branch information
kevinbenton committed Jul 26, 2016
1 parent 414f2ff commit 0e3f4b8
Show file tree
Hide file tree
Showing 19 changed files with 613 additions and 6 deletions.
2 changes: 2 additions & 0 deletions neutron/db/l3_db.py
Expand Up @@ -95,6 +95,8 @@ class Router(model_base.HasStandardAttributes, model_base.BASEV2,
admin_state_up = sa.Column(sa.Boolean)
gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
gw_port = orm.relationship(models_v2.Port, lazy='joined')
flavor_id = sa.Column(sa.String(36),
sa.ForeignKey("flavors.id"), nullable=True)
attached_ports = orm.relationship(
RouterPort,
backref='router',
Expand Down
@@ -1 +1 @@
a963b38d82f4
3d0e74aa7d37
@@ -0,0 +1,43 @@
# Copyright 2016 Mirantis
#
# 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.
#

"""Add flavor_id to Router
Revision ID: 3d0e74aa7d37
Revises: a963b38d82f4
Create Date: 2016-05-05 00:22:47.618593
"""

from alembic import op
import sqlalchemy as sa

from neutron.db import migration


# revision identifiers, used by Alembic.
revision = '3d0e74aa7d37'
down_revision = 'a963b38d82f4'

# milestone identifier, used by neutron-db-manage
neutron_milestone = [migration.NEWTON]


def upgrade():
op.add_column('routers',
sa.Column('flavor_id',
sa.String(length=36),
sa.ForeignKey('flavors.id'),
nullable=True))
55 changes: 55 additions & 0 deletions neutron/extensions/l3_flavors.py
@@ -0,0 +1,55 @@
#
# 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_lib import constants

from neutron.api import extensions

EXTENDED_ATTRIBUTES_2_0 = {
'routers': {
'flavor_id': {'allow_post': True, 'allow_put': False,
'default': constants.ATTR_NOT_SPECIFIED,
'is_visible': True, 'enforce_policy': True}

}
}


class L3_flavors(extensions.ExtensionDescriptor):
"""Extension class supporting flavors for routers."""

@classmethod
def get_name(cls):
return "Router Flavor Extension"

@classmethod
def get_alias(cls):
return 'l3-flavors'

@classmethod
def get_description(cls):
return "Flavor support for routers."

@classmethod
def get_updated(cls):
return "2016-05-17T00:00:00-00:00"

def get_extended_resources(self, version):
if version == "2.0":
return EXTENDED_ATTRIBUTES_2_0
else:
return {}

def get_required_extensions(self):
return ["router", "flavors"]
14 changes: 13 additions & 1 deletion neutron/services/l3_router/l3_router_plugin.py
Expand Up @@ -30,9 +30,11 @@
from neutron.db import l3_dvrscheduler_db
from neutron.db import l3_gwmode_db
from neutron.db import l3_hamode_db
from neutron.extensions import l3
from neutron.plugins.common import constants
from neutron.quota import resource_registry
from neutron import service
from neutron.services.l3_router.service_providers import driver_controller
from neutron.services import service_base


Expand All @@ -55,7 +57,8 @@ class L3RouterPlugin(service_base.ServicePluginBase,
"""
supported_extension_aliases = ["dvr", "router", "ext-gw-mode",
"extraroute", "l3_agent_scheduler",
"l3-ha", "router_availability_zone"]
"l3-ha", "router_availability_zone",
"l3-flavors"]

__native_pagination_support = True
__native_sorting_support = True
Expand All @@ -76,6 +79,7 @@ def __init__(self):
rpc_worker = service.RpcWorker([self], worker_process_count=0)

self.add_worker(rpc_worker)
self.l3_driver_controller = driver_controller.DriverController(self)

@log_helpers.log_method_call
def start_rpc_listeners(self):
Expand Down Expand Up @@ -111,3 +115,11 @@ def create_floatingip(self, context, floatingip):
return super(L3RouterPlugin, self).create_floatingip(
context, floatingip,
initial_status=n_const.FLOATINGIP_STATUS_DOWN)


def add_flavor_id(plugin, router_res, router_db):
router_res['flavor_id'] = router_db['flavor_id']


common_db_mixin.CommonDbMixin.register_dict_extend_funcs(
l3.ROUTERS, [add_flavor_id])
Empty file.
55 changes: 55 additions & 0 deletions neutron/services/l3_router/service_providers/base.py
@@ -0,0 +1,55 @@
# 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 neutron._i18n import _


class _FeatureFlag(object):

def is_compatible(self, value):
if value == self.requires:
return True
if value and self.supports:
return True
return False

def __init__(self, supports, requires):
self.supports = supports
self.requires = requires
if requires and not supports:
raise RuntimeError(_("A driver can't require a feature and not "
"support it."))

UNSUPPORTED = _FeatureFlag(supports=False, requires=False)
OPTIONAL = _FeatureFlag(supports=True, requires=False)
MANDATORY = _FeatureFlag(supports=True, requires=True)


class L3ServiceProvider(object):
"""Base class for L3 service providers.
On __init__ this will be given a handle to the l3 plugin. It is then the
responsibility of the driver to subscribe to the events it is interested
in (e.g. router_create, router_update, router_delete, etc).
The 'ha' and 'distributed' attributes below are used to determine if a
router request with the 'ha' or 'distributed' attribute can be supported
by this particular driver. These attributes must be present.
"""

ha_support = UNSUPPORTED
distributed_support = UNSUPPORTED

def __init__(self, l3plugin):
self.l3plugin = l3plugin

0 comments on commit 0e3f4b8

Please sign in to comment.