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 copy
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutron_lib.api.definitions import external_net
|
from neutron_lib.api.definitions import external_net
|
||||||
@ -34,6 +35,7 @@ from oslo_log import log
|
|||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import netutils
|
from oslo_utils import netutils
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
|
from ovsdbapp.backend.ovs_idl import rowview
|
||||||
from ovsdbapp import constants as ovsdbapp_const
|
from ovsdbapp import constants as ovsdbapp_const
|
||||||
import tenacity
|
import tenacity
|
||||||
|
|
||||||
@ -853,3 +855,76 @@ def get_ovn_chassis_other_config(chassis):
|
|||||||
return chassis.other_config
|
return chassis.other_config
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return chassis.external_ids
|
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 oslo_utils import timeutils
|
||||||
from ovsdbapp.backend.ovs_idl import command
|
from ovsdbapp.backend.ovs_idl import command
|
||||||
from ovsdbapp.backend.ovs_idl import idlutils
|
from ovsdbapp.backend.ovs_idl import idlutils
|
||||||
|
from ovsdbapp import utils as ovsdbapp_utils
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.common.ovn import constants as ovn_const
|
from neutron.common.ovn import constants as ovn_const
|
||||||
@ -137,6 +138,14 @@ class AddLSwitchPortCommand(command.BaseCommand):
|
|||||||
port.dhcpv6_options = dhcpv6_options
|
port.dhcpv6_options = dhcpv6_options
|
||||||
else:
|
else:
|
||||||
port.dhcpv6_options = [dhcpv6_options.result]
|
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():
|
for col, val in self.columns.items():
|
||||||
setattr(port, col, val)
|
setattr(port, col, val)
|
||||||
# add the newly created port to existing lswitch
|
# add the newly created port to existing lswitch
|
||||||
@ -202,6 +211,21 @@ class SetLSwitchPortCommand(command.BaseCommand):
|
|||||||
external_ids[k] = v
|
external_ids[k] = v
|
||||||
port.external_ids = external_ids
|
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():
|
for col, val in self.columns.items():
|
||||||
setattr(port, col, val)
|
setattr(port, col, val)
|
||||||
|
|
||||||
|
@ -603,15 +603,10 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
|||||||
network_id = port.external_ids[
|
network_id = port.external_ids[
|
||||||
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY].replace(
|
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY].replace(
|
||||||
ovn_const.OVN_NAME_PREFIX, '')
|
ovn_const.OVN_NAME_PREFIX, '')
|
||||||
ha_ch_grp = self._ovn_client.sync_ha_chassis_group(
|
ha_ch_grp = utils.sync_ha_chassis_group(
|
||||||
context, network_id, txn)
|
context, network_id, self._nb_idl, self._sb_idl, txn)
|
||||||
try:
|
txn.add(self._nb_idl.set_lswitch_port(
|
||||||
port_ha_ch_uuid = port.ha_chassis_group[0].uuid
|
port.name, ha_chassis_group=ha_ch_grp))
|
||||||
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))
|
|
||||||
|
|
||||||
self._delete_default_ha_chassis_group(txn)
|
self._delete_default_ha_chassis_group(txn)
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import random
|
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutron_lib.api.definitions import l3
|
from neutron_lib.api.definitions import l3
|
||||||
@ -447,76 +446,6 @@ class OVNClient(object):
|
|||||||
bp_info.vnic_type, bp_info.capabilities
|
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):
|
def update_port_dhcp_options(self, port_info, txn):
|
||||||
dhcpv4_options = []
|
dhcpv4_options = []
|
||||||
dhcpv6_options = []
|
dhcpv6_options = []
|
||||||
@ -598,9 +527,9 @@ class OVNClient(object):
|
|||||||
|
|
||||||
if (self.is_external_ports_supported() and
|
if (self.is_external_ports_supported() and
|
||||||
port_info.type == ovn_const.LSP_TYPE_EXTERNAL):
|
port_info.type == ovn_const.LSP_TYPE_EXTERNAL):
|
||||||
kwargs['ha_chassis_group'] = (
|
kwargs['ha_chassis_group'] = utils.sync_ha_chassis_group(
|
||||||
self.sync_ha_chassis_group(
|
context, port['network_id'], self._nb_idl, self._sb_idl,
|
||||||
context, port['network_id'], txn))
|
txn)
|
||||||
|
|
||||||
# NOTE(mjozefcz): Do not set addresses if the port is not
|
# NOTE(mjozefcz): Do not set addresses if the port is not
|
||||||
# bound, has no device_owner and it is OVN LB VIP port.
|
# 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 self.is_external_ports_supported():
|
||||||
if port_info.type == ovn_const.LSP_TYPE_EXTERNAL:
|
if port_info.type == ovn_const.LSP_TYPE_EXTERNAL:
|
||||||
columns_dict['ha_chassis_group'] = (
|
columns_dict['ha_chassis_group'] = (
|
||||||
self.sync_ha_chassis_group(
|
utils.sync_ha_chassis_group(
|
||||||
context, port['network_id'], txn))
|
context, port['network_id'], self._nb_idl,
|
||||||
|
self._sb_idl, txn))
|
||||||
else:
|
else:
|
||||||
# Clear the ha_chassis_group field
|
# Clear the ha_chassis_group field
|
||||||
columns_dict['ha_chassis_group'] = []
|
columns_dict['ha_chassis_group'] = []
|
||||||
@ -2068,7 +1998,9 @@ class OVNClient(object):
|
|||||||
neutron_net_azs = lswitch_params['external_ids'].get(
|
neutron_net_azs = lswitch_params['external_ids'].get(
|
||||||
ovn_const.OVN_AZ_HINTS_EXT_ID_KEY, '')
|
ovn_const.OVN_AZ_HINTS_EXT_ID_KEY, '')
|
||||||
if ovn_ls_azs != neutron_net_azs:
|
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
|
# Update the segment tags, if any
|
||||||
segments = segments_db.get_network_segments(context, network['id'])
|
segments = segments_db.get_network_segments(context, network['id'])
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# 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 constants as ovn_const
|
||||||
from neutron.common.ovn import utils
|
from neutron.common.ovn import utils
|
||||||
from neutron.tests.functional import base
|
from neutron.tests.functional import base
|
||||||
@ -58,3 +62,56 @@ class TestCreateNeutronPgDrop(base.TestOVNFunctionalBase):
|
|||||||
self.assertEqual(directions[0], acl2.direction)
|
self.assertEqual(directions[0], acl2.direction)
|
||||||
self.assertEqual('drop', acl2.action)
|
self.assertEqual('drop', acl2.action)
|
||||||
self.assertEqual(matches[0], acl2.match)
|
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 copy
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from oslo_utils import uuidutils
|
||||||
from ovsdbapp.backend.ovs_idl import connection
|
from ovsdbapp.backend.ovs_idl import connection
|
||||||
from ovsdbapp import constants as const
|
from ovsdbapp import constants as const
|
||||||
from ovsdbapp import event as ovsdb_event
|
from ovsdbapp import event as ovsdb_event
|
||||||
@ -269,6 +270,43 @@ class TestNbApi(BaseOvnIdlTest):
|
|||||||
self.assertIn((lb_match['name'], lb_match['external_ids']),
|
self.assertIn((lb_match['name'], lb_match['external_ids']),
|
||||||
exp_values)
|
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):
|
class TestIgnoreConnectionTimeout(BaseOvnIdlTest):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -74,7 +74,7 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
|
|||||||
return self.deserialize(self.fmt, res)['network']
|
return self.deserialize(self.fmt, res)['network']
|
||||||
|
|
||||||
def _create_port(self, name, net_id, security_groups=None,
|
def _create_port(self, name, net_id, security_groups=None,
|
||||||
device_owner=None):
|
device_owner=None, vnic_type=None):
|
||||||
data = {'port': {'name': name,
|
data = {'port': {'name': name,
|
||||||
'network_id': net_id}}
|
'network_id': net_id}}
|
||||||
|
|
||||||
@ -84,6 +84,9 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
|
|||||||
if device_owner is not None:
|
if device_owner is not None:
|
||||||
data['port']['device_owner'] = device_owner
|
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)
|
req = self.new_create_request('ports', data, self.fmt)
|
||||||
res = req.get_response(self.api)
|
res = req.get_response(self.api)
|
||||||
return self.deserialize(self.fmt, res)['port']
|
return self.deserialize(self.fmt, res)['port']
|
||||||
@ -949,6 +952,26 @@ class TestMaintenance(_TestMaintenanceHelper):
|
|||||||
# Assert load balancer for port forwarding is gone
|
# Assert load balancer for port forwarding is gone
|
||||||
self.assertFalse(self._find_pf_lb(router_id, fip_id))
|
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,
|
class TestLogMaintenance(_TestMaintenanceHelper,
|
||||||
test_log_driver.LogApiTestCaseBase):
|
test_log_driver.LogApiTestCaseBase):
|
||||||
|
@ -416,7 +416,8 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
|||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
self.fake_ovn_client.sync_ha_chassis_group.called)
|
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
|
self.fake_ovn_client.is_external_ports_supported.return_value = True
|
||||||
nb_idl = self.fake_ovn_client._nb_idl
|
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'}})
|
constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'neutron-net1'}})
|
||||||
|
|
||||||
nb_idl.db_find_rows.return_value.execute.return_value = [p0, p1]
|
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
|
# Invoke the periodic method, it meant to run only once at startup
|
||||||
# so NeverAgain will be raised at the end
|
# 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
|
# Assert sync_ha_chassis_group() is called for both networks
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call(mock.ANY, 'net0', mock.ANY),
|
mock.call(mock.ANY, 'net0',
|
||||||
mock.call(mock.ANY, 'net1', mock.ANY)]
|
self.fake_ovn_client._nb_idl,
|
||||||
self.fake_ovn_client.sync_ha_chassis_group.assert_has_calls(
|
self.fake_ovn_client._sb_idl, mock.ANY),
|
||||||
expected_calls)
|
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
|
expected_calls = [
|
||||||
# the ha_chassis_group is different than what was returned
|
mock.call('p0', ha_chassis_group=hcg0.uuid),
|
||||||
# by sync_ha_chassis_group()
|
mock.call('p1', ha_chassis_group=hcg0.uuid)]
|
||||||
nb_idl.set_lswitch_port.assert_called_once_with(
|
nb_idl.set_lswitch_port.assert_has_calls(expected_calls,
|
||||||
'p1', ha_chassis_group=hcg0.uuid)
|
any_order=True)
|
||||||
|
|
||||||
def test_check_port_has_address_scope(self):
|
def test_check_port_has_address_scope(self):
|
||||||
self.fake_ovn_client.is_external_ports_supported.return_value = True
|
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 = [
|
self.sb_ovn.get_gateway_chassis_from_cms_options.return_value = [
|
||||||
ch0, ch1, ch2, ch3, ch4]
|
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
|
# Invoke the method
|
||||||
ret = self.mech_driver._ovn_client.sync_ha_chassis_group(
|
hcg_cmd = ovn_utils.sync_ha_chassis_group(
|
||||||
self.context, fake_net['id'], fake_txn)
|
self.context, fake_net['id'], self.nb_ovn, self.sb_ovn, fake_txn)
|
||||||
|
|
||||||
# Assert the UUID of the HA Chassis Group is returned
|
|
||||||
self.assertEqual(fake_ch_grp_uuid, ret)
|
|
||||||
|
|
||||||
# Assert it attempts to add the chassis group for that network
|
# Assert it attempts to add the chassis group for that network
|
||||||
ha_ch_grp_name = ovn_utils.ovn_name(fake_net['id'])
|
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(
|
self.nb_ovn.ha_chassis_group_add.assert_called_once_with(
|
||||||
ha_ch_grp_name, may_exist=True)
|
ha_ch_grp_name, may_exist=True, external_ids=ext_ids)
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Assert that only Chassis belonging to the AZ hints are
|
# Assert that only Chassis belonging to the AZ hints are
|
||||||
# added to the HA Chassis Group for that network
|
# added to the HA Chassis Group for that network
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call(ha_ch_grp_name, ch0.name, priority=mock.ANY),
|
mock.call(hcg_cmd, ch0.name, priority=mock.ANY),
|
||||||
mock.call(ha_ch_grp_name, ch1.name, priority=mock.ANY)]
|
mock.call(hcg_cmd, ch1.name, priority=mock.ANY)]
|
||||||
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
|
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
|
||||||
expected_calls, any_order=True)
|
expected_calls, any_order=True)
|
||||||
|
|
||||||
@ -2624,36 +2608,21 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||||||
self.sb_ovn.get_gateway_chassis_from_cms_options.return_value = [
|
self.sb_ovn.get_gateway_chassis_from_cms_options.return_value = [
|
||||||
ch0, ch1, ch2, ch3, ch4]
|
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
|
# Invoke the method
|
||||||
ret = self.mech_driver._ovn_client.sync_ha_chassis_group(
|
hcg_cmd = ovn_utils.sync_ha_chassis_group(
|
||||||
self.context, fake_net['id'], fake_txn)
|
self.context, fake_net['id'], self.nb_ovn, self.sb_ovn, fake_txn)
|
||||||
|
|
||||||
# Assert the UUID of the HA Chassis Group is returned
|
|
||||||
self.assertEqual(fake_ch_grp_uuid, ret)
|
|
||||||
|
|
||||||
# Assert it attempts to add the chassis group for that network
|
# Assert it attempts to add the chassis group for that network
|
||||||
ha_ch_grp_name = ovn_utils.ovn_name(fake_net['id'])
|
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(
|
self.nb_ovn.ha_chassis_group_add.assert_called_once_with(
|
||||||
ha_ch_grp_name, may_exist=True)
|
ha_ch_grp_name, may_exist=True, external_ids=ext_ids)
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Assert that only Chassis that are gateways and DOES NOT
|
# Assert that only Chassis that are gateways and DOES NOT
|
||||||
# belong to any AZs are added
|
# belong to any AZs are added
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call(ha_ch_grp_name, ch2.name, priority=mock.ANY),
|
mock.call(hcg_cmd, ch2.name, priority=mock.ANY),
|
||||||
mock.call(ha_ch_grp_name, ch3.name, priority=mock.ANY)]
|
mock.call(hcg_cmd, ch3.name, priority=mock.ANY)]
|
||||||
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
|
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
|
||||||
expected_calls, any_order=True)
|
expected_calls, any_order=True)
|
||||||
|
|
||||||
@ -3966,8 +3935,7 @@ class TestOVNMechanismDriverSecurityGroup(MechDriverSetupBase,
|
|||||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
||||||
'ovn_client.OVNClient.is_external_ports_supported',
|
'ovn_client.OVNClient.is_external_ports_supported',
|
||||||
lambda *_: True)
|
lambda *_: True)
|
||||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
@mock.patch.object(ovn_utils, 'sync_ha_chassis_group')
|
||||||
'ovn_client.OVNClient.sync_ha_chassis_group')
|
|
||||||
def _test_create_port_with_vnic_type(self, vnic_type, sync_mock):
|
def _test_create_port_with_vnic_type(self, vnic_type, sync_mock):
|
||||||
fake_grp = 'fake-default-ha-group-uuid'
|
fake_grp = 'fake-default-ha-group-uuid'
|
||||||
sync_mock.return_value = fake_grp
|
sync_mock.return_value = fake_grp
|
||||||
@ -3987,7 +3955,9 @@ class TestOVNMechanismDriverSecurityGroup(MechDriverSetupBase,
|
|||||||
if vnic_type in ovn_const.EXTERNAL_PORT_TYPES:
|
if vnic_type in ovn_const.EXTERNAL_PORT_TYPES:
|
||||||
self.assertEqual(ovn_const.LSP_TYPE_EXTERNAL, kwargs['type'])
|
self.assertEqual(ovn_const.LSP_TYPE_EXTERNAL, kwargs['type'])
|
||||||
self.assertEqual(fake_grp, kwargs['ha_chassis_group'])
|
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):
|
def test_create_port_with_vnic_direct(self):
|
||||||
self._test_create_port_with_vnic_type(portbindings.VNIC_DIRECT)
|
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-ken>=2.2.0 # Apache-2.0
|
||||||
os-resource-classes>=1.1.0 # Apache-2.0
|
os-resource-classes>=1.1.0 # Apache-2.0
|
||||||
ovs>=2.10.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
|
packaging>=20.4 # Apache-2.0
|
||||||
psutil>=5.3.0 # BSD
|
psutil>=5.3.0 # BSD
|
||||||
pyroute2>=0.7.3;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2)
|
pyroute2>=0.7.3;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2)
|
||||||
|
Loading…
Reference in New Issue
Block a user