[OVN][QoS] Add external_ids reference to port QoS registers

Same as with floating IP QoS OVN registers, port QoSes should have a
"external_ids" reference to the port ID this QoS is attached to.

Related-Bug: #1947334

Conflicts:
    neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py
    neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py

Change-Id: If14ccff79f77618133cce4bf95e3f1eea7ba8cf3
(cherry picked from commit ce7f279538)
This commit is contained in:
Rodolfo Alonso Hernandez 2021-12-21 09:32:36 +00:00
parent ff0eab46b4
commit 92e8a6a378
5 changed files with 83 additions and 12 deletions

View File

@ -21,6 +21,7 @@ OVN_ML2_MECH_DRIVER_NAME = 'ovn'
OVN_NETWORK_NAME_EXT_ID_KEY = 'neutron:network_name'
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'
OVN_PORT_FIP_EXT_ID_KEY = 'neutron:port_fip'
OVN_ROUTER_NAME_EXT_ID_KEY = 'neutron:router_name'
OVN_AZ_HINTS_EXT_ID_KEY = 'neutron:availability_zone_hints'

View File

@ -150,14 +150,19 @@ class OVNClientQosExtension(object):
ovn_qos_rule = {'switch': lswitch_name, 'direction': direction,
'priority': OVN_QOS_DEFAULT_RULE_PRIORITY,
'match': match}
if fip_id:
ovn_qos_rule['external_ids'] = {
ovn_const.OVN_FIP_EXT_ID_KEY: fip_id}
if delete:
# Any specific rule parameter is left undefined.
return ovn_qos_rule
# All OVN QoS rules have an external ID reference to the port or the
# FIP that are attached to.
if fip_id:
key, value = ovn_const.OVN_FIP_EXT_ID_KEY, fip_id
else:
key, value = ovn_const.OVN_PORT_EXT_ID_KEY, port_id
ovn_qos_rule['external_ids'] = {key: value}
for rule_type, rule in rules.items():
if rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT:
ovn_qos_rule['rate'] = rule['max_kbps']

View File

@ -16,6 +16,7 @@
import abc
import copy
import inspect
import re
import threading
from futurist import periodics
@ -751,6 +752,41 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
txn.add(cmd)
raise periodics.NeverAgain()
# TODO(ralonsoh): Remove this in the Z+2 cycle
# A static spacing value is used here, but this method will only run
# once per lock due to the use of periodics.NeverAgain().
@periodics.periodic(spacing=600, run_immediately=True)
def update_port_qos_with_external_ids_reference(self):
"""Update all OVN QoS registers with the port ID
This method will only update the OVN QoS registers related to port QoS,
not FIP QoS. FIP QoS have the corresponding "external_ids" reference.
"""
if not self.has_lock:
return
regex = re.compile(
r'(inport|outport) == \"(?P<port_id>[a-z0-9\-]{36})\"')
cmds = []
for ls in self._nb_idl.ls_list().execute(check_error=True):
for qos in self._nb_idl.qos_list(ls.name).execute(
check_error=True):
if qos.external_ids:
continue
match = re.match(regex, qos.match)
if not match:
continue
port_id = match.group('port_id')
external_ids = {ovn_const.OVN_PORT_EXT_ID_KEY: port_id}
cmds.append(self._nb_idl.db_set(
'QoS', qos.uuid, ('external_ids', external_ids)))
if cmds:
with self._nb_idl.transaction(check_error=True) as txn:
for cmd in cmds:
txn.add(cmd)
raise periodics.NeverAgain()
class HashRingHealthCheckPeriodics(object):

View File

