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 if is_service_enabled df-controller ; then
# setup external bridge if necessary # 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 if [[ "$check_dnat" != "" ]]; then
echo "Setup external bridge for DNAT" echo "Setup external bridge for DNAT"
sudo ovs-vsctl add-br $PUBLIC_BRIDGE || true sudo ovs-vsctl add-br $PUBLIC_BRIDGE || true

View File

@ -158,7 +158,7 @@ function start_ovs {
function configure_ovs { function configure_ovs {
if is_service_enabled df-controller ; then if is_service_enabled df-controller ; then
# setup external bridge if necessary # 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 if [[ "$check_dnat" != "" ]]; then
echo "Setup external bridge for DNAT" echo "Setup external bridge for DNAT"
sudo ovs-vsctl add-br $PUBLIC_BRIDGE || true 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:-} EXTERNAL_HOST_IP=${EXTERNAL_HOST_IP:-}
DEFAULT_TUNNEL_TYPES="vxlan,geneve,gre" DEFAULT_TUNNEL_TYPES="vxlan,geneve,gre"
DEFAULT_APPS_LIST="l2_app.L2App,l3_proactive_app.L3ProactiveApp,"\ DEFAULT_APPS_LIST="l2,l3_proactive,dhcp,dnat,sg,portsec,portqos,classifier,tunneling,provider"
"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"
if is_service_enabled df-metadata ; then 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 fi
if is_service_enabled q-trunk ; then if is_service_enabled q-trunk ; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,trunk_app.TrunkApp" DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,trunk"
fi fi
ENABLE_ACTIVE_DETECTION=${ENABLE_ACTIVE_DETECTION:-True} ENABLE_ACTIVE_DETECTION=${ENABLE_ACTIVE_DETECTION:-True}
if [[ "$ENABLE_ACTIVE_DETECTION" == "True" ]]; then 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 fi
ENABLE_LIVE_MIGRATION=${ENABLE_LIVE_MIGRATION:-True} ENABLE_LIVE_MIGRATION=${ENABLE_LIVE_MIGRATION:-True}
if [[ "$ENABLE_LIVE_MIGRATION" == "True" ]]; then if [[ "$ENABLE_LIVE_MIGRATION" == "True" ]]; then
DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,migration_app.MigrationApp" DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,migration"
fi fi
if [[ ! -z ${EXTERNAL_HOST_IP} ]]; then 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 fi
ENABLED_AGING_APP=${ENABLE_AGING_APP:-True} ENABLED_AGING_APP=${ENABLE_AGING_APP:-True}
if [[ "$ENABLE_AGING_APP" == "True" ]]; then if [[ "$ENABLE_AGING_APP" == "True" ]]; then
DEFAULT_APPS_LIST="aging_app.Aging,$DEFAULT_APPS_LIST" DEFAULT_APPS_LIST="aging,$DEFAULT_APPS_LIST"
fi fi
DF_APPS_LIST=${DF_APPS_LIST:-$DEFAULT_APPS_LIST} DF_APPS_LIST=${DF_APPS_LIST:-$DEFAULT_APPS_LIST}

View File

@ -95,7 +95,7 @@ Basic Configurations
[df] [df]
metadata_interface = tap-metadata metadata_interface = tap-metadata
enable_selective_topology_distribution = True 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 integration_bridge = br-int
tunnel_type = geneve tunnel_type = geneve

View File

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

View File

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

View File

@ -39,8 +39,7 @@ class RyuDFAdapter(ofp_handler.OFPHandler):
def __init__(self, vswitch_api=None, nb_api=None, def __init__(self, vswitch_api=None, nb_api=None,
neutron_server_notifier=None): neutron_server_notifier=None):
super(RyuDFAdapter, self).__init__() super(RyuDFAdapter, self).__init__()
self.dispatcher = dispatcher.AppDispatcher('dragonflow.controller', self.dispatcher = dispatcher.AppDispatcher(cfg.CONF.df.apps_list)
cfg.CONF.df.apps_list)
self.vswitch_api = vswitch_api self.vswitch_api = vswitch_api
self.nb_api = nb_api self.nb_api = nb_api
self.neutron_server_notifier = neutron_server_notifier 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, # FIXME(nick-ma-z): if active-detection is enabled,
# we initialize the publisher here. Make sure it # we initialize the publisher here. Make sure it
# only supports redis-based pub/sub driver. # 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() self.publisher.initialize()
# NOTE(gampel) we want to start queuing event as soon # 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_base
from dragonflow.tests.fullstack import test_objects as objects 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): 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): class TestChassisSNATApp(test_app_base.DFAppTestBase):
apps_list = "chassis_snat_app.ChassisSNATApp" apps_list = "chassis_snat"
external_host_ip = '172.24.4.100' external_host_ip = '172.24.4.100'
def setUp(self): 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): class TestClassifierApp(test_app_base.DFAppTestBase):
apps_list = "classifier_app.ClassifierApp" apps_list = "classifier"
def setUp(self): def setUp(self):
super(TestClassifierApp, self).setUp() super(TestClassifierApp, self).setUp()

