Move controller ovs functionality to a driver
Move all the ovs-specific code from the df_local_controller to a driver that is loaded dynamically and wire it properly. Partial-Bug: #1781376 Change-Id: I2e59042286154bd4f1c58e92c1513ddda97ebedb
This commit is contained in:
parent
41595e7453
commit
cbb9993b3d
|
@ -21,16 +21,12 @@ import time
|
|||
from eventlet import queue
|
||||
from oslo_log import log
|
||||
from oslo_service import loopingcall
|
||||
from ryu.app.ofctl import service as of_service
|
||||
from ryu.base import app_manager
|
||||
from ryu import cfg as ryu_cfg
|
||||
|
||||
from dragonflow.common import exceptions
|
||||
from dragonflow.common import utils as df_utils
|
||||
from dragonflow import conf as cfg
|
||||
from dragonflow.controller.common import constants as ctrl_const
|
||||
from dragonflow.controller import df_config
|
||||
from dragonflow.controller import ryu_base_app
|
||||
from dragonflow.controller import service
|
||||
from dragonflow.controller import topology
|
||||
from dragonflow.db import api_nb
|
||||
|
@ -41,9 +37,7 @@ from dragonflow.db import model_proxy
|
|||
from dragonflow.db.models import core
|
||||
from dragonflow.db.models import l2
|
||||
from dragonflow.db.models import mixins
|
||||
from dragonflow.db.models import ovs
|
||||
from dragonflow.db import sync
|
||||
from dragonflow.ovsdb import vswitch_impl
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -66,24 +60,18 @@ class DfLocalController(object):
|
|||
self.ip = cfg.CONF.df.local_ip
|
||||
# Virtual tunnel port support multiple tunnel types together
|
||||
self.tunnel_types = cfg.CONF.df.tunnel_types
|
||||
self.sync_finished = False
|
||||
self.vswitch_api = vswitch_impl.OvsApi(cfg.CONF.df.management_ip)
|
||||
self.neutron_notifier = None
|
||||
if cfg.CONF.df.enable_neutron_notifier:
|
||||
self.neutron_notifier = df_utils.load_driver(
|
||||
cfg.CONF.df.neutron_notifier,
|
||||
df_utils.DF_NEUTRON_NOTIFIER_DRIVER_NAMESPACE)
|
||||
self.switch_backend = df_utils.load_driver(
|
||||
cfg.CONF.df.switch_backend,
|
||||
df_utils.DF_SWITCH_BACKEND_DRIVER_NAMESPACE,
|
||||
nb_api, cfg.CONF.df.management_ip)
|
||||
|
||||
app_mgr = app_manager.AppManager.get_instance()
|
||||
self.open_flow_app = app_mgr.instantiate(
|
||||
ryu_base_app.RyuDFAdapter,
|
||||
nb_api=self.nb_api,
|
||||
vswitch_api=self.vswitch_api,
|
||||
neutron_server_notifier=self.neutron_notifier,
|
||||
db_change_callback=self.db_change_callback
|
||||
)
|
||||
# The OfctlService is needed to support the 'get_flows' method
|
||||
self.open_flow_service = app_mgr.instantiate(of_service.OfctlService)
|
||||
self.switch_backend.initialize(self.db_change_callback,
|
||||
self.neutron_notifier)
|
||||
self.topology = None
|
||||
self.enable_selective_topo_dist = \
|
||||
cfg.CONF.df.enable_selective_topology_distribution
|
||||
|
@ -113,11 +101,9 @@ class DfLocalController(object):
|
|||
self._queue.task_done()
|
||||
|
||||
def run(self):
|
||||
self.vswitch_api.initialize(self.db_change_callback)
|
||||
self.nb_api.register_notification_callback(self._handle_update)
|
||||
if cfg.CONF.df.enable_neutron_notifier:
|
||||
self.neutron_notifier.initialize(nb_api=self.nb_api,
|
||||
is_neutron_server=False)
|
||||
if self.neutron_notifier:
|
||||
self.neutron_notifier.initialize(nb_api=self.nb_api)
|
||||
self.topology = topology.Topology(self,
|
||||
self.enable_selective_topo_dist)
|
||||
self._sync_pulse.start(
|
||||
|
@ -125,23 +111,7 @@ class DfLocalController(object):
|
|||
initial_delay=cfg.CONF.df.db_sync_time,
|
||||
)
|
||||
|
||||
# both set_controller and del_controller will delete flows.
|
||||
# for reliability, here we should check if controller is set for OVS,
|
||||
# if yes, don't set controller and don't delete controller.
|
||||
# if no, set controller
|
||||
targets = ('tcp:' + cfg.CONF.df_ryu.of_listen_address + ':' +
|
||||
str(cfg.CONF.df_ryu.of_listen_port))
|
||||
is_controller_set = self.vswitch_api.check_controller(targets)
|
||||
integration_bridge = cfg.CONF.df.integration_bridge
|
||||
if not is_controller_set:
|
||||
self.vswitch_api.set_controller(integration_bridge, [targets])
|
||||
is_fail_mode_set = self.vswitch_api.check_controller_fail_mode(
|
||||
'secure')
|
||||
if not is_fail_mode_set:
|
||||
self.vswitch_api.set_controller_fail_mode(
|
||||
integration_bridge, 'secure')
|
||||
self.open_flow_service.start()
|
||||
self.open_flow_app.start()
|
||||
self.switch_backend.start()
|
||||
self._register_models()
|
||||
self.register_chassis()
|
||||
self.sync()
|
||||
|
@ -152,13 +122,13 @@ class DfLocalController(object):
|
|||
ctrl_const.CONTROLLER_SYNC, None)
|
||||
|
||||
def _register_models(self):
|
||||
ignore_models = self.switch_backend.sync_ignore_models()
|
||||
for model in model_framework.iter_models_by_dependency_order():
|
||||
# FIXME (dimak) generalize sync to support non-northbound models
|
||||
# Adding OvsPort will cause sync to delete all OVS ports
|
||||
# periodically
|
||||
if model == ovs.OvsPort:
|
||||
continue
|
||||
self._sync.add_model(model)
|
||||
if model not in ignore_models:
|
||||
self._sync.add_model(model)
|
||||
|
||||
def sync(self):
|
||||
self.topology.check_topology_info()
|
||||
|
@ -237,10 +207,10 @@ class DfLocalController(object):
|
|||
self.db_store.delete(publisher)
|
||||
|
||||
def switch_sync_finished(self):
|
||||
self.open_flow_app.notify_switch_sync_finished()
|
||||
self.switch_backend.switch_sync_finished()
|
||||
|
||||
def switch_sync_started(self):
|
||||
self.open_flow_app.notify_switch_sync_started()
|
||||
self.switch_backend.switch_sync_started()
|
||||
|
||||
def _is_newer(self, obj, cached_obj):
|
||||
'''Check wether obj is newer than cached_on.
|
||||
|
@ -290,11 +260,7 @@ class DfLocalController(object):
|
|||
# TODO(snapiri): We should not have ovs here
|
||||
# TODO(snapiri): This should be part of the interface
|
||||
def notify_port_status(self, ovs_port, status):
|
||||
if self.neutron_notifier:
|
||||
table_name = l2.LogicalPort.table_name
|
||||
iface_id = ovs_port.lport
|
||||
self.neutron_notifier.notify_neutron_server(table_name, iface_id,
|
||||
'update', status)
|
||||
self.switch_backend.notify_port_status(ovs_port, status)
|
||||
|
||||
def _get_delete_handler(self, table):
|
||||
method_name = 'delete_{0}'.format(table)
|
||||
|
@ -329,7 +295,7 @@ class DfLocalController(object):
|
|||
action = update.action
|
||||
if action == ctrl_const.CONTROLLER_REINITIALIZE:
|
||||
self.db_store.clear()
|
||||
self.vswitch_api.initialize(self.db_change_callback)
|
||||
self.switch_backend.initialize(self.db_change_callback)
|
||||
self.sync()
|
||||
elif action == ctrl_const.CONTROLLER_SYNC:
|
||||
self.sync()
|
||||
|
@ -461,12 +427,6 @@ def _has_basic_events(obj):
|
|||
return isinstance(obj, mixins.BasicEvents)
|
||||
|
||||
|
||||
def init_ryu_config():
|
||||
ryu_cfg.CONF(project='ryu', args=[])
|
||||
ryu_cfg.CONF.ofp_listen_host = cfg.CONF.df_ryu.of_listen_address
|
||||
ryu_cfg.CONF.ofp_tcp_listen_port = cfg.CONF.df_ryu.of_listen_port
|
||||
|
||||
|
||||
# Run this application like this:
|
||||
# python df_local_controller.py <chassis_unique_name>
|
||||
# <local ip address> <southbound_db_ip_address>
|
||||
|
@ -474,7 +434,6 @@ def main():
|
|||
chassis_name = cfg.CONF.host
|
||||
df_config.init(sys.argv)
|
||||
|
||||
init_ryu_config()
|
||||
nb_api = api_nb.NbApi.get_instance()
|
||||
controller = DfLocalController(chassis_name, nb_api)
|
||||
service.register_service('df-local-controller', nb_api)
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
# 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 ryu.app.ofctl import service as of_service
|
||||
from ryu.base import app_manager
|
||||
from ryu import cfg as ryu_cfg
|
||||
|
||||
from dragonflow import conf as cfg
|
||||
from dragonflow.controller import ryu_base_app
|
||||
from dragonflow.db.models import l2
|
||||
from dragonflow.db.models import ovs
|
||||
from dragonflow.ovsdb import vswitch_impl
|
||||
from dragonflow.switch.drivers import df_switch_driver
|
||||
|
||||
|
||||
class DfOvsDriver(df_switch_driver.DfSwitchDriver):
|
||||
def __init__(self, nb_api, ip):
|
||||
super(DfOvsDriver, self).__init__(nb_api)
|
||||
init_ryu_config()
|
||||
self.vswitch_api = vswitch_impl.OvsApi(ip)
|
||||
self.app_mgr = app_manager.AppManager.get_instance()
|
||||
self.open_flow_app = None
|
||||
self.open_flow_service = None
|
||||
self.neutron_notifier = None
|
||||
|
||||
def initialize(self, db_change_callback, neutron_notifier):
|
||||
super(DfOvsDriver, self).initialize(db_change_callback,
|
||||
neutron_notifier)
|
||||
self.open_flow_app = self.app_mgr.instantiate(
|
||||
ryu_base_app.RyuDFAdapter,
|
||||
nb_api=self.nb_api,
|
||||
vswitch_api=self.vswitch_api,
|
||||
neutron_server_notifier=self.neutron_notifier,
|
||||
db_change_callback=self.db_change_callback
|
||||
)
|
||||
# The OfctlService is needed to support the 'get_flows' method
|
||||
self.open_flow_service = self.app_mgr.instantiate(
|
||||
of_service.OfctlService)
|
||||
|
||||
def start(self):
|
||||
self.vswitch_api.initialize(self.db_change_callback)
|
||||
# both set_controller and del_controller will delete flows.
|
||||
# for reliability, here we should check if controller is set for OVS,
|
||||
# if yes, don't set controller and don't delete controller.
|
||||
# if no, set controller
|
||||
targets = ('tcp:' + cfg.CONF.df_ryu.of_listen_address + ':' +
|
||||
str(cfg.CONF.df_ryu.of_listen_port))
|
||||
is_controller_set = self.vswitch_api.check_controller(targets)
|
||||
integration_bridge = cfg.CONF.df.integration_bridge
|
||||
if not is_controller_set:
|
||||
self.vswitch_api.set_controller(integration_bridge, [targets])
|
||||
is_fail_mode_set = self.vswitch_api.check_controller_fail_mode(
|
||||
'secure')
|
||||
if not is_fail_mode_set:
|
||||
self.vswitch_api.set_controller_fail_mode(integration_bridge,
|
||||
'secure')
|
||||
self.open_flow_service.start()
|
||||
self.open_flow_app.start()
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def switch_sync_started(self):
|
||||
self.open_flow_app.notify_switch_sync_started()
|
||||
|
||||
def switch_sync_finished(self):
|
||||
self.open_flow_app.notify_switch_sync_finished()
|
||||
|
||||
def sync_ignore_models(self):
|
||||
return [ovs.OvsPort, ]
|
||||
|
||||
def notify_port_status(self, ovs_port, status):
|
||||
if self.neutron_notifier:
|
||||
table_name = l2.LogicalPort.table_name
|
||||
iface_id = ovs_port.lport
|
||||
self.neutron_notifier.notify_neutron_server(table_name, iface_id,
|
||||
'update', status)
|
||||
|
||||
|
||||
def init_ryu_config():
|
||||
ryu_cfg.CONF(project='ryu', args=[])
|
||||
ryu_cfg.CONF.ofp_listen_host = cfg.CONF.df_ryu.of_listen_address
|
||||
ryu_cfg.CONF.ofp_tcp_listen_port = cfg.CONF.df_ryu.of_listen_port
|
|
@ -25,8 +25,9 @@ class DfSwitchDriver(object):
|
|||
self.db_change_callback = None
|
||||
self.nb_api = nb_api
|
||||
|
||||
def initialize(self, db_change_callback):
|
||||
def initialize(self, db_change_callback, neutron_notifier):
|
||||
self.db_change_callback = db_change_callback
|
||||
self.neutron_notifier = neutron_notifier
|
||||
|
||||
@abc.abstractmethod
|
||||
def start(self):
|
||||
|
|
|
@ -83,16 +83,18 @@ class DFAppTestBase(tests_base.BaseTestCase):
|
|||
self.nb_api = api_nb.NbApi.get_instance()
|
||||
self.controller = df_local_controller.DfLocalController(
|
||||
fake_chassis1.id, self.nb_api)
|
||||
self.vswitch_api = self.controller.vswitch_api = mock.MagicMock()
|
||||
switch_backend = self.controller.switch_backend
|
||||
self.vswitch_api = switch_backend.vswitch_api = mock.MagicMock()
|
||||
kwargs = dict(
|
||||
nb_api=self.controller.nb_api,
|
||||
vswitch_api=self.controller.vswitch_api
|
||||
vswitch_api=self.vswitch_api
|
||||
)
|
||||
self.controller.open_flow_app = ryu_base_app.RyuDFAdapter(
|
||||
db_change_callback=self.controller.db_change_callback, **kwargs)
|
||||
self.open_flow_app = self.controller.open_flow_app
|
||||
switch_backend.open_flow_app = ryu_base_app.RyuDFAdapter(
|
||||
db_change_callback=self.controller.db_change_callback,
|
||||
**kwargs)
|
||||
self.open_flow_app = switch_backend.open_flow_app
|
||||
self.datapath = self.open_flow_app._datapath = mock.Mock()
|
||||
self.open_flow_app.load(self.controller.open_flow_app, **kwargs)
|
||||
self.open_flow_app.load(self.open_flow_app, **kwargs)
|
||||
self.topology = self.controller.topology = topology.Topology(
|
||||
self.controller, enable_selective_topo_dist)
|
||||
cfg.CONF.set_override(
|
||||
|
|
|
@ -35,7 +35,8 @@ class TestChassisSNATApp(test_app_base.DFAppTestBase):
|
|||
def test_switch_features_handler(self):
|
||||
ev = mock.Mock()
|
||||
ev.msg.datapath.ofproto.OFP_VERSION = 0x04
|
||||
self.controller.open_flow_app.switch_features_handler(ev)
|
||||
open_flow_app = self.controller.switch_backend.open_flow_app
|
||||
open_flow_app.switch_features_handler(ev)
|
||||
|
||||
self.SNAT_app.add_flow_go_to_table.assert_has_calls(
|
||||
[mock.call(
|
||||
|
|
|
@ -32,8 +32,9 @@ class TestMigrationApp(test_app_base.DFAppTestBase):
|
|||
|
||||
self.controller.db_store.update(fake_lswitch)
|
||||
self.controller.db_store.update(lport)
|
||||
self.controller.vswitch_api.get_chassis_ofport.return_value = 3
|
||||
self.controller.vswitch_api.get_port_ofport_by_id.return_value = 2
|
||||
vswitch_api = self.controller.switch_backend.vswitch_api
|
||||
vswitch_api.get_chassis_ofport.return_value = 3
|
||||
vswitch_api.get_port_ofport_by_id.return_value = 2
|
||||
|
||||
mock_update_patch = mock.patch.object(
|
||||
self.controller.db_store,
|
||||
|
|
|
@ -74,6 +74,8 @@ dragonflow.nb_db_driver =
|
|||
_dummy_nb_db_driver = dragonflow.tests.database._dummy_db_driver:_DummyDbDriver
|
||||
dragonflow.neutron_notifier_driver =
|
||||
nb_api_neutron_notifier_driver = dragonflow.db.pubsub_drivers.nb_api_neutron_notifier:NbApiNeutronNotifier
|
||||
dragonflow.switch_backend_driver =
|
||||
vswitch_backend_driver = dragonflow.switch.drivers.df_ovs_driver:DfOvsDriver
|
||||
neutron.service_plugins =
|
||||
df-l3-agentless = dragonflow.neutron.services.l3_router_plugin:DFL3AgentlessRouterPlugin
|
||||
df-l3 = dragonflow.neutron.services.l3_router_plugin:DFL3RouterPlugin
|
||||
|
|
Loading…
Reference in New Issue