Merge "Ensure that L3 managed port have status ACTIVE"
This commit is contained in:
commit
e4ddee8e82
@ -352,3 +352,13 @@ def get_installed_ovs_klm_version():
|
|||||||
return ver[0]
|
return ver[0]
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_("Unable to retrieve OVS kernel module version."))
|
LOG.exception(_("Unable to retrieve OVS kernel module version."))
|
||||||
|
|
||||||
|
|
||||||
|
def get_bridge_external_bridge_id(root_helper, bridge):
|
||||||
|
args = ["ovs-vsctl", "--timeout=2", "br-get-external-id",
|
||||||
|
bridge, "bridge-id"]
|
||||||
|
try:
|
||||||
|
return utils.execute(args, root_helper=root_helper).strip()
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_("Bridge %s not found."), bridge)
|
||||||
|
return None
|
||||||
|
@ -186,6 +186,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
self._check_ovs_version()
|
self._check_ovs_version()
|
||||||
if self.enable_tunneling:
|
if self.enable_tunneling:
|
||||||
self.setup_tunnel_br(tun_br)
|
self.setup_tunnel_br(tun_br)
|
||||||
|
# Collect additional bridges to monitor
|
||||||
|
self.ancillary_brs = self.setup_ancillary_bridges(integ_br, tun_br)
|
||||||
self.agent_state = {
|
self.agent_state = {
|
||||||
'binary': 'neutron-openvswitch-agent',
|
'binary': 'neutron-openvswitch-agent',
|
||||||
'host': cfg.CONF.host,
|
'host': cfg.CONF.host,
|
||||||
@ -541,6 +543,32 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
int_br.add_flow(priority=1, actions="normal")
|
int_br.add_flow(priority=1, actions="normal")
|
||||||
return int_br
|
return int_br
|
||||||
|
|
||||||
|
def setup_ancillary_bridges(self, integ_br, tun_br):
|
||||||
|
'''Setup ancillary bridges - for example br-ex.'''
|
||||||
|
ovs_bridges = set(ovs_lib.get_bridges(self.root_helper))
|
||||||
|
# Remove all known bridges
|
||||||
|
ovs_bridges.remove(integ_br)
|
||||||
|
if self.enable_tunneling:
|
||||||
|
ovs_bridges.remove(tun_br)
|
||||||
|
br_names = [self.phys_brs[physical_network].br_name for
|
||||||
|
physical_network in self.phys_brs]
|
||||||
|
ovs_bridges.difference_update(br_names)
|
||||||
|
# Filter list of bridges to those that have external
|
||||||
|
# bridge-id's configured
|
||||||
|
br_names = []
|
||||||
|
for bridge in ovs_bridges:
|
||||||
|
id = ovs_lib.get_bridge_external_bridge_id(self.root_helper,
|
||||||
|
bridge)
|
||||||
|
if id != bridge:
|
||||||
|
br_names.append(bridge)
|
||||||
|
ovs_bridges.difference_update(br_names)
|
||||||
|
ancillary_bridges = []
|
||||||
|
for bridge in ovs_bridges:
|
||||||
|
br = ovs_lib.OVSBridge(bridge, self.root_helper)
|
||||||
|
LOG.info(_('Adding %s to list of bridges.'), bridge)
|
||||||
|
ancillary_bridges.append(br)
|
||||||
|
return ancillary_bridges
|
||||||
|
|
||||||
def setup_tunnel_br(self, tun_br):
|
def setup_tunnel_br(self, tun_br):
|
||||||
'''Setup the tunnel bridge.
|
'''Setup the tunnel bridge.
|
||||||
|
|
||||||
@ -633,6 +661,19 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
'added': added,
|
'added': added,
|
||||||
'removed': removed}
|
'removed': removed}
|
||||||
|
|
||||||
|
def update_ancillary_ports(self, registered_ports):
|
||||||
|
ports = set()
|
||||||
|
for bridge in self.ancillary_brs:
|
||||||
|
ports |= bridge.get_vif_port_set()
|
||||||
|
|
||||||
|
if ports == registered_ports:
|
||||||
|
return
|
||||||
|
added = ports - registered_ports
|
||||||
|
removed = registered_ports - ports
|
||||||
|
return {'current': ports,
|
||||||
|
'added': added,
|
||||||
|
'removed': removed}
|
||||||
|
|
||||||
def treat_vif_port(self, vif_port, port_id, network_id, network_type,
|
def treat_vif_port(self, vif_port, port_id, network_id, network_type,
|
||||||
physical_network, segmentation_id, admin_state_up):
|
physical_network, segmentation_id, admin_state_up):
|
||||||
if vif_port:
|
if vif_port:
|
||||||
@ -675,6 +716,21 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
self.port_dead(port)
|
self.port_dead(port)
|
||||||
return resync
|
return resync
|
||||||
|
|
||||||
|
def treat_ancillary_devices_added(self, devices):
|
||||||
|
resync = False
|
||||||
|
for device in devices:
|
||||||
|
LOG.info(_("Ancillary Port %s added"), device)
|
||||||
|
try:
|
||||||
|
self.plugin_rpc.get_device_details(self.context, device,
|
||||||
|
self.agent_id)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug(_("Unable to get port details for "
|
||||||
|
"%(device)s: %(e)s"),
|
||||||
|
{'device': device, 'e': e})
|
||||||
|
resync = True
|
||||||
|
continue
|
||||||
|
return resync
|
||||||
|
|
||||||
def treat_devices_removed(self, devices):
|
def treat_devices_removed(self, devices):
|
||||||
resync = False
|
resync = False
|
||||||
self.sg_agent.remove_devices_filter(devices)
|
self.sg_agent.remove_devices_filter(devices)
|
||||||
@ -697,6 +753,26 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
self.port_unbound(device)
|
self.port_unbound(device)
|
||||||
return resync
|
return resync
|
||||||
|
|
||||||
|
def treat_ancillary_devices_removed(self, devices):
|
||||||
|
resync = False
|
||||||
|
for device in devices:
|
||||||
|
LOG.info(_("Attachment %s removed"), device)
|
||||||
|
try:
|
||||||
|
details = self.plugin_rpc.update_device_down(self.context,
|
||||||
|
device,
|
||||||
|
self.agent_id)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug(_("port_removed failed for %(device)s: %(e)s"),
|
||||||
|
{'device': device, 'e': e})
|
||||||
|
resync = True
|
||||||
|
continue
|
||||||
|
if details['exists']:
|
||||||
|
LOG.info(_("Port %s updated."), device)
|
||||||
|
# Nothing to do regarding local networking
|
||||||
|
else:
|
||||||
|
LOG.debug(_("Device %s not defined on plugin"), device)
|
||||||
|
return resync
|
||||||
|
|
||||||
def process_network_ports(self, port_info):
|
def process_network_ports(self, port_info):
|
||||||
resync_a = False
|
resync_a = False
|
||||||
resync_b = False
|
resync_b = False
|
||||||
@ -707,6 +783,17 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
# If one of the above opertaions fails => resync with plugin
|
# If one of the above opertaions fails => resync with plugin
|
||||||
return (resync_a | resync_b)
|
return (resync_a | resync_b)
|
||||||
|
|
||||||
|
def process_ancillary_network_ports(self, port_info):
|
||||||
|
resync_a = False
|
||||||
|
resync_b = False
|
||||||
|
if 'added' in port_info:
|
||||||
|
resync_a = self.treat_ancillary_devices_added(port_info['added'])
|
||||||
|
if 'removed' in port_info:
|
||||||
|
resync_b = self.treat_ancillary_devices_removed(
|
||||||
|
port_info['removed'])
|
||||||
|
# If one of the above opertaions fails => resync with plugin
|
||||||
|
return (resync_a | resync_b)
|
||||||
|
|
||||||
def tunnel_sync(self):
|
def tunnel_sync(self):
|
||||||
resync = False
|
resync = False
|
||||||
try:
|
try:
|
||||||
@ -733,6 +820,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
def rpc_loop(self):
|
def rpc_loop(self):
|
||||||
sync = True
|
sync = True
|
||||||
ports = set()
|
ports = set()
|
||||||
|
ancillary_ports = set()
|
||||||
tunnel_sync = True
|
tunnel_sync = True
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -741,6 +829,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
if sync:
|
if sync:
|
||||||
LOG.info(_("Agent out of sync with plugin!"))
|
LOG.info(_("Agent out of sync with plugin!"))
|
||||||
ports.clear()
|
ports.clear()
|
||||||
|
ancillary_ports.clear()
|
||||||
sync = False
|
sync = False
|
||||||
|
|
||||||
# Notify the plugin of tunnel IP
|
# Notify the plugin of tunnel IP
|
||||||
@ -757,6 +846,14 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
|||||||
sync = self.process_network_ports(port_info)
|
sync = self.process_network_ports(port_info)
|
||||||
ports = port_info['current']
|
ports = port_info['current']
|
||||||
|
|
||||||
|
# Treat ancillary devices if they exist
|
||||||
|
if self.ancillary_brs:
|
||||||
|
port_info = self.update_ancillary_ports(ancillary_ports)
|
||||||
|
if port_info:
|
||||||
|
rc = self.process_ancillary_network_ports(port_info)
|
||||||
|
ancillary_ports = port_info['current']
|
||||||
|
sync = sync | rc
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_("Error in agent event loop"))
|
LOG.exception(_("Error in agent event loop"))
|
||||||
sync = True
|
sync = True
|
||||||
|
@ -103,6 +103,9 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
|||||||
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
||||||
'OVSNeutronAgent.setup_integration_br',
|
'OVSNeutronAgent.setup_integration_br',
|
||||||
return_value=mock.Mock()),
|
return_value=mock.Mock()),
|
||||||
|
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
||||||
|
'OVSNeutronAgent.setup_ancillary_bridges',
|
||||||
|
return_value=[]),
|
||||||
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
||||||
return_value='00:00:00:00:00:01')):
|
return_value='00:00:00:00:00:01')):
|
||||||
self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs)
|
self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs)
|
||||||
@ -395,3 +398,57 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
|||||||
self._check_ovs_vxlan_version('1.10', '1.9',
|
self._check_ovs_vxlan_version('1.10', '1.9',
|
||||||
constants.MINIMUM_OVS_VXLAN_VERSION,
|
constants.MINIMUM_OVS_VXLAN_VERSION,
|
||||||
expecting_ok=False)
|
expecting_ok=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AncillaryBridgesTest(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(AncillaryBridgesTest, self).setUp()
|
||||||
|
self.addCleanup(cfg.CONF.reset)
|
||||||
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
notifier_p = mock.patch(NOTIFIER)
|
||||||
|
notifier_cls = notifier_p.start()
|
||||||
|
self.notifier = mock.Mock()
|
||||||
|
notifier_cls.return_value = self.notifier
|
||||||
|
# Avoid rpc initialization for unit tests
|
||||||
|
cfg.CONF.set_override('rpc_backend',
|
||||||
|
'neutron.openstack.common.rpc.impl_fake')
|
||||||
|
cfg.CONF.set_override('report_interval', 0, 'AGENT')
|
||||||
|
self.kwargs = ovs_neutron_agent.create_agent_config_map(cfg.CONF)
|
||||||
|
|
||||||
|
def _test_ancillary_bridges(self, bridges, ancillary):
|
||||||
|
device_ids = ancillary[:]
|
||||||
|
|
||||||
|
def pullup_side_effect(self, *args):
|
||||||
|
result = device_ids.pop(0)
|
||||||
|
return result
|
||||||
|
|
||||||
|
with contextlib.nested(
|
||||||
|
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
||||||
|
'OVSNeutronAgent.setup_integration_br',
|
||||||
|
return_value=mock.Mock()),
|
||||||
|
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
||||||
|
return_value='00:00:00:00:00:01'),
|
||||||
|
mock.patch('neutron.agent.linux.ovs_lib.get_bridges',
|
||||||
|
return_value=bridges),
|
||||||
|
mock.patch(
|
||||||
|
'neutron.agent.linux.ovs_lib.get_bridge_external_bridge_id',
|
||||||
|
side_effect=pullup_side_effect)):
|
||||||
|
self.agent = ovs_neutron_agent.OVSNeutronAgent(**self.kwargs)
|
||||||
|
self.assertEqual(len(ancillary), len(self.agent.ancillary_brs))
|
||||||
|
if ancillary:
|
||||||
|
bridges = [br.br_name for br in self.agent.ancillary_brs]
|
||||||
|
for br in ancillary:
|
||||||
|
self.assertIn(br, bridges)
|
||||||
|
|
||||||
|
def test_ancillary_bridges_single(self):
|
||||||
|
bridges = ['br-int', 'br-ex']
|
||||||
|
self._test_ancillary_bridges(bridges, ['br-ex'])
|
||||||
|
|
||||||
|
def test_ancillary_bridges_none(self):
|
||||||
|
bridges = ['br-int']
|
||||||
|
self._test_ancillary_bridges(bridges, [])
|
||||||
|
|
||||||
|
def test_ancillary_bridges_multiple(self):
|
||||||
|
bridges = ['br-int', 'br-ex1', 'br-ex2']
|
||||||
|
self._test_ancillary_bridges(bridges, ['br-ex1', 'br-ex2'])
|
||||||
|
@ -88,6 +88,7 @@ class TunnelTest(base.BaseTestCase):
|
|||||||
|
|
||||||
self.mock_map_tun_bridge = ovs_lib.OVSBridge(
|
self.mock_map_tun_bridge = ovs_lib.OVSBridge(
|
||||||
self.MAP_TUN_BRIDGE, 'sudo')
|
self.MAP_TUN_BRIDGE, 'sudo')
|
||||||
|
self.mock_map_tun_bridge.br_name = self.MAP_TUN_BRIDGE
|
||||||
self.mock_map_tun_bridge.remove_all_flows()
|
self.mock_map_tun_bridge.remove_all_flows()
|
||||||
self.mock_map_tun_bridge.add_flow(priority=1, actions='normal')
|
self.mock_map_tun_bridge.add_flow(priority=1, actions='normal')
|
||||||
self.mock_int_bridge.delete_port('int-tunnel_bridge_mapping')
|
self.mock_int_bridge.delete_port('int-tunnel_bridge_mapping')
|
||||||
@ -124,6 +125,10 @@ class TunnelTest(base.BaseTestCase):
|
|||||||
'phy-tunnel_bridge_mapping').AndReturn([self.inta, self.intb])
|
'phy-tunnel_bridge_mapping').AndReturn([self.inta, self.intb])
|
||||||
|
|
||||||
self.mock_int_bridge.get_local_port_mac().AndReturn('000000000001')
|
self.mock_int_bridge.get_local_port_mac().AndReturn('000000000001')
|
||||||
|
self.mox.StubOutWithMock(ovs_lib, 'get_bridges')
|
||||||
|
ovs_lib.get_bridges('sudo').AndReturn([self.INT_BRIDGE,
|
||||||
|
self.TUN_BRIDGE,
|
||||||
|
self.MAP_TUN_BRIDGE])
|
||||||
|
|
||||||
def testConstruct(self):
|
def testConstruct(self):
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
Loading…
Reference in New Issue
Block a user