Pass get_networks() callback to interface driver

In order to support out of tree interface drivers it is required
to pass a callback to allow the drivers to query information about
the network.

- Allow passing **kwargs to interface drivers
- Pass get_networks() as `get_networks_cb` kw arg
  `get_networks_cb` has the same API as
  `neutron.neutron_plugin_base_v2.NeutronPluginBaseV2.get_networks()`
   minus the the request context which will be embeded in the callback
   itself.

The out of tree interface drivers in question are:

MultiInterfaceDriver - a per-physnet interface driver that delegates
                       operations on a per-physnet basis.
IPoIBInterfaceDriver - an interface driver for IPoIB (IP over Infiniband)
                       networks.

Those drivers are a part of networking-mlnx[1], Their implementation
is vendor agnostic so they can later be moved to a more common place
if desired.

[1] https://github.com/openstack/networking-mlnx

Change-Id: I74d9f449fb24f64548b0f6db4d5562f7447efb25
Closes-Bug: #1834176
This commit is contained in:
Adrian Chiris 2019-07-14 11:08:31 +03:00
parent c62c67f413
commit 0e80d2251e
7 changed files with 57 additions and 29 deletions

View File

@ -44,17 +44,20 @@ get_root_helper_child_pid = utils.get_root_helper_child_pid
pid_invoked_with_cmdline = utils.pid_invoked_with_cmdline
def load_interface_driver(conf):
def load_interface_driver(conf, get_networks_callback=None):
"""Load interface driver for agents like DHCP or L3 agent.
:param conf: driver configuration object
:param conf: Driver configuration object
:param get_networks_callback: A callback to get network information.
This will be passed as additional keyword
argument to the interface driver.
:raises SystemExit of 1 if driver cannot be loaded
"""
try:
loaded_class = runtime.load_class_by_alias_or_classname(
INTERFACE_NAMESPACE, conf.interface_driver)
return loaded_class(conf)
return loaded_class(conf, get_networks_callback=get_networks_callback)
except ImportError:
LOG.error("Error loading interface driver '%s'",
conf.interface_driver)

View File

@ -13,6 +13,8 @@
# under the License.
#
import functools
import eventlet
import netaddr
from neutron_lib.agent import constants as agent_consts
@ -292,10 +294,14 @@ class L3NATAgent(ha.AgentMixin,
config=self.conf,
resource_type='router')
self.driver = common_utils.load_interface_driver(self.conf)
self._context = n_context.get_admin_context_without_session()
self.plugin_rpc = L3PluginApi(topics.L3PLUGIN, host)
self.driver = common_utils.load_interface_driver(
self.conf,
get_networks_callback=functools.partial(
self.plugin_rpc.get_networks, self.context))
self.fullsync = True
self.sync_routers_chunk_size = SYNC_ROUTERS_MAX_CHUNK_SIZE

View File

@ -1209,7 +1209,9 @@ class DeviceManager(object):
def __init__(self, conf, plugin):
self.conf = conf
self.plugin = plugin
self.driver = agent_common_utils.load_interface_driver(conf)
self.driver = agent_common_utils.load_interface_driver(
conf,
get_networks_callback=self.plugin.get_networks)
def get_interface_name(self, network, port):
"""Return interface(device) name for use by the DHCP process."""

View File

@ -41,7 +41,7 @@ class LinuxInterfaceDriver(object):
DEV_NAME_LEN = constants.LINUX_DEV_LEN
DEV_NAME_PREFIX = constants.TAP_DEVICE_PREFIX
def __init__(self, conf):
def __init__(self, conf, **kwargs):
self.conf = conf
self._mtu_update_warn_logged = False
@ -314,8 +314,8 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
DEV_NAME_PREFIX = constants.TAP_DEVICE_PREFIX
def __init__(self, conf):
super(OVSInterfaceDriver, self).__init__(conf)
def __init__(self, conf, **kwargs):
super(OVSInterfaceDriver, self).__init__(conf, **kwargs)
if self.conf.ovs_use_veth:
self.DEV_NAME_PREFIX = 'ns-'

View File