View File

@ -36,7 +36,7 @@ class _ModelNoEvents(model_framework.ModelBase, mixins.Version):
class DfLocalControllerTestCase(test_app_base.DFAppTestBase): 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') @mock.patch.object(ryu_base_app.RyuDFAdapter, 'notify_ovs_sync_finished')
def test_ovs_sync_finished(self, mock_notify): def test_ovs_sync_finished(self, mock_notify):

View File

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

View File

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

View File

@ -46,7 +46,7 @@ remote_lport.is_local = False
class TestDNATApp(test_app_base.DFAppTestBase): class TestDNATApp(test_app_base.DFAppTestBase):
apps_list = "dnat_app.DNATApp" apps_list = "dnat"
def setUp(self): def setUp(self):
super(TestDNATApp, self).setUp(enable_selective_topo_dist=True) 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): class TestL2App(test_app_base.DFAppTestBase):
apps_list = "l2_app.L2App" apps_list = "l2"
def setUp(self): def setUp(self):
super(TestL2App, self).setUp() super(TestL2App, self).setUp()

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ from dragonflow.tests.unit import test_app_base
class TestMigrationApp(test_app_base.DFAppTestBase): class TestMigrationApp(test_app_base.DFAppTestBase):
apps_list = "migration_app.MigrationApp" apps_list = "migration"
def test_update_migration_flows(self): def test_update_migration_flows(self):
cfg.CONF.set_override('host', 'fake-local-host') 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): class TestProviderNetsApp(test_app_base.DFAppTestBase):
apps_list = "provider_networks_app.ProviderNetworksApp" apps_list = "provider"
def setUp(self): def setUp(self):
super(TestProviderNetsApp, self).setUp() super(TestProviderNetsApp, self).setUp()

View File

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

View File

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

View File

@ -41,7 +41,7 @@ class SettingMock(object):
class TestTrunkApp(test_app_base.DFAppTestBase): class TestTrunkApp(test_app_base.DFAppTestBase):
apps_list = "trunk_app.TrunkApp" apps_list = "trunk"
def setUp(self): def setUp(self):
super(TestTrunkApp, self).setUp() 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): class TestTunnelingApp(test_app_base.DFAppTestBase):
apps_list = "tunneling_app.TunnelingApp" apps_list = "tunneling"
def setUp(self): def setUp(self):
super(TestTunnelingApp, self).setUp() super(TestTunnelingApp, self).setUp()

View File

@ -75,3 +75,22 @@ neutron.service_plugins =
df-bgp = dragonflow.neutron.services.bgp.bgp_plugin:DFBgpPlugin df-bgp = dragonflow.neutron.services.bgp.bgp_plugin:DFBgpPlugin
oslo.config.opts = oslo.config.opts =
dragonflow.conf = dragonflow.conf.opts:list_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