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:
Shachar Snapiri 2018-07-10 15:56:09 +03:00
parent 41595e7453
commit cbb9993b3d
7 changed files with 128 additions and 67 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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