Merge "Improve "sync_ha_chassis_group" method"

This commit is contained in:
Zuul 2023-05-30 10:07:42 +00:00 committed by Gerrit Code Review
commit 940a1b6cea
10 changed files with 265 additions and 145 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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'])

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -2573,37 +2573,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)
@ -2625,36 +2609,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)
@ -3967,8 +3936,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
@ -3988,7 +3956,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)

View File

@ -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)