Skip to content

Commit 3db4088

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Use the OVN policer for max-bw only QoS policies" into stable/2025.1
2 parents d7e79d1 + 63ba2c9 commit 3db4088

File tree

4 files changed

+132
-33
lines changed

4 files changed

+132
-33
lines changed

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,14 +348,25 @@ def _add_port_qos_rules(self, txn, port_id, network_id, network_type,
348348
_qos_rules = (copy.deepcopy(qos_rules) if qos_rules else
349349
self._qos_rules(admin_context, qos_policy_id))
350350
for direction, rules in _qos_rules.items():
351+
min_bw = rules.get(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH)
352+
# NOTE(ralonsoh): the QoS rules are defined in the LSP.options
353+
# dictionary if (1) direction=egress, (2) the network is physical
354+
# and (3) there are min-bw rules. Otherwise, the OVN QoS registers
355+
# are used (OVN BW policer).
351356
if (network_type in TYPE_PHYSICAL and
352357
direction == constants.EGRESS_DIRECTION):
353-
ovn_rule_lsp = self._ovn_lsp_rule(rules)
354-
self._update_lsp_qos_options(txn, lsp, port_id, ovn_rule_lsp)
355-
# In this particular case, the QoS rules should be defined in
356-
# LSP.options. Only DSCP rule will create a QoS entry.
357-
rules.pop(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, None)
358-
rules.pop(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH, None)
358+
if min_bw:
359+
ovn_rule_lsp = self._ovn_lsp_rule(rules)
360+
self._update_lsp_qos_options(txn, lsp, port_id,
361+
ovn_rule_lsp)
362+
# In this particular case, the QoS rules should be defined
363+
# in LSP.options. Only DSCP rule will create a QoS entry.
364+
rules.pop(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, None)
365+
rules.pop(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH, None)
366+
else:
367+
# Clear the LSP.options QoS rules.
368+
self._update_lsp_qos_options(txn, lsp, port_id,
369+
self._ovn_lsp_rule({}))
359370

360371
ovn_rule_qos = self._ovn_qos_rule(direction, rules, port_id,
361372
network_id)

neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/test_qos.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@
6969
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH: QOS_RULE_MINBW_1}
7070
}
7171

72+
QOS_RULES_5 = {
73+
constants.EGRESS_DIRECTION: {
74+
qos_constants.RULE_TYPE_BANDWIDTH_LIMIT: QOS_RULE_BW_1,
75+
qos_constants.RULE_TYPE_DSCP_MARKING: QOS_RULE_DSCP_1,
76+
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH: QOS_RULE_MINBW_1},
77+
}
78+
7279

7380
class _TestOVNClientQosExtensionBase(base.TestOVNFunctionalBase):
7481
def setUp(self, maintenance_worker=False):
@@ -78,7 +85,9 @@ def setUp(self, maintenance_worker=False):
7885
def _check_rules_qos(self, rules, port_id, network_id, network_type,
7986
fip_id=None, ip_address=None, expected_ext_ids=None):
8087
qos_rules = copy.deepcopy(rules)
81-
if network_type in (constants.TYPE_VLAN, constants.TYPE_FLAT):
88+
min_bw = qos_rules.get(constants.EGRESS_DIRECTION, {}).get(
89+
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)
90+
if network_type in constants.TYPE_PHYSICAL and min_bw:
8291
# Remove the egress max-rate and min-rate rules, these are defined
8392
# in the LSP.options field for a physical network.
8493
try:
@@ -135,12 +144,13 @@ def _check_rules_qos(self, rules, port_id, network_id, network_type,
135144
self.assertEqual(bandwidth, rule.bandwidth)
136145

137146
def _check_rules_lsp(self, rules, port_id, network_type):
138-
if network_type not in (constants.TYPE_VLAN, constants.TYPE_FLAT):
147+
egress_rules = rules.get(constants.EGRESS_DIRECTION, {})
148+
min_bw = egress_rules.get(qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)
149+
if not (network_type in constants.TYPE_PHYSICAL and min_bw):
139150
return
140151

141152
# If there are no egress rules, it is checked that there are no
142153
# QoS parameters in the LSP.options dictionary.
143-
egress_rules = rules.get(constants.EGRESS_DIRECTION, {})
144154
qos_rule_lsp = self.qos_driver._ovn_lsp_rule(egress_rules)
145155
lsp = self.qos_driver.nb_idl.lsp_get(port_id).execute(
146156
check_error=True)
@@ -267,6 +277,62 @@ def test_update_policy(self):
267277
constants.TYPE_VLAN)
268278
self._check_rules_lsp(_qos_rules, port['id'], constants.TYPE_VLAN)
269279

