Improve "sync_ha_chassis_group" method
The method "sync_ha_chassis_group" now creates (or retrieves) a HA Chassis Group register and updates the needed HA Chassis registers in a single transaction. That is possible using the new ovsdbapp release 2.2.1 (check the depends-on patch). Depends-On: https://review.opendev.org/c/openstack/ovsdbapp/+/871836 Related-Bug: #1995078 Change-Id: I936855214c635de0e89d5d13a86562f5b282633c
This commit is contained in:
parent
0360eb8e12
commit
ac231c8174
@ -14,6 +14,7 @@ import collections
|
||||
import copy
|
||||
import inspect
|
||||
import os
|
||||
import random
|
||||
|
||||
import netaddr
|
||||
from neutron_lib.api.definitions import external_net
|
||||
@ -34,6 +35,7 @@ from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import netutils
|
||||
from oslo_utils import strutils
|
||||
from ovsdbapp.backend.ovs_idl import rowview
|
||||
from ovsdbapp import constants as ovsdbapp_const
|
||||
import tenacity
|
||||
|
||||
@ -853,3 +855,76 @@ def get_ovn_chassis_other_config(chassis):
|
||||
return chassis.other_config
|
||||
except AttributeError:
|
||||
return chassis.external_ids
|
||||
|
||||
|
||||
def sync_ha_chassis_group(context, network_id, nb_idl, sb_idl, txn):
|
||||
"""Return the UUID of the HA Chassis Group or the HA Chassis Group cmd.
|
||||
|
||||
Given the Neutron Network ID, this method will return (or create
|
||||
and then return) the appropriate HA Chassis Group the external
|
||||
port (in that network) needs to be associated with.
|
||||
|
||||
:param context: Neutron API context.
|
||||
:param network_id: The Neutron network ID.
|
||||
:param nb_idl: OVN NB IDL
|
||||
:param sb_idl: OVN SB IDL
|
||||
:param txn: The ovsdbapp transaction object.
|
||||
:returns: The HA Chassis Group UUID or the HA Chassis Group command object.
|
||||
"""
|
||||
plugin = directory.get_plugin()
|
||||
az_hints = common_utils.get_az_hints(
|
||||
plugin.get_network(context, network_id))
|
||||
|
||||
ha_ch_grp_name = ovn_name(network_id)
|
||||
ext_ids = {constants.OVN_AZ_HINTS_EXT_ID_KEY: ','.join(az_hints)}
|
||||
hcg_cmd = txn.add(nb_idl.ha_chassis_group_add(
|
||||
ha_ch_grp_name, may_exist=True, external_ids=ext_ids))
|
||||
|
||||
if isinstance(hcg_cmd.result, rowview.RowView):
|
||||
# The HA chassis group existed before this transaction.
|
||||
ha_ch_grp = hcg_cmd.result
|
||||
else:
|
||||
# The HA chassis group is being created in this transaction.
|
||||
ha_ch_grp = None
|
||||
|
||||
# Get the chassis belonging to the AZ hints
|
||||
ch_list = sb_idl.get_gateway_chassis_from_cms_options(name_only=False)
|
||||
if not az_hints:
|
||||
az_chassis = get_gateway_chassis_without_azs(ch_list)
|
||||
else:
|
||||
az_chassis = get_chassis_in_azs(ch_list, az_hints)
|
||||
|
||||
priority = constants.HA_CHASSIS_GROUP_HIGHEST_PRIORITY
|
||||
if ha_ch_grp:
|
||||
# Remove any chassis that no longer belongs to the AZ hints
|
||||
all_ch = {ch.chassis_name for ch in ha_ch_grp.ha_chassis}
|
||||
ch_to_del = all_ch - az_chassis
|
||||
for ch in ch_to_del:
|
||||
txn.add(nb_idl.ha_chassis_group_del_chassis(
|
||||
ha_ch_grp_name, ch, if_exists=True))
|
||||
|
||||
# Find the highest priority chassis in the HA Chassis Group. If
|
||||
# it exists and still belongs to the same AZ, keep it as the
|
||||
# highest priority in the group to avoid ports already bond to it
|
||||
# from moving to another chassis.
|
||||
high_prio_ch = max(ha_ch_grp.ha_chassis, key=lambda x: x.priority,
|
||||
default=None)
|
||||
priority = constants.HA_CHASSIS_GROUP_HIGHEST_PRIORITY
|
||||
if high_prio_ch and high_prio_ch.chassis_name in az_chassis:
|
||||
txn.add(nb_idl.ha_chassis_group_add_chassis(
|
||||
ha_ch_grp_name, high_prio_ch.chassis_name,
|
||||
priority=priority))
|
||||
az_chassis.remove(high_prio_ch.chassis_name)
|
||||
priority -= 1
|
||||
|
||||
# Randomize the order so that networks belonging to the same
|
||||
# availability zones do not necessarily end up with the same
|
||||
# Chassis as the highest priority one.
|
||||
for ch in random.sample(list(az_chassis), len(az_chassis)):
|
||||
txn.add(nb_idl.ha_chassis_group_add_chassis(
|
||||
hcg_cmd, ch, priority=priority))
|
||||
priority -= 1
|
||||
|
||||
# Return the existing register UUID or the HA chassis group creation
|
||||
# command (see ovsdbapp ``HAChassisGroupAddChassisCommand`` class).
|
||||
return ha_ch_grp.uuid if ha_ch_grp else hcg_cmd
|
||||
|
@ -15,6 +15,7 @@
|
||||
from oslo_utils import timeutils
|
||||
from ovsdbapp.backend.ovs_idl import command
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp import utils as ovsdbapp_utils
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
@ -137,6 +138,14 @@ class AddLSwitchPortCommand(command.BaseCommand):
|
||||
port.dhcpv6_options = dhcpv6_options
|
||||
else:
|
||||
port.dhcpv6_options = [dhcpv6_options.result]
|
||||
|
||||
# NOTE(ralonsoh): HA chassis group is created by Neutron, there is no
|
||||
# need to create it in this command.
|
||||
ha_chassis_group = self.columns.pop('ha_chassis_group', None)
|
||||
if ha_chassis_group:
|
||||
hcg_uuid = ovsdbapp_utils.get_uuid(ha_chassis_group)
|
||||
port.ha_chassis_group = hcg_uuid
|
||||
|
||||
for col, val in self.columns.items():
|
||||
setattr(port, col, val)
|
||||
# add the newly created port to existing lswitch
|
||||
@ -202,6 +211,21 @@ class SetLSwitchPortCommand(command.BaseCommand):
|
||||
external_ids[k] = v
|
||||
port.external_ids = external_ids
|
||||
|
||||
# NOTE(ralonsoh): HA chassis group is created by Neutron, there is no
|
||||
# need to create it in this command. The register is also deleted when
|
||||
# the network to which the HA chassis group is associated is deleted.
|
||||
ha_chassis_group = self.columns.pop('ha_chassis_group', None)
|
||||
if ha_chassis_group:
|
||||
hcg_uuid = ovsdbapp_utils.get_uuid(ha_chassis_group)
|
||||
try:
|
||||
port_hcg_uuid = port.ha_chassis_group[0].uuid
|
||||
except IndexError:
|
||||
port_hcg_uuid = None
|
||||
if port_hcg_uuid != hcg_uuid:
|
||||
port.ha_chassis_group = hcg_uuid
|
||||
elif ha_chassis_group == []:
|
||||
port.ha_chassis_group = []
|
||||
|
||||
for col, val in self.columns.items():
|
||||
setattr(port, col, val)
|
||||
|
||||
|
@ -603,15 +603,10 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||
network_id = port.external_ids[
|
||||
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY].replace(
|
||||
ovn_const.OVN_NAME_PREFIX, '')
|
||||
ha_ch_grp = self._ovn_client.sync_ha_chassis_group(
|
||||
context, network_id, txn)
|
||||
try:
|
||||
port_ha_ch_uuid = port.ha_chassis_group[0].uuid
|
||||
except IndexError:
|
||||
port_ha_ch_uuid = None
|
||||
if port_ha_ch_uuid != ha_ch_grp:
|
||||
txn.add(self._nb_idl.set_lswitch_port(
|
||||
port.name, ha_chassis_group=ha_ch_grp))
|
||||
ha_ch_grp = utils.sync_ha_chassis_group(
|
||||
context, network_id, self._nb_idl, self._sb_idl, txn)
|
||||
txn.add(self._nb_idl.set_lswitch_port(
|
||||
port.name, ha_chassis_group=ha_ch_grp))
|
||||
|
||||
self._delete_default_ha_chassis_group(txn)
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
import collections
|
||||
import copy
|
||||
import datetime
|
||||
import random
|
||||
|
||||
import netaddr
|
||||
from neutron_lib.api.definitions import l3
|
||||
@ -447,76 +446,6 @@ class OVNClient(object):
|
||||
bp_info.vnic_type, bp_info.capabilities
|
||||
)
|
||||
|
||||
def sync_ha_chassis_group(self, context, network_id, txn):
|
||||
"""Return the UUID of the HA Chassis Group.
|
||||
|
||||
Given the Neutron Network ID, this method will return (or create
|
||||
and then return) the appropriate HA Chassis Group the external
|
||||
port (in that network) needs to be associated with.
|
||||
|
||||
:param context: Neutron API context.
|
||||
:param network_id: The Neutron network ID.
|
||||
:param txn: The ovsdbapp transaction object.
|
||||
:returns: An HA Chassis Group UUID.
|
||||
"""
|
||||
az_hints = common_utils.get_az_hints(
|
||||
self._plugin.get_network(context, network_id))
|
||||
|
||||
ha_ch_grp_name = utils.ovn_name(network_id)
|
||||
# FIXME(lucasagomes): Couldn't find a better way of doing this
|
||||
# without a sub-transaction. This shouldn't be a problem since
|
||||
# the HA Chassis Group associated with a network will be deleted
|
||||
# as part of the network delete method (if present)
|
||||
with self._nb_idl.create_transaction(check_error=True) as sub_txn:
|
||||
sub_txn.add(self._nb_idl.ha_chassis_group_add(
|
||||
ha_ch_grp_name, may_exist=True))
|
||||
|
||||
ha_ch_grp = self._nb_idl.ha_chassis_group_get(
|
||||
ha_ch_grp_name).execute(check_error=True)
|
||||
txn.add(self._nb_idl.db_set(
|
||||
'HA_Chassis_Group', ha_ch_grp_name,
|
||||
('external_ids',
|
||||
{ovn_const.OVN_AZ_HINTS_EXT_ID_KEY: ','.join(az_hints)})))
|
||||
|
||||
# Get the chassis belonging to the AZ hints
|
||||
ch_list = self._sb_idl.get_gateway_chassis_from_cms_options(
|
||||
name_only=False)
|
||||
if not az_hints:
|
||||
az_chassis = utils.get_gateway_chassis_without_azs(ch_list)
|
||||
else:
|
||||
az_chassis = utils.get_chassis_in_azs(ch_list, az_hints)
|
||||
|
||||
# Remove any chassis that no longer belongs to the AZ hints
|
||||
all_ch = {ch.chassis_name for ch in ha_ch_grp.ha_chassis}
|
||||
ch_to_del = all_ch - az_chassis
|
||||
for ch in ch_to_del:
|
||||
txn.add(self._nb_idl.ha_chassis_group_del_chassis(
|
||||
ha_ch_grp_name, ch, if_exists=True))
|
||||
|
||||
# Find the highest priority chassis in the HA Chassis Group. If
|
||||
# it exists and still belongs to the same AZ, keep it as the highest
|
||||
# priority in the group to avoid ports already bond to it from
|
||||
# moving to another chassis.
|
||||
high_prio_ch = max(ha_ch_grp.ha_chassis, key=lambda x: x.priority,
|
||||
default=None)
|
||||
priority = ovn_const.HA_CHASSIS_GROUP_HIGHEST_PRIORITY
|
||||
if high_prio_ch and high_prio_ch.chassis_name in az_chassis:
|
||||
txn.add(self._nb_idl.ha_chassis_group_add_chassis(
|
||||
ha_ch_grp_name, high_prio_ch.chassis_name,
|
||||
priority=priority))
|
||||
az_chassis.remove(high_prio_ch.chassis_name)
|
||||
priority -= 1
|
||||
|
||||
# Randomize the order so that networks belonging to the same
|
||||
# availability zones do not necessarily end up with the same
|
||||
# Chassis as the highest priority one.
|
||||
for ch in random.sample(list(az_chassis), len(az_chassis)):
|
||||
txn.add(self._nb_idl.ha_chassis_group_add_chassis(
|
||||
ha_ch_grp_name, ch, priority=priority))
|
||||
priority -= 1
|
||||
|
||||
return ha_ch_grp.uuid
|
||||
|
||||
def update_port_dhcp_options(self, port_info, txn):
|
||||
dhcpv4_options = []
|
||||
dhcpv6_options = []
|
||||
@ -598,9 +527,9 @@ class OVNClient(object):
|
||||
|
||||
if (self.is_external_ports_supported() and
|
||||
port_info.type == ovn_const.LSP_TYPE_EXTERNAL):
|
||||
kwargs['ha_chassis_group'] = (
|
||||
self.sync_ha_chassis_group(
|
||||
context, port['network_id'], txn))
|
||||
kwargs['ha_chassis_group'] = utils.sync_ha_chassis_group(
|
||||
context, port['network_id'], self._nb_idl, self._sb_idl,
|
||||
txn)
|
||||
|
||||
# NOTE(mjozefcz): Do not set addresses if the port is not
|
||||
# bound, has no device_owner and it is OVN LB VIP port.
|
||||
@ -721,8 +650,9 @@ class OVNClient(object):
|
||||
if self.is_external_ports_supported():
|
||||
if port_info.type == ovn_const.LSP_TYPE_EXTERNAL:
|
||||
columns_dict['ha_chassis_group'] = (
|
||||
self.sync_ha_chassis_group(
|
||||
context, port['network_id'], txn))
|
||||
utils.sync_ha_chassis_group(
|
||||
context, port['network_id'], self._nb_idl,
|
||||
self._sb_idl, txn))
|
||||
else:
|
||||
# Clear the ha_chassis_group field
|
||||
columns_dict['ha_chassis_group'] = []
|
||||
@ -2068,7 +1998,9 @@ class OVNClient(object):
|
||||
neutron_net_azs = lswitch_params['external_ids'].get(
|
||||
ovn_const.OVN_AZ_HINTS_EXT_ID_KEY, '')
|
||||
if ovn_ls_azs != neutron_net_azs:
|
||||
self.sync_ha_chassis_group(context, network['id'], txn)
|
||||
utils.sync_ha_chassis_group(
|
||||
context, network['id'], self._nb_idl,
|
||||
self._sb_idl, txn)
|
||||
|
||||
# Update the segment tags, if any
|
||||
segments = segments_db.get_network_segments(context, network['id'])
|
||||
|
@ -12,6 +12,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.common.ovn import utils
|
||||
from neutron.tests.functional import base
|
||||
@ -58,3 +62,56 @@ class TestCreateNeutronPgDrop(base.TestOVNFunctionalBase):
|
||||
self.assertEqual(directions[0], acl2.direction)
|
||||
self.assertEqual('drop', acl2.action)
|
||||
self.assertEqual(matches[0], acl2.match)
|
||||
|
||||
|
||||
class TestSyncHaChassisGroup(base.TestOVNFunctionalBase):
|
||||
|
||||
def test_sync_ha_chassis_group(self):
|
||||
plugin = mock.Mock()
|
||||
plugin.get_network.return_value = {}
|
||||
network_id = uuidutils.generate_uuid()
|
||||
hcg_name = utils.ovn_name(network_id)
|
||||
chassis1 = self.add_fake_chassis('host1', azs=[],
|
||||
enable_chassis_as_gw=True)
|
||||
chassis2 = self.add_fake_chassis('host2', azs=[],
|
||||
enable_chassis_as_gw=True)
|
||||
self.add_fake_chassis('host3')
|
||||
|
||||
with self.nb_api.transaction(check_error=True) as txn:
|
||||
utils.sync_ha_chassis_group(self.context, network_id, self.nb_api,
|
||||
self.sb_api, txn)
|
||||
|
||||
ha_chassis = self.nb_api.db_find('HA_Chassis').execute(
|
||||
check_error=True)
|
||||
ha_chassis_names = [hc['chassis_name'] for hc in ha_chassis]
|
||||
self.assertEqual(2, len(ha_chassis))
|
||||
self.assertEqual(sorted([chassis1, chassis2]),
|
||||
sorted(ha_chassis_names))
|
||||
|
||||
hcg = self.nb_api.ha_chassis_group_get(hcg_name).execute(
|
||||
check_error=True)
|
||||
self.assertEqual(hcg_name, hcg.name)
|
||||
ha_chassis_exp = sorted([str(hc['_uuid']) for hc in ha_chassis])
|
||||
ha_chassis_ret = sorted([str(hc.uuid) for hc in hcg.ha_chassis])
|
||||
self.assertEqual(ha_chassis_exp, ha_chassis_ret)
|
||||
|
||||
# Delete one GW chassis and resync the HA chassis group associated to
|
||||
# the same network. The method will now not create again the existing
|
||||
# HA Chassis Group register but will update the "ha_chassis" list.
|
||||
self.del_fake_chassis(chassis2)
|
||||
with self.nb_api.transaction(check_error=True) as txn:
|
||||
utils.sync_ha_chassis_group(self.context, network_id, self.nb_api,
|
||||
self.sb_api, txn)
|
||||
|
||||
ha_chassis = self.nb_api.db_find('HA_Chassis').execute(
|
||||
check_error=True)
|
||||
ha_chassis_names = [hc['chassis_name'] for hc in ha_chassis]
|
||||
self.assertEqual(1, len(ha_chassis))
|
||||
self.assertEqual([chassis1], ha_chassis_names)
|
||||
|
||||
hcg = self.nb_api.ha_chassis_group_get(hcg_name).execute(
|
||||
check_error=True)
|
||||
self.assertEqual(hcg_name, hcg.name)
|
||||
ha_chassis_exp = str(ha_chassis[0]['_uuid'])
|
||||
ha_chassis_ret = str(hcg.ha_chassis[0].uuid)
|
||||
self.assertEqual(ha_chassis_exp, ha_chassis_ret)
|
||||
|
@ -15,6 +15,7 @@
|
||||
import copy
|
||||
import uuid
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
from ovsdbapp.backend.ovs_idl import connection
|
||||
from ovsdbapp import constants as const
|
||||
from ovsdbapp import event as ovsdb_event
|
||||
@ -269,6 +270,43 @@ class TestNbApi(BaseOvnIdlTest):
|
||||
self.assertIn((lb_match['name'], lb_match['external_ids']),
|
||||
exp_values)
|
||||
|
||||
def test_create_lswitch_port_ha_chassis_group(self):
|
||||
ls_name = uuidutils.generate_uuid()
|
||||
lsp_name = uuidutils.generate_uuid()
|
||||
hcg_name = uuidutils.generate_uuid()
|
||||
self.nbapi.ha_chassis_group_add(hcg_name).execute(check_error=True)
|
||||
hcg = self.nbapi.lookup('HA_Chassis_Group', hcg_name)
|
||||
self.nbapi.ls_add(ls_name).execute(check_error=True)
|
||||
self.nbapi.create_lswitch_port(
|
||||
lsp_name, ls_name, ha_chassis_group=hcg.uuid).execute(
|
||||
check_error=True)
|
||||
lsp = self.nbapi.lookup('Logical_Switch_Port', lsp_name)
|
||||
self.assertEqual(hcg.uuid, lsp.ha_chassis_group[0].uuid)
|
||||
|
||||
def test_set_lswitch_port_ha_chassis_group(self):
|
||||
ls_name = uuidutils.generate_uuid()
|
||||
lsp_name = uuidutils.generate_uuid()
|
||||
self.nbapi.ls_add(ls_name).execute(check_error=True)
|
||||
self.nbapi.create_lswitch_port(lsp_name, ls_name).execute(
|
||||
check_error=True)
|
||||
lsp = self.nbapi.lookup('Logical_Switch_Port', lsp_name)
|
||||
self.assertEqual([], lsp.ha_chassis_group)
|
||||
|
||||
# Create an HA Chassis Group register and assign to the LSP.
|
||||
hcg_name = uuidutils.generate_uuid()
|
||||
self.nbapi.ha_chassis_group_add(hcg_name).execute(check_error=True)
|
||||
hcg = self.nbapi.lookup('HA_Chassis_Group', hcg_name)
|
||||
self.nbapi.set_lswitch_port(
|
||||
lsp_name, ha_chassis_group=hcg.uuid).execute(check_error=True)
|
||||
lsp = self.nbapi.lookup('Logical_Switch_Port', lsp_name)
|
||||
self.assertEqual(hcg.uuid, lsp.ha_chassis_group[0].uuid)
|
||||
|
||||
# Unassign the HA Chassis Group from the LSP.
|
||||
self.nbapi.set_lswitch_port(
|
||||
lsp_name, ha_chassis_group=[]).execute(check_error=True)
|
||||
lsp = self.nbapi.lookup('Logical_Switch_Port', lsp_name)
|
||||
self.assertEqual([], lsp.ha_chassis_group)
|
||||
|
||||
|
||||
class TestIgnoreConnectionTimeout(BaseOvnIdlTest):
|
||||
@classmethod
|
||||
|
@ -74,7 +74,7 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
|
||||
return self.deserialize(self.fmt, res)['network']
|
||||
|
||||
def _create_port(self, name, net_id, security_groups=None,
|
||||
device_owner=None):
|
||||
device_owner=None, vnic_type=None):
|
||||
data = {'port': {'name': name,
|
||||
'network_id': net_id}}
|
||||
|
||||
@ -84,6 +84,9 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
|
||||
if device_owner is not None:
|
||||
data['port']['device_owner'] = device_owner
|
||||
|
||||
if vnic_type is not None:
|
||||
data['port']['binding:vnic_type'] = vnic_type
|
||||
|
||||
req = self.new_create_request('ports', data, self.fmt)
|
||||
res = req.get_response(self.api)
|
||||
return self.deserialize(self.fmt, res)['port']
|
||||
@ -949,6 +952,26 @@ class TestMaintenance(_TestMaintenanceHelper):
|
||||
# Assert load balancer for port forwarding is gone
|
||||
self.assertFalse(self._find_pf_lb(router_id, fip_id))
|
||||
|
||||
def test_check_for_ha_chassis_group(self):
|
||||
net1 = self._create_network('network1test', external=False)
|
||||
self._create_subnet('subnet1test', net1['id'])
|
||||
p1 = self._create_port('testp1', net1['id'], vnic_type='direct')
|
||||
|
||||
# Remove the HA Chassis Group register, created during the port
|
||||
# creation.
|
||||
self.nb_api.set_lswitch_port(p1['id'], ha_chassis_group=[]).execute(
|
||||
check_error=True)
|
||||
hcg_uuid = next(iter(self.nb_api._tables['HA_Chassis_Group'].rows))
|
||||
self.nb_api.ha_chassis_group_del(hcg_uuid).execute(check_error=True)
|
||||
lsp = self.nb_api.lookup('Logical_Switch_Port', p1['id'])
|
||||
self.assertEqual([], lsp.ha_chassis_group)
|
||||
|
||||
self.assertRaises(periodics.NeverAgain,
|
||||
self.maint.check_for_ha_chassis_group)
|
||||
hcg_uuid = next(iter(self.nb_api._tables['HA_Chassis_Group'].rows))
|
||||
lsp = self.nb_api.lookup('Logical_Switch_Port', p1['id'])
|
||||
self.assertEqual(hcg_uuid, lsp.ha_chassis_group[0].uuid)
|
||||
|
||||
|
||||
class TestLogMaintenance(_TestMaintenanceHelper,
|
||||
test_log_driver.LogApiTestCaseBase):
|
||||
|
@ -416,7 +416,8 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||
self.assertFalse(
|
||||
self.fake_ovn_client.sync_ha_chassis_group.called)
|
||||
|
||||
def test_check_for_ha_chassis_group(self):
|
||||
@mock.patch.object(utils, 'sync_ha_chassis_group')
|
||||
def test_check_for_ha_chassis_group(self, mock_sync_ha_chassis_group):
|
||||
self.fake_ovn_client.is_external_ports_supported.return_value = True
|
||||
nb_idl = self.fake_ovn_client._nb_idl
|
||||
|
||||
@ -440,7 +441,7 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||
constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'neutron-net1'}})
|
||||
|
||||
nb_idl.db_find_rows.return_value.execute.return_value = [p0, p1]
|
||||
self.fake_ovn_client.sync_ha_chassis_group.return_value = hcg0.uuid
|
||||
mock_sync_ha_chassis_group.return_value = hcg0.uuid
|
||||
|
||||
# Invoke the periodic method, it meant to run only once at startup
|
||||
# so NeverAgain will be raised at the end
|
||||
@ -449,16 +450,21 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||
|
||||
# Assert sync_ha_chassis_group() is called for both networks
|
||||
expected_calls = [
|
||||
mock.call(mock.ANY, 'net0', mock.ANY),
|
||||
mock.call(mock.ANY, 'net1', mock.ANY)]
|
||||
self.fake_ovn_client.sync_ha_chassis_group.assert_has_calls(
|
||||
expected_calls)
|
||||
mock.call(mock.ANY, 'net0',
|
||||
self.fake_ovn_client._nb_idl,
|
||||
self.fake_ovn_client._sb_idl, mock.ANY),
|
||||
mock.call(mock.ANY, 'net1',
|
||||
self.fake_ovn_client._nb_idl,
|
||||
self.fake_ovn_client._sb_idl, mock.ANY),
|
||||
]
|
||||
mock_sync_ha_chassis_group.assert_has_calls(expected_calls,
|
||||
any_order=True)
|
||||
|
||||
# Assert set_lswitch_port() is only called for p1 because
|
||||
# the ha_chassis_group is different than what was returned
|
||||
# by sync_ha_chassis_group()
|
||||
nb_idl.set_lswitch_port.assert_called_once_with(
|
||||
'p1', ha_chassis_group=hcg0.uuid)
|
||||
expected_calls = [
|
||||
mock.call('p0', ha_chassis_group=hcg0.uuid),
|
||||
mock.call('p1', ha_chassis_group=hcg0.uuid)]
|
||||
nb_idl.set_lswitch_port.assert_has_calls(expected_calls,
|
||||
any_order=True)
|
||||
|
||||
def test_check_port_has_address_scope(self):
|
||||
self.fake_ovn_client.is_external_ports_supported.return_value = True
|
||||
|
@ -2572,37 +2572,21 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
||||
self.sb_ovn.get_gateway_chassis_from_cms_options.return_value = [
|
||||
ch0, ch1, ch2, ch3, ch4]
|
||||
|
||||
fake_ha_ch = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||
attrs={'chassis_name': ch2.name, 'priority': 1})
|
||||
fake_ch_grp_uuid = 'fake-ha-ch-grp-uuid'
|
||||
fake_ch_grp = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||
attrs={'uuid': fake_ch_grp_uuid, 'ha_chassis': [fake_ha_ch]})
|
||||
|
||||
self.nb_ovn.ha_chassis_group_get.return_value.execute.return_value = (
|
||||
fake_ch_grp)
|
||||
|
||||
# Invoke the method
|
||||
ret = self.mech_driver._ovn_client.sync_ha_chassis_group(
|
||||
self.context, fake_net['id'], fake_txn)
|
||||
|
||||
# Assert the UUID of the HA Chassis Group is returned
|
||||
self.assertEqual(fake_ch_grp_uuid, ret)
|
||||
hcg_cmd = ovn_utils.sync_ha_chassis_group(
|
||||
self.context, fake_net['id'], self.nb_ovn, self.sb_ovn, fake_txn)
|
||||
|
||||
# Assert it attempts to add the chassis group for that network
|
||||
ha_ch_grp_name = ovn_utils.ovn_name(fake_net['id'])
|
||||
ext_ids = {ovn_const.OVN_AZ_HINTS_EXT_ID_KEY: 'az0,az1,az2'}
|
||||
self.nb_ovn.ha_chassis_group_add.assert_called_once_with(
|
||||
ha_ch_grp_name, may_exist=True)
|
||||
|
||||
# Assert existing members that no longer belong to those
|
||||
# AZs are removed
|
||||
self.nb_ovn.ha_chassis_group_del_chassis.assert_called_once_with(
|
||||
ha_ch_grp_name, ch2.name, if_exists=True)
|
||||
ha_ch_grp_name, may_exist=True, external_ids=ext_ids)
|
||||
|
||||
# Assert that only Chassis belonging to the AZ hints are
|
||||
# added to the HA Chassis Group for that network
|
||||
expected_calls = [
|
||||
mock.call(ha_ch_grp_name, ch0.name, priority=mock.ANY),
|
||||
mock.call(ha_ch_grp_name, ch1.name, priority=mock.ANY)]
|
||||
mock.call(hcg_cmd, ch0.name, priority=mock.ANY),
|
||||
mock.call(hcg_cmd, ch1.name, priority=mock.ANY)]
|
||||
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
|
||||
expected_calls, any_order=True)
|
||||
|
||||
@ -2624,36 +2608,21 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
||||
self.sb_ovn.get_gateway_chassis_from_cms_options.return_value = [
|
||||
ch0, ch1, ch2, ch3, ch4]
|
||||
|
||||
fake_ha_ch = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||
attrs={'chassis_name': ch1.name, 'priority': 1})
|
||||
fake_ch_grp_uuid = 'fake-ha-ch-grp-uuid'
|
||||
fake_ch_grp = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||
attrs={'uuid': fake_ch_grp_uuid, 'ha_chassis': [fake_ha_ch]})
|
||||
|
||||
self.nb_ovn.ha_chassis_group_get.return_value.execute.return_value = (
|
||||
fake_ch_grp)
|
||||
|
||||
# Invoke the method
|
||||
ret = self.mech_driver._ovn_client.sync_ha_chassis_group(
|
||||
self.context, fake_net['id'], fake_txn)
|
||||
|
||||
# Assert the UUID of the HA Chassis Group is returned
|
||||
self.assertEqual(fake_ch_grp_uuid, ret)
|
||||
hcg_cmd = ovn_utils.sync_ha_chassis_group(
|
||||
self.context, fake_net['id'], self.nb_ovn, self.sb_ovn, fake_txn)
|
||||
|
||||
# Assert it attempts to add the chassis group for that network
|
||||
ha_ch_grp_name = ovn_utils.ovn_name(fake_net['id'])
|
||||
ext_ids = {ovn_const.OVN_AZ_HINTS_EXT_ID_KEY: ''}
|
||||
self.nb_ovn.ha_chassis_group_add.assert_called_once_with(
|
||||
ha_ch_grp_name, may_exist=True)
|
||||
|
||||
# Assert existing members that does belong to any AZ are removed
|
||||
self.nb_ovn.ha_chassis_group_del_chassis.assert_called_once_with(
|
||||
ha_ch_grp_name, ch1.name, if_exists=True)
|
||||
ha_ch_grp_name, may_exist=True, external_ids=ext_ids)
|
||||
|
||||
# Assert that only Chassis that are gateways and DOES NOT
|
||||
# belong to any AZs are added
|
||||
expected_calls = [
|
||||
mock.call(ha_ch_grp_name, ch2.name, priority=mock.ANY),
|
||||
mock.call(ha_ch_grp_name, ch3.name, priority=mock.ANY)]
|
||||
mock.call(hcg_cmd, ch2.name, priority=mock.ANY),
|
||||
mock.call(hcg_cmd, ch3.name, priority=mock.ANY)]
|
||||
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
|
||||
expected_calls, any_order=True)
|
||||
|
||||
@ -3966,8 +3935,7 @@ class TestOVNMechanismDriverSecurityGroup(MechDriverSetupBase,
|
||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
||||
'ovn_client.OVNClient.is_external_ports_supported',
|
||||
lambda *_: True)
|
||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
||||
'ovn_client.OVNClient.sync_ha_chassis_group')
|
||||
@mock.patch.object(ovn_utils, 'sync_ha_chassis_group')
|
||||
def _test_create_port_with_vnic_type(self, vnic_type, sync_mock):
|
||||
fake_grp = 'fake-default-ha-group-uuid'
|
||||
sync_mock.return_value = fake_grp
|
||||
@ -3987,7 +3955,9 @@ class TestOVNMechanismDriverSecurityGroup(MechDriverSetupBase,
|
||||
if vnic_type in ovn_const.EXTERNAL_PORT_TYPES:
|
||||
self.assertEqual(ovn_const.LSP_TYPE_EXTERNAL, kwargs['type'])
|
||||
self.assertEqual(fake_grp, kwargs['ha_chassis_group'])
|
||||
sync_mock.assert_called_once_with(mock.ANY, net_id, mock.ANY)
|
||||
sync_mock.assert_called_once_with(
|
||||
mock.ANY, net_id, self.mech_driver.nb_ovn,
|
||||
self.mech_driver.sb_ovn, mock.ANY)
|
||||
|
||||
def test_create_port_with_vnic_direct(self):
|
||||
self._test_create_port_with_vnic_type(portbindings.VNIC_DIRECT)
|
||||
|
@ -50,7 +50,7 @@ osprofiler>=2.3.0 # Apache-2.0
|
||||
os-ken>=2.2.0 # Apache-2.0
|
||||
os-resource-classes>=1.1.0 # Apache-2.0
|
||||
ovs>=2.10.0 # Apache-2.0
|
||||
ovsdbapp>=1.16.0 # Apache-2.0
|
||||
ovsdbapp>=2.2.1 # Apache-2.0
|
||||
packaging>=20.4 # Apache-2.0
|
||||
psutil>=5.3.0 # BSD
|
||||
pyroute2>=0.7.3;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2)
|
||||
|
Loading…
Reference in New Issue
Block a user