[OVN] Check new added segments in OVN mech driver

Check new segments added to OVN mechanism driver. This mechanism
driver does not inherit from ``SimpleAgentMechanismDriverBase``
because OVN has no agents (NOTE 1). However OVN mechanism driver
requires an OVN controller service in each deployed chassis. This
is how OVN driver knows the hosts using this backend.

The segments, attached to an external network (connected to a
physical network), can be mapped to a host if ther is a chassis (OVN
controller agent) in this host.

NOTE 1: OVN provides agent API compatibility, presenting the
controller and the metadata services as agents. But OVN mechanism
driver has no control or provides any information (via RPC) to
those services.

Closes-Bug: #1949967
Change-Id: I570b2251da8a4a25d155ba838346b24afafd727f
This commit is contained in:
Rodolfo Alonso Hernandez
2021-11-05 14:56:15 +00:00
parent 4b1bf15f7b
commit c8427c82da
11 changed files with 121 additions and 32 deletions

View File

@@ -69,6 +69,9 @@ OVN_AGENT_METADATA_ID_KEY = 'neutron:ovn-metadata-id'
OVN_CONTROLLER_AGENT = 'OVN Controller agent'
OVN_CONTROLLER_GW_AGENT = 'OVN Controller Gateway agent'
OVN_METADATA_AGENT = 'OVN Metadata agent'
OVN_CONTROLLER_TYPES = (OVN_CONTROLLER_AGENT,
OVN_CONTROLLER_GW_AGENT,
)
# OVN ACLs have priorities. The highest priority ACL that matches is the one
# that takes effect. Our choice of priority numbers is arbitrary, but it

View File

@@ -364,6 +364,8 @@ class SimpleAgentMechanismDriverBase(AgentMechanismDriverBase,
determine whether or not the specified network segment can be
bound for the agent.
"""
if agent['agent_type'] != self.agent_type:
return False
mappings = self.get_mappings(agent)
allowed_network_types = self.get_allowed_network_types(agent)

View File

@@ -177,6 +177,9 @@ class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
:param agent: agents_db entry describing agent to bind or None
:returns: True if segment can be bound for agent
"""
if agent and agent['agent_type'] != self.agent_type:
return False
network_type = segment[api.NETWORK_TYPE]
if network_type in self.get_allowed_network_types(agent):
if agent:

View File

@@ -33,6 +33,7 @@ from neutron_lib import exceptions as n_exc
from neutron_lib.exceptions import availability_zone as az_exc
from neutron_lib.plugins import directory
from neutron_lib.plugins.ml2 import api
from neutron_lib.utils import helpers
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_db import exception as os_db_exc
@@ -1138,6 +1139,20 @@ class OVNMechanismDriver(api.MechanismDriver):
if phynet in phynets}
segment_service_db.map_segment_to_hosts(context, segment.id, hosts)
def check_segment_for_agent(self, segment, agent):
"""Check if the OVN controller agent br mappings has segment physnet
Only segments on physical networks (flat or vlan) can be associated
to a host.
"""
if agent['agent_type'] not in ovn_const.OVN_CONTROLLER_TYPES:
return False
br_map = agent.get('configurations', {}).get('bridge-mappings', '')
mapping_dict = helpers.parse_mappings(br_map.split(','),
unique_values=False)
return segment['physical_network'] in mapping_dict
def patch_plugin_merge(self, method_name, new_fn, op=operator.add):
old_method = getattr(self._plugin, method_name)

View File

@@ -2350,8 +2350,9 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
def check_segment_for_agent(self, segment, agent):
for mech_driver in self.mechanism_manager.ordered_mech_drivers:
driver_agent_type = getattr(mech_driver.obj, 'agent_type', None)
if driver_agent_type and driver_agent_type == agent['agent_type']:
# TODO(ralonsoh): add "check_segment_for_agent" method to ABC
# "MechanismDriver", returning False by default.
if hasattr(mech_driver.obj, 'check_segment_for_agent'):
if mech_driver.obj.check_segment_for_agent(segment, agent):
return True
return False

View File

