[OVN] Add method `sync_ha_chassis_group_network_unified
`
This new method is meant to be used when a network with external ports is connected to a router. In this case, it is needed to create a single scheduler for all the ports connected to the router and matching the same chassis as the gateway router port. This method uses the network ID to assign the group name. It also uses a defined chassis priority (that will match the gateway router port assignation). The created ``HA_Chassis_Group`` must have a set of ``HA_Chassis`` registers that should have the same chassis name and priority as the gateway router port ``Gateway_Chassis``. NOTE: in future developments, the router port ``Gateway_Chassis`` will be replaced with a ``HA_Chassis_Group`` register. Related-Bug: #2125553 Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Change-Id: Ia4f685077a8d72bf28f66daf21225d96f57ddef6
This commit is contained in:
@@ -22,6 +22,7 @@ OVN_SG_EXT_ID_KEY = 'neutron:security_group_id'
|
||||
OVN_SG_RULE_EXT_ID_KEY = 'neutron:security_group_rule_id'
|
||||
OVN_ML2_MECH_DRIVER_NAME = 'ovn'
|
||||
OVN_NETWORK_NAME_EXT_ID_KEY = 'neutron:network_name'
|
||||
OVN_NETWORK_ID_EXT_ID_KEY = 'neutron:network_id'
|
||||
OVN_NETWORK_MTU_EXT_ID_KEY = 'neutron:mtu'
|
||||
OVN_PORT_NAME_EXT_ID_KEY = 'neutron:port_name'
|
||||
OVN_PORT_EXT_ID_KEY = 'neutron:port_id'
|
||||
|
@@ -16,6 +16,7 @@ import functools
|
||||
import inspect
|
||||
import os
|
||||
import random
|
||||
import typing
|
||||
|
||||
import netaddr
|
||||
from neutron_lib.api.definitions import external_net
|
||||
@@ -64,13 +65,34 @@ PortExtraDHCPValidation = collections.namedtuple(
|
||||
BPInfo = collections.namedtuple(
|
||||
'BPInfo', ['bp_param', 'vnic_type', 'capabilities'])
|
||||
|
||||
HAChassisGroupInfo = collections.namedtuple(
|
||||
'HAChassisGroupInfo', ['group_name', 'chassis_list', 'az_hints',
|
||||
'ignore_chassis', 'external_ids'])
|
||||
|
||||
_OVS_PERSIST_UUID = _SENTINEL = object()
|
||||
|
||||
|
||||
class HAChassisGroupInfo:
|
||||
def __init__(self,
|
||||
group_name: str,
|
||||
chassis_list: list[typing.Any],
|
||||
az_hints: list[str],
|
||||
ignore_chassis: set[str],
|
||||
external_ids: dict[str, typing.Any],
|
||||
priority: dict[str, typing.Any] | None = None):
|
||||
if priority:
|
||||
# If present, the "priority" dictionary must contain all the
|
||||
# chassis names present in "chassis_list".
|
||||
ch_name_list = [ch.name for ch in chassis_list]
|
||||
if sorted(ch_name_list) != sorted(list(priority.keys())):
|
||||
raise RuntimeError(_(
|
||||
'In a "HAChassisGroupInfo", the "chassis_list" must have '
|
||||
'the same chassis as the "priority" dictionary'))
|
||||
|
||||
self.group_name = group_name
|
||||
self.chassis_list = chassis_list
|
||||
self.az_hints = az_hints
|
||||
self.ignore_chassis = ignore_chassis
|
||||
self.external_ids = external_ids
|
||||
self.priority = priority
|
||||
|
||||
|
||||
class OvsdbClientCommand:
|
||||
_CONNECTION = 0
|
||||
_PRIVATE_KEY = 1
|
||||
@@ -1112,6 +1134,16 @@ def _sync_ha_chassis_group(nb_idl, hcg_info, txn):
|
||||
:returns: The HA Chassis Group UUID or the HA Chassis Group command object,
|
||||
The name of the Chassis with the highest priority (could be None)
|
||||
"""
|
||||
def get_priority(ch_name):
|
||||
nonlocal priority
|
||||
nonlocal hcg_info
|
||||
if hcg_info.priority:
|
||||
return hcg_info.priority[ch_name]
|
||||
|
||||
_priority = int(priority)
|
||||
priority -= 1
|
||||
return _priority
|
||||
|
||||
# If there are Chassis marked for hosting external ports create a HA
|
||||
# Chassis Group per external port, otherwise do it at the network level
|
||||
candidates = _filter_candidates_for_ha_chassis_group(hcg_info)
|
||||
@@ -1164,8 +1196,7 @@ def _sync_ha_chassis_group(nb_idl, hcg_info, txn):
|
||||
ch_ordered_list = [ch[0] for ch in ch_ordered_list] + ch_add_list
|
||||
for ch in ch_ordered_list:
|
||||
txn.add(nb_idl.ha_chassis_group_add_chassis(
|
||||
hcg_info.group_name, ch, priority=priority))
|
||||
priority -= 1
|
||||
hcg_info.group_name, ch, priority=get_priority(ch)))
|
||||
if not high_prio_ch_name:
|
||||
high_prio_ch_name = ch
|
||||
|
||||
@@ -1222,13 +1253,60 @@ def sync_ha_chassis_group_network(context, nb_idl, sb_idl, port_id,
|
||||
plugin = directory.get_plugin()
|
||||
resource = plugin.get_network(context, network_id)
|
||||
az_hints = common_utils.get_az_hints(resource)
|
||||
external_ids = {constants.OVN_AZ_HINTS_EXT_ID_KEY: ','.join(az_hints)}
|
||||
external_ids = {constants.OVN_AZ_HINTS_EXT_ID_KEY: ','.join(az_hints),
|
||||
constants.OVN_NETWORK_ID_EXT_ID_KEY: network_id,
|
||||
}
|
||||
hcg_info = HAChassisGroupInfo(
|
||||
group_name=group_name, chassis_list=chassis_list, az_hints=az_hints,
|
||||
ignore_chassis=ignore_chassis, external_ids=external_ids)
|
||||
return _sync_ha_chassis_group(nb_idl, hcg_info, txn)
|
||||
|
||||
|
||||
@ovn_context(idl_var_name='nb_idl')
|
||||
def sync_ha_chassis_group_network_unified(context, nb_idl, sb_idl, network_id,
|
||||
router_id, chassis_prio, txn):
|
||||
"""Creates a single HA_Chassis_Group for a given network
|
||||
|
||||
This method creates a single HA_Chassis_Group for a network. This method
|
||||
is called when a network with external ports is connected to a router;
|
||||
in order to provide N/S connectivity all external ports need to be bound
|
||||
to the same chassis as the gateway Logical_Router_Port.
|
||||
|
||||
The chassis list and the priority is already provided. This method checks
|
||||
if all gateway chassis provided have external connectivity to this network.
|
||||
"""
|
||||
chassis_physnets = sb_idl.get_chassis_and_physnets()
|
||||
group_name = ovn_name(network_id)
|
||||
ls = nb_idl.get_lswitch(group_name)
|
||||
|
||||
# It is expected to be called for a non-tunnelled network with a physical
|
||||
# network assigned.
|
||||
physnet = ls.external_ids.get(constants.OVN_PHYSNET_EXT_ID_KEY)
|
||||
if physnet:
|
||||
missing_mappings = set()
|
||||
for ch_name in chassis_prio:
|
||||
if physnet not in chassis_physnets[ch_name]:
|
||||
missing_mappings.add(ch_name)
|
||||
|
||||
if missing_mappings:
|
||||
LOG.warning('The following chassis do not have mapped the '
|
||||
f'physical network {physnet}: {missing_mappings}')
|
||||
|
||||
chassis_list = [sb_idl.lookup('Chassis', ch_name, None)
|
||||
for ch_name in chassis_prio.keys()]
|
||||
plugin = directory.get_plugin()
|
||||
resource = plugin.get_network(context, network_id)
|
||||
az_hints = common_utils.get_az_hints(resource)
|
||||
external_ids = {constants.OVN_AZ_HINTS_EXT_ID_KEY: ','.join(az_hints),
|
||||
constants.OVN_NETWORK_ID_EXT_ID_KEY: network_id,
|
||||
constants.OVN_ROUTER_ID_EXT_ID_KEY: router_id,
|
||||
}
|
||||
hcg_info = HAChassisGroupInfo(
|
||||
group_name=group_name, chassis_list=chassis_list, az_hints=az_hints,
|
||||
ignore_chassis=set(), external_ids=external_ids, priority=chassis_prio)
|
||||
return _sync_ha_chassis_group(nb_idl, hcg_info, txn)
|
||||
|
||||
|
||||
def get_port_type_virtual_and_parents(subnets_by_id, fixed_ips, network_id,
|
||||
port_id, nb_idl):
|
||||
"""Returns if a port is type virtual and its corresponding parents.
|
||||
|
@@ -15,6 +15,7 @@
|
||||
import ddt
|
||||
from neutron_lib.api.definitions import external_net
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.api.definitions import provider_net
|
||||
from oslo_utils import uuidutils
|
||||
from ovsdbapp.backend.ovs_idl import event
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
@@ -212,6 +213,54 @@ class TestSyncHaChassisGroup(base.TestOVNFunctionalBase):
|
||||
self.nb_api.ha_chassis_group_get(hcg_name).execute,
|
||||
check_error=True)
|
||||
|
||||
def _test_sync_unify_ha_chassis_group_network(self, create_hcg=False):
|
||||
physnet = 'physnet1'
|
||||
net_ext_args = {provider_net.NETWORK_TYPE: 'vlan',
|
||||
provider_net.PHYSICAL_NETWORK: physnet,
|
||||
external_net.EXTERNAL: True}
|
||||
net_ext = self._make_network(self.fmt, 'test-ext-net', True,
|
||||
as_admin=True,
|
||||
arg_list=tuple(net_ext_args.keys()),
|
||||
**net_ext_args)['network']
|
||||
other_config = {'ovn-bridge-mappings': physnet + ':br-ex'}
|
||||
ch1 = self.add_fake_chassis('host1', azs=[], enable_chassis_as_gw=True,
|
||||
other_config=other_config)
|
||||
ch2 = self.add_fake_chassis('host2', azs=[], enable_chassis_as_gw=True,
|
||||
other_config=other_config)
|
||||
ch3 = self.add_fake_chassis('host3', azs=[], enable_chassis_as_gw=True)
|
||||
group_name = utils.ovn_name(net_ext['id'])
|
||||
|
||||
# Create a pre-existing HCG.
|
||||
if create_hcg:
|
||||
chassis_list = [self.sb_api.lookup('Chassis', ch2)]
|
||||
hcg_info = utils.HAChassisGroupInfo(
|
||||
group_name=group_name, chassis_list=chassis_list,
|
||||
az_hints=[], ignore_chassis=set(), external_ids={})
|
||||
with self.nb_api.transaction(check_error=True) as txn:
|
||||
utils._sync_ha_chassis_group(self.nb_api, hcg_info, txn)
|
||||
hcg = self.nb_api.lookup('HA_Chassis_Group', group_name)
|
||||
self.assertEqual(1, len(hcg.ha_chassis))
|
||||
self.assertEqual(ovn_const.HA_CHASSIS_GROUP_HIGHEST_PRIORITY,
|
||||
hcg.ha_chassis[0].priority)
|
||||
|
||||
# Invoke the sync method
|
||||
chassis_prio = {ch1: 10, ch2: 20, ch3: 30}
|
||||
with self.nb_api.transaction(check_error=True) as txn:
|
||||
utils.sync_ha_chassis_group_network_unified(
|
||||
self.context, self.nb_api, self.sb_api, net_ext['id'],
|
||||
'router-id', chassis_prio, txn)
|
||||
|
||||
hcg = self.nb_api.lookup('HA_Chassis_Group', group_name)
|
||||
self.assertEqual(3, len(hcg.ha_chassis))
|
||||
for hc in hcg.ha_chassis:
|
||||
self.assertEqual(chassis_prio[hc.chassis_name], hc.priority)
|
||||
|
||||
def test_sync_unify_ha_chassis_group_network_no_hcg(self):
|
||||
self._test_sync_unify_ha_chassis_group_network()
|
||||
|
||||
def test_sync_unify_ha_chassis_group_network_existing_hcg(self):
|
||||
self._test_sync_unify_ha_chassis_group_network(create_hcg=True)
|
||||
|
||||
|
||||
@utils.ovn_context()
|
||||
def method_with_idl_and_default_txn(ls_name, idl, txn=None):
|
||||
|
@@ -2993,8 +2993,10 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
||||
'fake-net-id', fake_txn)
|
||||
|
||||
# Assert it creates the HA Chassis Group
|
||||
ext_ids = {ovn_const.OVN_AZ_HINTS_EXT_ID_KEY:
|
||||
','.join(hcg_info.az_hints)}
|
||||
ext_ids = {
|
||||
ovn_const.OVN_AZ_HINTS_EXT_ID_KEY: ','.join(hcg_info.az_hints),
|
||||
ovn_const.OVN_NETWORK_ID_EXT_ID_KEY: 'fake-net-id',
|
||||
}
|
||||
self.nb_ovn.ha_chassis_group_add.assert_called_once_with(
|
||||
hcg_info.group_name, may_exist=True, external_ids=ext_ids)
|
||||
|
||||
|
Reference in New Issue
Block a user