280+
def test_set_and_update_physical_network_qos(self):
281+
# The goal of this test is to check how the OVN QoS registers and
282+
# LSP.options are set and deleted, depending on the QoS policy rules.
283+
# Check LP#2115952 for more information.
284+
port = uuidutils.generate_uuid()
285+
self._add_logical_switch_port(port)
286+
287+
def _apply_rules(qos_rules):
288+
with self.nb_api.transaction(check_error=True) as txn:
289+
_qos_rules = copy.deepcopy(qos_rules)
290+
for direction in constants.VALID_DIRECTIONS:
291+
_qos_rules[direction] = _qos_rules.get(direction, {})
292+
self.mock_qos_rules.return_value = copy.deepcopy(_qos_rules)
293+
self.qos_driver._update_port_qos_rules(
294+
txn, port, self.network_1, constants.TYPE_VLAN, 'qos1',
295+
None)
296+
297+
# Loop this test twice, to check that all the QoS registers and
298+
# parameters are correctly created, set or removed, regardless of the
299+
# previous state.
300+
for _ in range(2):
301+
# Apply QOS_RULES_5: egress with max-bw, min-bw and DSCP rules.
302+
# * Check the OVN QoS rule created has only DSCP information.
303+
# * Check the LSP.options have the correct fields.
304+
_apply_rules(QOS_RULES_5)
305+
lsp = self.qos_driver.nb_idl.lsp_get(port).execute(
306+
check_error=True)
307+
for _param in ('qos_max_rate', 'qos_burst', 'qos_min_rate'):
308+
self.assertIn(_param, lsp.options)
309+
ls = self.qos_driver.nb_idl.lookup(
310+
'Logical_Switch', ovn_utils.ovn_name(self.network_1))
311+
self.assertEqual(1, len(ls.qos_rules))
312+
rule = ls.qos_rules[0]
313+
self.assertIn(port, rule.match)
314+
self.assertEqual({'dscp': QOS_RULE_DSCP_1['dscp_mark']},
315+
rule.action)
316+
self.assertEqual({}, rule.bandwidth)
317+
318+
# Apply QOS_RULES_3: egress with max-bw only rule.
319+
# * Check the OVN QoS rule created has only max-bw information.
320+
# * Check the LSP.options has no QoS information.
321+
_apply_rules(QOS_RULES_3)
322+
lsp = self.qos_driver.nb_idl.lsp_get(port).execute(
323+
check_error=True)
324+
for _param in ('qos_max_rate', 'qos_burst', 'qos_min_rate'):
325+
self.assertNotIn(_param, lsp.options)
326+
ls = self.qos_driver.nb_idl.lookup(
327+
'Logical_Switch', ovn_utils.ovn_name(self.network_1))
328+
self.assertEqual(1, len(ls.qos_rules))
329+
rule = ls.qos_rules[0]
330+
self.assertIn(port, rule.match)
331+
self.assertEqual({}, rule.action)
332+
self.assertEqual({'burst': QOS_RULE_BW_1['max_burst_kbps'],
333+
'rate': QOS_RULE_BW_1['max_kbps']},
334+
rule.bandwidth)
335+
270336

271337
class TestOVNClientQosExtensionEndToEnd(_TestOVNClientQosExtensionBase):
272338

neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -386,8 +386,13 @@ def test_create_router_port_multiple_routers(self):
386386
# GW network MTU=1400
387387
self.assertEqual(1400, int(lrp.options['gateway_mtu']))
388388