@@ -246,17 +246,19 @@ class AgentMechanismGenericTestCase(AgentMechanismBaseTestCase):
api.NETWORK_ID: 'fake_network_id'}]
def test_unknown_type(self):
context = FakePortContext(self.AGENT_TYPE,
self.AGENTS,
self.UNKNOWN_TYPE_SEGMENTS,
vnic_type=self.VNIC_TYPE)
self.context = FakePortContext(self.AGENT_TYPE, self.AGENTS,
self.UNKNOWN_TYPE_SEGMENTS,
vnic_type=self.VNIC_TYPE)
context = self.context
self.driver.bind_port(context)
self._check_unbound(context)
def test_driver_not_responsible_for_ports_allocation(self):
agents = [
{'configurations': {'rp_bandwidths': {'eth0': {}}},
'host': 'host'},
'host': 'host',
'agent_type': self.AGENT_TYPE,
},
]
profile = {}
segments = []

View File

@@ -38,16 +38,24 @@ class LinuxbridgeMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
AGENTS = [{'alive': True,
'configurations': GOOD_CONFIGS,
'host': 'host'}]
'host': 'host',
'agent_type': AGENT_TYPE,
}]
AGENTS_DEAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'dead_host'}]
'host': 'dead_host',
'agent_type': AGENT_TYPE,
}]
AGENTS_BAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'bad_host_1'},
'host': 'bad_host_1',
'agent_type': AGENT_TYPE,
},
{'alive': True,
'configurations': BAD_CONFIGS,
'host': 'bad_host_2'}]
'host': 'bad_host_2',
'agent_type': AGENT_TYPE,
}]
def setUp(self):
super(LinuxbridgeMechanismBaseTestCase, self).setUp()

View File

@@ -34,18 +34,26 @@ class MacvtapMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
AGENT = {'alive': True,
'configurations': GOOD_CONFIGS,
'host': 'host'}
'host': 'host',
'agent_type': AGENT_TYPE,
}
AGENTS = [AGENT]
AGENTS_DEAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'dead_host'}]
'host': 'dead_host',
'agent_type': AGENT_TYPE,
}]
AGENTS_BAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'bad_host_1'},
'host': 'bad_host_1',
'agent_type': AGENT_TYPE,
},
{'alive': True,
'configurations': BAD_CONFIGS,
'host': 'bad_host_2'}]
'host': 'bad_host_2',
'agent_type': AGENT_TYPE,
}]
def setUp(self):
super(MacvtapMechanismBaseTestCase, self).setUp()

View File

