Use stevedore to load apps

We used to load apps by listing module and class names in a
configuration entry. This patch defines entry points for all present
apps, so we don't have to import them by module path, additionally,
other packages can register new apps, allowing further extensibility.

Closes-Bug: #1702462
Change-Id: Iffa0fea3db36df2f59ef19a621b41838d66cb8d7
This commit is contained in:
Dima Kuznetsov 2017-07-05 21:12:04 +03:00
parent cd10f47219
commit 7367754398
27 changed files with 62 additions and 50 deletions

View File

@ -138,7 +138,7 @@ function configure_ovs {
if is_service_enabled df-controller ; then
# setup external bridge if necessary
check_dnat=$(echo $DF_APPS_LIST | grep "DNATApp")
check_dnat=$(echo $DF_APPS_LIST | grep "dnat")
if [[ "$check_dnat" != "" ]]; then
echo "Setup external bridge for DNAT"
sudo ovs-vsctl add-br $PUBLIC_BRIDGE || true

View File

@ -158,7 +158,7 @@ function start_ovs {
function configure_ovs {
if is_service_enabled df-controller ; then
# setup external bridge if necessary
check_dnat=$(echo $DF_APPS_LIST | grep "DNATApp")
check_dnat=$(echo $DF_APPS_LIST | grep "dnat")
if [[ "$check_dnat" != "" ]]; then
echo "Setup external bridge for DNAT"
sudo ovs-vsctl add-br $PUBLIC_BRIDGE || true

View File

@ -17,36 +17,33 @@ OVS_BRANCH=${OVS_BRANCH:-branch-2.6}
EXTERNAL_HOST_IP=${EXTERNAL_HOST_IP:-}
DEFAULT_TUNNEL_TYPES="vxlan,geneve,gre"
DEFAULT_APPS_LIST="l2_app.L2App,l3_proactive_app.L3ProactiveApp,"\
"dhcp_app.DHCPApp,dnat_app.DNATApp,sg_app.SGApp,portsec_app.PortSecApp,"\
"portqos_app.PortQosApp,classifier_app.ClassifierApp,tunneling_app.TunnelingApp,"\
"provider_networks_app.ProviderNetworksApp"
DEFAULT_APPS_LIST="l2,l3_proactive,dhcp,dnat,sg,portsec,portqos,classifier,tunneling,provider"
if is_service_enabled df-metadata ; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,metadata_service_app.MetadataServiceApp"
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,metadata_service"
fi
if is_service_enabled q-trunk ; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,trunk_app.TrunkApp"
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,trunk"
fi
ENABLE_ACTIVE_DETECTION=${ENABLE_ACTIVE_DETECTION:-True}
if [[ "$ENABLE_ACTIVE_DETECTION" == "True" ]]; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,active_port_detection_app.ActivePortDetectionApp"
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,active_port_detection"
fi
ENABLE_LIVE_MIGRATION=${ENABLE_LIVE_MIGRATION:-True}
if [[ "$ENABLE_LIVE_MIGRATION" == "True" ]]; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,migration_app.MigrationApp"
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,migration"
fi
if [[ ! -z ${EXTERNAL_HOST_IP} ]]; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,chassis_snat_app.ChassisSNATApp"
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,chassis_snat"
fi
ENABLED_AGING_APP=${ENABLE_AGING_APP:-True}
if [[ "$ENABLE_AGING_APP" == "True" ]]; then
DEFAULT_APPS_LIST="aging_app.Aging,$DEFAULT_APPS_LIST"
DEFAULT_APPS_LIST="aging,$DEFAULT_APPS_LIST"
fi
DF_APPS_LIST=${DF_APPS_LIST:-$DEFAULT_APPS_LIST}

View File

@ -95,7 +95,7 @@ Basic Configurations
[df]
metadata_interface = tap-metadata
enable_selective_topology_distribution = True
apps_list = l2_app.L2App,l3_proactive_app.L3ProactiveApp,dhcp_app.DHCPApp,dnat_app.DNATApp,sg_app.SGApp,portsec_app.PortSecApp,portqos_app.PortQosApp
apps_list = l2,l3_proactive,dhcp,dnat,sg,portsec,portqos
integration_bridge = br-int
tunnel_type = geneve

View File

@ -41,9 +41,7 @@ df_opts = [
default=False,
help=_("Enable dpdk")),
cfg.StrOpt('apps_list',
default='l2_app.L2App,'
'l3_proactive_app.L3ProactiveApp,'
'dhcp_app.DHCPApp',
default='l2,l3_proactive,dhcp',
help=_('List of openflow applications classes to load')),
cfg.StrOpt('integration_bridge', default='br-int',
help=_("Integration bridge to use. "

View File

@ -11,9 +11,8 @@
# under the License.
from oslo_log import log
from oslo_utils import importutils
import stevedore
from dragonflow._i18n import _
from dragonflow.common import exceptions
LOG = log.getLogger(__name__)
@ -21,21 +20,21 @@ LOG = log.getLogger(__name__)
class AppDispatcher(object):
def __init__(self, apps_location_prefix, app_list):
self.apps_location_prefix = apps_location_prefix
def __init__(self, app_list):
self.apps_list = app_list.split(',')
self.apps = []
def load(self, *args, **kwargs):
for app in self.apps_list:
app_class_name = self.apps_location_prefix + "." + app
try:
app_class = importutils.import_class(app_class_name)
app = app_class(*args, **kwargs)
self.apps.append(app)
except ImportError as e:
LOG.exception("Error loading application by class, %s", e)
raise ImportError(_("Application class not found."))
mgr = stevedore.NamedExtensionManager(
'dragonflow.controller.apps',
self.apps_list,
invoke_on_load=True,
invoke_args=args,
invoke_kwds=kwargs,
)
for ext in mgr:
self.apps.append(ext.obj)
def dispatch(self, method, *args, **kwargs):
errors = []

View File

@ -39,8 +39,7 @@ class RyuDFAdapter(ofp_handler.OFPHandler):
def __init__(self, vswitch_api=None, nb_api=None,
neutron_server_notifier=None):
super(RyuDFAdapter, self).__init__()
self.dispatcher = dispatcher.AppDispatcher('dragonflow.controller',
cfg.CONF.df.apps_list)
self.dispatcher = dispatcher.AppDispatcher(cfg.CONF.df.apps_list)
self.vswitch_api = vswitch_api
self.nb_api = nb_api
self.neutron_server_notifier = neutron_server_notifier

View File

@ -91,7 +91,7 @@ class NbApi(object):
# FIXME(nick-ma-z): if active-detection is enabled,
# we initialize the publisher here. Make sure it
# only supports redis-based pub/sub driver.
if "ActivePortDetectionApp" in cfg.CONF.df.apps_list:
if "active_port_detection" in cfg.CONF.df.apps_list:
self.publisher.initialize()
# NOTE(gampel) we want to start queuing event as soon

View File

@ -16,7 +16,7 @@ from dragonflow.tests.common import utils
from dragonflow.tests.fullstack import test_base
from dragonflow.tests.fullstack import test_objects as objects
SNAT_APP_NAME = 'chassis_snat_app.ChassisSNATApp'
SNAT_APP_NAME = 'chassis_snat'
class TestSnatFlows(test_base.DFTestBase):

View File

@ -21,7 +21,7 @@ from dragonflow.tests.unit import test_app_base
class TestChassisSNATApp(test_app_base.DFAppTestBase):
apps_list = "chassis_snat_app.ChassisSNATApp"
apps_list = "chassis_snat"
external_host_ip = '172.24.4.100'
def setUp(self):

View File

@ -22,7 +22,7 @@ make_fake_local_port = test_app_base.make_fake_local_port
class TestClassifierApp(test_app_base.DFAppTestBase):
apps_list = "classifier_app.ClassifierApp"
apps_list = "classifier"
def setUp(self):
super(TestClassifierApp, self).setUp()

View File

@ -36,7 +36,7 @@ class _ModelNoEvents(model_framework.ModelBase, mixins.Version):
class DfLocalControllerTestCase(test_app_base.DFAppTestBase):
apps_list = "l2_app.L2App"
apps_list = "l2"
@mock.patch.object(ryu_base_app.RyuDFAdapter, 'notify_ovs_sync_finished')
def test_ovs_sync_finished(self, mock_notify):

View File

@ -32,7 +32,7 @@ class Option(object):
class TestDHCPApp(test_app_base.DFAppTestBase):
apps_list = "dhcp_app.DHCPApp"
apps_list = "dhcp"
def setUp(self):
super(TestDHCPApp, self).setUp()

View File

@ -30,7 +30,7 @@ class TestAppDispatcher(tests_base.BaseTestCase):
def setUp(self):
super(TestAppDispatcher, self).setUp()
self.dispatcher = dispatcher.AppDispatcher("", "")
self.dispatcher = dispatcher.AppDispatcher("")
def test_dispatch_with_exception(self):
fake_app = mock.MagicMock()

View File

@ -46,7 +46,7 @@ remote_lport.is_local = False
class TestDNATApp(test_app_base.DFAppTestBase):
apps_list = "dnat_app.DNATApp"
apps_list = "dnat"
def setUp(self):
super(TestDNATApp, self).setUp(enable_selective_topo_dist=True)

View File

@ -21,7 +21,7 @@ from dragonflow.tests.unit import test_app_base
class TestL2App(test_app_base.DFAppTestBase):
apps_list = "l2_app.L2App"
apps_list = "l2"
def setUp(self):
super(TestL2App, self).setUp()

View File

@ -23,7 +23,7 @@ from dragonflow.tests.unit import test_app_base
class TestL3App(test_app_base.DFAppTestBase,
_test_l3.L3AppTestCaseMixin):
apps_list = "l3_app.L3App"
apps_list = "l3_reactive"
def setUp(self):
super(TestL3App, self).setUp()

View File

@ -24,7 +24,7 @@ from dragonflow.tests.unit import test_app_base
class TestL3ProactiveApp(test_app_base.DFAppTestBase,
_test_l3.L3AppTestCaseMixin):
apps_list = "l3_proactive_app.L3ProactiveApp"
apps_list = "l3_proactive"
def setUp(self):
super(TestL3ProactiveApp, self).setUp()

View File

@ -20,7 +20,7 @@ from dragonflow.tests.unit import test_app_base
class TestLegacySNatApp(test_app_base.DFAppTestBase):
apps_list = "legacy_snat_app.LegacySNatApp"
apps_list = "legacy_snat"
def setUp(self):
super(TestLegacySNatApp, self).setUp()

View File

@ -25,7 +25,7 @@ from dragonflow.tests.unit import test_app_base
class TestMetadataServiceApp(test_app_base.DFAppTestBase):
apps_list = "metadata_service_app.MetadataServiceApp"
apps_list = "metadata_service"
def setUp(self):
super(TestMetadataServiceApp, self).setUp()

View File

@ -18,7 +18,7 @@ from dragonflow.tests.unit import test_app_base
class TestMigrationApp(test_app_base.DFAppTestBase):
apps_list = "migration_app.MigrationApp"
apps_list = "migration"
def test_update_migration_flows(self):
cfg.CONF.set_override('host', 'fake-local-host')

View File

@ -23,7 +23,7 @@ make_fake_remote_port = test_app_base.make_fake_remote_port
class TestProviderNetsApp(test_app_base.DFAppTestBase):
apps_list = "provider_networks_app.ProviderNetworksApp"
apps_list = "provider"
def setUp(self):
super(TestProviderNetsApp, self).setUp()

View File

@ -27,7 +27,7 @@ COMMAND_DELETE = 2
class TestSGApp(test_app_base.DFAppTestBase):
apps_list = "sg_app.SGApp"
apps_list = "sg"
def setUp(self):
super(TestSGApp, self).setUp()

View File

@ -50,7 +50,7 @@ def nb_api_get_all_func(*instances):
class TestTopology(test_app_base.DFAppTestBase):
# This is to comply the current code, as the app_list can't be empty.
# But we don't need any app in this test, acutally.
apps_list = "l2_app.L2App"
apps_list = "l2"
def setUp(self):
cfg.CONF.set_override('enable_selective_topology_distribution',

View File

@ -41,7 +41,7 @@ class SettingMock(object):
class TestTrunkApp(test_app_base.DFAppTestBase):
apps_list = "trunk_app.TrunkApp"
apps_list = "trunk"
def setUp(self):
super(TestTrunkApp, self).setUp()

View File

@ -23,7 +23,7 @@ make_fake_remote_port = test_app_base.make_fake_remote_port
class TestTunnelingApp(test_app_base.DFAppTestBase):
apps_list = "tunneling_app.TunnelingApp"
apps_list = "tunneling"
def setUp(self):
super(TestTunnelingApp, self).setUp()

View File

@ -75,3 +75,22 @@ neutron.service_plugins =
df-bgp = dragonflow.neutron.services.bgp.bgp_plugin:DFBgpPlugin
oslo.config.opts =
dragonflow.conf = dragonflow.conf.opts:list_opts
dragonflow.controller.apps =
active_port_detection = dragonflow.controller.active_port_detection_app:ActivePortDetectionApp
aging = dragonflow.controller.aging_app:Aging
chassis_snat = dragonflow.controller.chassis_snat_app:ChassisSNATApp
classifier = dragonflow.controller.classifier_app:ClassifierApp
dhcp = dragonflow.controller.dhcp_app:DHCPApp
dnat = dragonflow.controller.dnat_app:DNATApp
l2 = dragonflow.controller.l2_app:L2App
legacy_snat = dragonflow.controller.legacy_snat_app:LegacySNatApp
l3_proactive = dragonflow.controller.l3_proactive_app:L3ProactiveApp
l3_reactive = dragonflow.controller.l3_app:L3App
metadata_service = dragonflow.controller.metadata_service_app:MetadataServiceApp
migration = dragonflow.controller.migration_app:MigrationApp
portqos = dragonflow.controller.portqos_app:PortQosApp
portsec = dragonflow.controller.portsec_app:PortSecApp
provider = dragonflow.controller.provider_networks_app:ProviderNetworksApp
sg = dragonflow.controller.sg_app:SGApp
trunk = dragonflow.controller.trunk_app:TrunkApp
tunneling = dragonflow.controller.tunneling_app:TunnelingApp