389-
def test_update_port_with_qos(self):
390-
def _check_bw(port_id, max_kbps=None, max_burst_kbps=None):
389+
def test_update_port_with_qos_in_lsp_options(self):
390+
# The goal of this test is to check that a Neutron port that has a
391+
# QoS policy defined in the LSP.options (physical network, egress
392+
# rules, max+min BW rules) is correctly updated and the LSP.options
393+
# are defined when the Neutron port is updated (see LP#2106231).
394+
def _check_bw(port_id, max_kbps=None, max_burst_kbps=None,
395+
min_kbps=None):
391396
lsp = self.nb_api.lookup('Logical_Switch_Port', port_id)
392397
if max_kbps:
393398
self.assertEqual(
@@ -403,37 +408,47 @@ def _check_bw(port_id, max_kbps=None, max_burst_kbps=None):
403408
else:
404409
self.assertNotIn(ovn_const.LSP_OPTIONS_QOS_BURST,
405410
lsp.options)
411+
if min_kbps:
412+
self.assertEqual(
413+
'{}'.format(min_kbps * 1000),
414+
lsp.options[ovn_const.LSP_OPTIONS_QOS_MIN_RATE])
415+
else:
416+
self.assertNotIn(ovn_const.LSP_OPTIONS_QOS_MIN_RATE,
417+
lsp.options)
406418

407419
res = self._create_qos_policy(self.fmt, is_admin=True)
408420
qos = self.deserialize(self.fmt, res)['policy']
409-
max_kbps, max_burst_kbps = 1000, 800
421+
max_kbps, max_burst_kbps, min_kbps = 1000, 800, 600
422+
410423
self._create_qos_rule(self.fmt, qos['id'],
411424
qos_const.RULE_TYPE_BANDWIDTH_LIMIT,
412425
max_kbps=max_kbps, max_burst_kbps=max_burst_kbps,
413426
is_admin=True)
427+
self._create_qos_rule(self.fmt, qos['id'],
428+
qos_const.RULE_TYPE_MINIMUM_BANDWIDTH,
429+
min_kbps=min_kbps, is_admin=True)
414430
net_args = {provider_net.NETWORK_TYPE: 'flat',
415431
provider_net.PHYSICAL_NETWORK: 'datacentre'}
416432
with self.network(uuidutils.generate_uuid(),
417433
arg_list=tuple(net_args.keys()), as_admin=True,
418434
**net_args) as net:
419-
with self.subnet(net) as subnet:
420-
with self.port(subnet) as port:
421-
port_data = port['port']
422-
# Check no QoS options.
423-
_check_bw(port_data['id'])
424-
425-
# Add QoS policy.
426-
req = self.new_update_request(
427-
'ports',
428-
{'port': {'qos_policy_id': qos['id']}},
429-
port_data['id'])
430-
req.get_response(self.api)
431-
_check_bw(port_data['id'], max_kbps, max_burst_kbps)
432-
433-
# Update port.
434-
req = self.new_update_request(
435-
'ports',
436-
{'port': {'name': uuidutils.generate_uuid()}},
437-
port_data['id'])
438-
req.get_response(self.api)
439-
_check_bw(port_data['id'], max_kbps, max_burst_kbps)
435+
with self.subnet(net) as subnet, self.port(subnet) as port:
436+
port_data = port['port']
437+
# Check no QoS options.
438+
_check_bw(port_data['id'])
439+
440+
# Add QoS policy.
441+
req = self.new_update_request(
442+
'ports',
443+
{'port': {'qos_policy_id': qos['id']}},
444+
port_data['id'])
445+
req.get_response(self.api)
446+
_check_bw(port_data['id'], max_kbps, max_burst_kbps, min_kbps)
447+
448+
# Update port.
449+
req = self.new_update_request(
450+
'ports',
451+
{'port': {'name': uuidutils.generate_uuid()}},
452+
port_data['id'])
453+
req.get_response(self.api)
454+
_check_bw(port_data['id'], max_kbps, max_burst_kbps, min_kbps)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
When using the ML2/OVN mechanism driver, the QoS policies with maximum
5+
bandwidth rules only are always enforced using the internal OVN policers,
6+
regardless of the direction and network type. It is not relevant if the
7+
QoS policy has or not DSCP rules.

0 commit comments

Comments
 (0)