@@ -58,13 +58,21 @@ class SriovNicSwitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
BAD_CONFIGS = {'device_mappings': BAD_MAPPINGS}
AGENTS = [{'alive': True,
'configurations': GOOD_CONFIGS}]
'configurations': GOOD_CONFIGS,
'agent_type': AGENT_TYPE,
}]
AGENTS_DEAD = [{'alive': False,
'configurations': GOOD_CONFIGS}]
'configurations': GOOD_CONFIGS,
'agent_type': AGENT_TYPE,
}]
AGENTS_BAD = [{'alive': False,
'configurations': GOOD_CONFIGS},
'configurations': GOOD_CONFIGS,
'agent_type': AGENT_TYPE,
},
{'alive': True,
'configurations': BAD_CONFIGS}]
'configurations': BAD_CONFIGS,
'agent_type': AGENT_TYPE,
}]
def setUp(self):
super(SriovNicSwitchMechanismBaseTestCase, self).setUp()
@@ -76,17 +84,23 @@ class SriovSwitchMechGenericTestCase(SriovNicSwitchMechanismBaseTestCase,
base.AgentMechanismGenericTestCase):
def test_check_segment(self):
"""Validate the check_segment call."""
segment = {'api.NETWORK_TYPE': ""}
segment[api.NETWORK_TYPE] = constants.TYPE_VLAN
self.assertTrue(self.driver.check_segment_for_agent(segment))
agent = {'agent_type': self.AGENT_TYPE,
'configurations': {'device_mappings': ['physnet1']}}
segment = {api.NETWORK_TYPE: constants.TYPE_VLAN,
api.PHYSICAL_NETWORK: 'physnet1'}
self.assertTrue(self.driver.check_segment_for_agent(segment, agent))
# Validate a network type not currently supported
segment[api.NETWORK_TYPE] = constants.TYPE_GRE
self.assertFalse(self.driver.check_segment_for_agent(segment))
self.assertFalse(self.driver.check_segment_for_agent(segment, agent))
def test_check_segment_allows_supported_network_types(self):
for network_type in self.driver.get_allowed_network_types(agent=None):
segment = {api.NETWORK_TYPE: network_type}
self.assertTrue(self.driver.check_segment_for_agent(segment))
agent = {'agent_type': self.AGENT_TYPE,
'configurations': {'device_mappings': ['physnet1']}}
segment = {api.NETWORK_TYPE: network_type,
api.PHYSICAL_NETWORK: 'physnet1'}
self.assertTrue(self.driver.check_segment_for_agent(segment,
agent))
def test_driver_responsible_for_ports_allocation(self):
agents = [

View File

@@ -55,16 +55,24 @@ class OpenvswitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
AGENTS = [{'alive': True,
'configurations': GOOD_CONFIGS,
'host': 'host'}]
'host': 'host',
'agent_type': AGENT_TYPE,
}]
AGENTS_DEAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'dead_host'}]
'host': 'dead_host',
'agent_type': AGENT_TYPE,
}]
AGENTS_BAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'bad_host_1'},
'host': 'bad_host_1',
'agent_type': AGENT_TYPE,
},
{'alive': True,
'configurations': BAD_CONFIGS,
'host': 'bad_host_2'}]
'host': 'bad_host_2',
'agent_type': AGENT_TYPE,
}]
def setUp(self):
super(OpenvswitchMechanismBaseTestCase, self).setUp()
@@ -128,7 +136,9 @@ class OpenvswitchMechanismSGDisabledBaseTestCase(
'tunnel_types': GOOD_TUNNEL_TYPES}
AGENTS = [{'alive': True,
'configurations': GOOD_CONFIGS,
'host': 'host'}]
'host': 'host',
'agent_type': constants.AGENT_TYPE_OVS,
}]
def setUp(self):
cfg.CONF.set_override('enable_security_group',
@@ -151,7 +161,9 @@ class OpenvswitchMechanismHybridPlugTestCase(OpenvswitchMechanismBaseTestCase):
self.driver.vif_details[hybrid] = False
agents = [{'alive': True,
'configurations': {hybrid: True},
'host': 'host'}]
'host': 'host',
'agent_type': self.AGENT_TYPE,
}]
context = self._make_port_ctx(agents)
self.driver.bind_port(context)
self.assertTrue(context._bound_vif_details[hybrid])
@@ -163,7 +175,9 @@ class OpenvswitchMechanismHybridPlugTestCase(OpenvswitchMechanismBaseTestCase):
self.driver.vif_details[hybrid] = True
agents = [{'alive': True,
'configurations': {hybrid: False},
'host': 'host'}]
'host': 'host',
'agent_type': self.AGENT_TYPE,
}]
context = self._make_port_ctx(agents)
self.driver.bind_port(context)
self.assertFalse(context._bound_vif_details[hybrid])

View File

@@ -2607,6 +2607,25 @@ class TestOVNMechanismDriverSegment(MechDriverSetupBase,
3,
ovn_nb_api.delete_lswitch_port.call_count)
def test_check_segment_for_agent(self):
segment = {'physical_network': 'physnet1'}
agent = {'agent_type': ovn_const.OVN_METADATA_AGENT}
self.assertFalse(
self.mech_driver.check_segment_for_agent(segment, agent))
agent = {'agent_type': ovn_const.OVN_CONTROLLER_AGENT,
'configurations': {}}
self.assertFalse(
self.mech_driver.check_segment_for_agent(segment, agent))
agent['configurations'] = {'bridge-mappings': 'physnet2:br-ex2'}
self.assertFalse(
self.mech_driver.check_segment_for_agent(segment, agent))
agent['configurations'] = {'bridge-mappings': 'physnet1:br-ex1'}
self.assertTrue(
self.mech_driver.check_segment_for_agent(segment, agent))
@mock.patch.object(n_net, 'get_random_mac', lambda *_: '01:02:03:04:05:06')
class TestOVNMechanismDriverDHCPOptions(OVNMechanismDriverTestCase):