@ -599,7 +599,7 @@ class TestDhcpAgent(base.BaseTestCase):
def test_none_interface_driver(self):
cfg.CONF.set_override('interface_driver', None)
self.assertRaises(SystemExit, dhcp.DeviceManager,
cfg.CONF, None)
cfg.CONF, mock.Mock())
def test_nonexistent_interface_driver(self):
# Temporarily turn off mock, so could use the real import_class
@ -608,7 +608,7 @@ class TestDhcpAgent(base.BaseTestCase):
self.addCleanup(self.driver_cls_p.start)
cfg.CONF.set_override('interface_driver', 'foo.bar')
self.assertRaises(SystemExit, dhcp.DeviceManager,
cfg.CONF, None)
cfg.CONF, mock.Mock())
class TestLogArgs(base.BaseTestCase):
@ -1981,7 +1981,8 @@ class TestDeviceManager(base.BaseTestCase):
dh = dhcp.DeviceManager(cfg.CONF, plugin)
dh.destroy(fake_net, 'tap12345678-12')
dvr_cls.assert_called_once_with(cfg.CONF)
dvr_cls.assert_called_once_with(
cfg.CONF, get_networks_callback=plugin.get_networks)
mock_driver.assert_has_calls(
[mock.call.unplug('tap12345678-12',
namespace='qdhcp-' + fake_net.id)])
@ -2003,7 +2004,8 @@ class TestDeviceManager(base.BaseTestCase):
dh = dhcp.DeviceManager(cfg.CONF, plugin)
dh.destroy(fake_net, None)
dvr_cls.assert_called_once_with(cfg.CONF)
dvr_cls.assert_called_once_with(
cfg.CONF, get_networks_callback=plugin.get_networks)
plugin.assert_has_calls(
[mock.call.release_dhcp_port(fake_net.id, mock.ANY)])
self.assertFalse(mock_driver.called)
@ -2027,7 +2029,8 @@ class TestDeviceManager(base.BaseTestCase):
dh = dhcp.DeviceManager(cfg.CONF, plugin)
dh.get_interface_name(fake_net, fake_port)
dvr_cls.assert_called_once_with(cfg.CONF)
dvr_cls.assert_called_once_with(
cfg.CONF, get_networks_callback=plugin.get_networks)
mock_driver.assert_has_calls(
[mock.call.get_device_name(fake_port)])
@ -2045,14 +2048,14 @@ class TestDeviceManager(base.BaseTestCase):
with mock.patch('uuid.uuid5') as uuid5:
uuid5.return_value = '1ae5f96c-c527-5079-82ea-371a01645457'
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
self.assertEqual(expected, dh.get_device_id(fake_net))
uuid5.assert_called_once_with(uuid.NAMESPACE_DNS, local_hostname)
def test_update(self):
# Try with namespaces and no metadata network
cfg.CONF.set_override('enable_metadata_network', False)
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
dh._set_default_route = mock.Mock()
network = mock.Mock()
@ -2063,7 +2066,7 @@ class TestDeviceManager(base.BaseTestCase):
# Meta data network enabled, don't interfere with its gateway.
cfg.CONF.set_override('enable_metadata_network', True)
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
dh._set_default_route = mock.Mock()
dh.update(FakeV4Network(), 'ns-12345678-12')
@ -2071,7 +2074,7 @@ class TestDeviceManager(base.BaseTestCase):
self.assertTrue(dh._set_default_route.called)
def test_set_default_route(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice:
device = mock.Mock()
mock_IPDevice.return_value = device
@ -2085,7 +2088,7 @@ class TestDeviceManager(base.BaseTestCase):
device.route.add_gateway.assert_called_once_with('192.168.0.1')
def test_set_default_route_outside_subnet(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice:
device = mock.Mock()
mock_IPDevice.return_value = device
@ -2101,7 +2104,7 @@ class TestDeviceManager(base.BaseTestCase):
device.route.add_gateway.assert_called_once_with('192.168.1.1')
def test_set_default_route_no_subnet(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice:
device = mock.Mock()
mock_IPDevice.return_value = device
@ -2115,7 +2118,7 @@ class TestDeviceManager(base.BaseTestCase):
self.assertFalse(device.route.add_gateway.called)
def test_set_default_route_no_subnet_delete_gateway(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
v4_gateway = '192.168.0.1'
v6_gateway = '2001:db8:0:1::1'
expected = [mock.call(v4_gateway),
@ -2135,7 +2138,7 @@ class TestDeviceManager(base.BaseTestCase):
self.assertFalse(device.route.add_gateway.called)
def test_set_default_route_no_gateway(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
v4_gateway = '192.168.0.1'
v6_gateway = '2001:db8:0:1::1'
expected = [mock.call(v4_gateway),
@ -2155,7 +2158,7 @@ class TestDeviceManager(base.BaseTestCase):
self.assertFalse(device.route.add_gateway.called)
def test_set_default_route_do_nothing(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
v4_gateway = '192.168.0.1'
v6_gateway = '2001:db8:0:1::1'
with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice:
@ -2171,7 +2174,7 @@ class TestDeviceManager(base.BaseTestCase):
self.assertFalse(device.route.add_gateway.called)
def test_set_default_route_change_gateway(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
v4_gateway = '192.168.0.1'
old_v4_gateway = '192.168.0.2'
v6_gateway = '2001:db8:0:1::1'
@ -2191,7 +2194,7 @@ class TestDeviceManager(base.BaseTestCase):
device.route.add_gateway.assert_has_calls(expected)
def test_set_default_route_change_gateway_outside_subnet(self):
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
v4_gateway = '192.168.1.1'
old_v4_gateway = '192.168.2.1'
v6_gateway = '2001:db8:1:1::1'
@ -2220,7 +2223,7 @@ class TestDeviceManager(base.BaseTestCase):
def test_set_default_route_two_subnets(self):
# Try two subnets. Should set gateway from the first.
dh = dhcp.DeviceManager(cfg.CONF, None)
dh = dhcp.DeviceManager(cfg.CONF, mock.Mock())
v4_gateway = '192.168.1.1'
v6_gateway = '2001:db8:1:1::1'
expected = [mock.call(v4_gateway),

View File

@ -14,6 +14,7 @@
# under the License.
import copy
import functools
from itertools import chain as iter_chain
from itertools import combinations as iter_combinations
@ -33,6 +34,7 @@ from oslo_utils import uuidutils
from testtools import matchers
from neutron.agent.common import resource_processing_queue
from neutron.agent.common import utils as common_utils
from neutron.agent.l3 import agent as l3_agent
from neutron.agent.l3 import dvr_edge_ha_router
from neutron.agent.l3 import dvr_edge_router as dvr_router
@ -4013,3 +4015,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
floating_ips[0], mock.sentinel.interface_name, device)
ri.remove_floating_ip.assert_called_once_with(
device, need_to_remove_fip[0]['cidr'])
@mock.patch.object(functools, 'partial')
@mock.patch.object(common_utils, 'load_interface_driver')
def test_interface_driver_init(self, load_driver_mock, funct_partial_mock):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
load_driver_mock.assert_called_once_with(
self.conf, get_networks_callback=mock.ANY)
funct_partial_mock.assert_called_once_with(
self.plugin_api.get_networks, agent.context)

View File

@ -2808,7 +2808,8 @@ class TestDeviceManager(TestConfBase):
mock_IPDevice.return_value = device
device.route.get_gateway.return_value = None
mgr = dhcp.DeviceManager(self.conf, plugin)
load_interface_driver.assert_called_with(self.conf)
load_interface_driver.assert_called_with(
self.conf, get_networks_callback=plugin.get_networks)
# Setup with no existing DHCP port - expect a new DHCP port to
# be created.
@ -2891,7 +2892,8 @@ class TestDeviceManager(TestConfBase):
mock_IPDevice.return_value = device
device.route.get_gateway.return_value = None
mgr = dhcp.DeviceManager(self.conf, plugin)
self.mock_load_interface_driver.assert_called_with(self.conf)
self.mock_load_interface_driver.assert_called_with(
self.conf, get_networks_callback=plugin.get_networks)
# Setup with a reserved DHCP port.
network = FakeDualNetworkReserved()
@ -2959,7 +2961,8 @@ class TestDeviceManager(TestConfBase):
mock_IPDevice.return_value = device
device.route.get_gateway.return_value = None
mgr = dhcp.DeviceManager(self.conf, plugin)
self.mock_load_interface_driver.assert_called_with(self.conf)
self.mock_load_interface_driver.assert_called_with(
self.conf, get_networks_callback=plugin.get_networks)
# Setup with a reserved DHCP port.
network = FakeDualNetworkReserved2()