@ -188,6 +188,10 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
self.qos_driver._qos_rules(mock.ANY, mock.ANY))
def _test__ovn_qos_rule_ingress(self, fip_id=None, ip_address=None):
if fip_id:
external_ids = {ovn_const.OVN_FIP_EXT_ID_KEY: fip_id}
else:
external_ids = {ovn_const.OVN_PORT_EXT_ID_KEY: 'port_id'}
direction = constants.INGRESS_DIRECTION
rule = {qos_constants.RULE_TYPE_BANDWIDTH_LIMIT: QOS_RULE_BW_1}
match = self.qos_driver._ovn_qos_rule_match(
@ -195,9 +199,8 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
expected = {'burst': 100, 'rate': 200, 'direction': 'to-lport',
'match': match,
'priority': qos_extension.OVN_QOS_DEFAULT_RULE_PRIORITY,
'switch': 'neutron-network_id'}
if fip_id:
expected['external_ids'] = {ovn_const.OVN_FIP_EXT_ID_KEY: fip_id}
'switch': 'neutron-network_id',
'external_ids': external_ids}
result = self.qos_driver._ovn_qos_rule(
direction, rule, 'port_id', 'network_id', fip_id=fip_id,
ip_address=ip_address, resident_port='resident_port')
@ -210,15 +213,18 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
self._test__ovn_qos_rule_ingress(fip_id='fipid', ip_address='1.2.3.4')
def _test__ovn_qos_rule_egress(self, fip_id=None, ip_address=None):
if fip_id:
external_ids = {ovn_const.OVN_FIP_EXT_ID_KEY: fip_id}
else:
external_ids = {ovn_const.OVN_PORT_EXT_ID_KEY: 'port_id'}
direction = constants.EGRESS_DIRECTION
rule = {qos_constants.RULE_TYPE_DSCP_MARKING: QOS_RULE_DSCP_1}
match = self.qos_driver._ovn_qos_rule_match(
direction, 'port_id', ip_address, 'resident_port')
expected = {'direction': 'from-lport', 'match': match,
'dscp': 16, 'switch': 'neutron-network_id',
'priority': qos_extension.OVN_QOS_DEFAULT_RULE_PRIORITY}
if fip_id:
expected['external_ids'] = {ovn_const.OVN_FIP_EXT_ID_KEY: fip_id}
'priority': qos_extension.OVN_QOS_DEFAULT_RULE_PRIORITY,
'external_ids': external_ids}
result = self.qos_driver._ovn_qos_rule(
direction, rule, 'port_id', 'network_id', fip_id=fip_id,
ip_address=ip_address, resident_port='resident_port')
@ -228,9 +234,8 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
qos_constants.RULE_TYPE_DSCP_MARKING: QOS_RULE_DSCP_2}
expected = {'direction': 'from-lport', 'match': match,
'rate': 300, 'dscp': 20, 'switch': 'neutron-network_id',
'priority': qos_extension.OVN_QOS_DEFAULT_RULE_PRIORITY}
if fip_id:
expected['external_ids'] = {ovn_const.OVN_FIP_EXT_ID_KEY: fip_id}
'priority': qos_extension.OVN_QOS_DEFAULT_RULE_PRIORITY,
'external_ids': external_ids}
result = self.qos_driver._ovn_qos_rule(
direction, rule, 'port_id', 'network_id', fip_id=fip_id,
ip_address=ip_address, resident_port='resident_port')

View File

@ -19,6 +19,7 @@ from futurist import periodics
from neutron_lib import context
from neutron_lib.db import api as db_api
from oslo_config import cfg
from oslo_utils import uuidutils
from neutron.common.ovn import constants
from neutron.common.ovn import utils
@ -535,3 +536,26 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
mock.call('Logical_Router_Port', 'lrp-port1', ('options', opt))]
self.fake_ovn_client._nb_idl.db_set.assert_has_calls(
expected_calls)
def test_update_port_qos_with_external_ids_reference(self):
nb_idl = self.fake_ovn_client._nb_idl
lrs = [fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lr%s' % idx}) for idx in range(3)]
uuid1 = uuidutils.generate_uuid()
qoses1 = [fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'external_ids': {}, 'match': 'inport == "%s"' % uuid1})]
qoses2 = [fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'external_ids': {constants.OVN_PORT_EXT_ID_KEY: uuid1},
'match': 'inport == "%s"' % uuid1})]
qoses3 = []
nb_idl.ls_list.return_value.execute.return_value = lrs
nb_idl.qos_list.return_value.execute.side_effect = [qoses1, qoses2,
qoses3]
self.assertRaises(
periodics.NeverAgain,
self.periodic.update_port_qos_with_external_ids_reference)
external_ids = {constants.OVN_PORT_EXT_ID_KEY: uuid1}
expected_calls = [mock.call('QoS', qoses1[0].uuid,
('external_ids', external_ids))]
nb_idl.db_set.assert_has_calls(expected_calls)