Fullstack tests of packet rate limit for ovs qos driver

Depends-On: https://review.opendev.org/c/openstack/python-neutronclient/+/818717

Partially-Implements: bp/packet-rate-limit
Related-Bug: #1938966
Related-Bug: #1912460
Change-Id: If2d8f4e89987dcb55fcbef126a02bff4433c6aa8
This commit is contained in:
LIU Yulong 2021-11-22 16:08:02 +08:00
parent 5765186516
commit f1a082ce50
4 changed files with 161 additions and 0 deletions

View File

@ -18,6 +18,7 @@ import functools
import itertools
import operator
import random
import re
import time
import uuid
@ -82,6 +83,11 @@ CTRL_BURST_LIMIT_MIN = 25
TYPE_GRE_IP6 = 'ip6gre'
MAX_METER_REGEX = re.compile(r"max_meter:(\w+) max_bands:(\w+) *")
BAND_TYPES_REGEX = re.compile(r"band_types: (\w+)")
CAPS_REGEX = re.compile(r"capabilities: (\w+) (\w+) (\w+) (\w+)")
def _ovsdb_result_pending(result):
"""Return True if ovsdb indicates the result is still pending."""
# ovsdb can return '[]' for an ofport that has not yet been assigned
@ -443,6 +449,24 @@ class OVSBridge(BaseOVS):
def remove_all_flows(self):
self.run_ofctl("del-flows", [])
def list_meter_features(self):
# For fullstack test mainly
f_list = self.run_ofctl("meter-features", []).split("\n")[1:]
max_meter = max_bands = support_drop = support_caps = None
for output in f_list:
match = MAX_METER_REGEX.match(output)
if match:
max_meter = match.group(1)
max_bands = match.group(2)
match = BAND_TYPES_REGEX.match(output)
if match:
support_drop = match.group(1)
match = CAPS_REGEX.match(output)
if match:
support_caps = [match.group(1), match.group(2),
match.group(3), match.group(4)]
return all([max_meter, max_bands, support_drop, support_caps])
@_ovsdb_retry
def _get_port_val(self, port_name, port_val):
return self.db_get_val("Interface", port_name, port_val)

View File

@ -86,6 +86,29 @@ def wait_until_dscp_marking_rule_applied_ovs(bridge, port_vif, rule):
common_utils.wait_until_true(_dscp_marking_rule_applied)
def wait_until_pkt_meter_rule_applied_ovs(bridge, port_vif, port_id,
direction, mac=None):
def _pkt_rate_limit_rule_applied():
port_num = bridge.get_port_ofport(port_vif)
port_vlan = bridge.get_port_tag_by_name(port_vif)
key = "%s_%s" % (port_id, direction)
meter_id = bridge.get_value_from_other_config(
port_vif, key, value_type=int)
if direction == "egress":
flows = bridge.dump_flows_for(table='59', in_port=str(port_num),
dl_src=str(mac))
else:
flows = bridge.dump_flows_for(table='59', dl_vlan=str(port_vlan),
dl_dst=str(mac))
if mac:
return bool(flows) and meter_id
else:
return not bool(flows) and not meter_id
common_utils.wait_until_true(_pkt_rate_limit_rule_applied)
def wait_until_dscp_marking_rule_applied_linuxbridge(namespace, port_vif,
expected_rule):

View File

@ -283,6 +283,27 @@ class ClientFixture(fixtures.Fixture):
return rule['bandwidth_limit_rule']
def create_packet_rate_limit_rule(
self, project_id, qos_policy_id, limit=None,
burst=None, direction=None):
rule = {'project_id': project_id}
if limit:
rule['max_kpps'] = limit
if burst:
rule['max_burst_kpps'] = burst
if direction:
rule['direction'] = direction
rule = self.client.create_packet_rate_limit_rule(
policy=qos_policy_id,
body={'packet_rate_limit_rule': rule})
self.addCleanup(
_safe_method(self.client.delete_packet_rate_limit_rule),
rule['packet_rate_limit_rule']['id'],
qos_policy_id)
return rule['packet_rate_limit_rule']
def create_minimum_bandwidth_rule(self, tenant_id, qos_policy_id,
min_bw, direction=None):
rule = {'tenant_id': tenant_id,

View File

@ -46,6 +46,9 @@ BANDWIDTH_LIMIT = 500
MIN_BANDWIDTH = 300
DSCP_MARK = 16
PACKET_RATE_LIMIT = 10000
PACKET_RATE_BURST = 1000
class BaseQoSRuleTestCase(object):
number_of_hosts = 1
@ -547,6 +550,96 @@ class TestDscpMarkingQoSLinuxbridge(_TestDscpMarkingQoS,
vm.host.host_namespace, vm.port.name, dscp_mark)
class _TestPacketRateLimitQoS(BaseQoSRuleTestCase):
number_of_hosts = 1
def _wait_for_packet_rate_limit_rule_applied(self, vm, direction):
l2_extensions.wait_until_pkt_meter_rule_applied_ovs(
vm.bridge, vm.port.name, vm.neutron_port['id'],
direction, vm.mac_address)
def _wait_for_packet_rate_limit_rule_removed(self, vm, direction):
l2_extensions.wait_until_pkt_meter_rule_applied_ovs(
vm.bridge, vm.port.name, vm.neutron_port['id'], direction)
def _add_packet_rate_limit_rule(self, limit, burst, direction, qos_policy):
qos_policy_id = qos_policy['id']
rule = self.safe_client.create_packet_rate_limit_rule(
self.tenant_id, qos_policy_id, limit, burst, direction)
rule['type'] = qos_consts.RULE_TYPE_PACKET_RATE_LIMIT
rule['qos_policy_id'] = qos_policy_id
qos_policy['rules'].append(rule)
def _create_vm_with_limit_rules(self):
# Create port with qos policy attached, with different direction
vm, qos_policy = self._prepare_vm_with_qos_policy(
[functools.partial(
self._add_packet_rate_limit_rule,
PACKET_RATE_LIMIT, PACKET_RATE_BURST, self.direction),
functools.partial(
self._add_packet_rate_limit_rule,
PACKET_RATE_LIMIT, PACKET_RATE_BURST, self.reverse_direction)])
self._wait_for_packet_rate_limit_rule_applied(
vm, self.direction)
self._wait_for_packet_rate_limit_rule_applied(
vm, self.reverse_direction)
return vm, qos_policy
def test_packet_rate_limit_qos_policy_rule_lifecycle(self):
new_limit = PACKET_RATE_LIMIT + 100
# Create port with qos policy attached
vm, qos_policy = self._prepare_vm_with_qos_policy(
[functools.partial(
self._add_packet_rate_limit_rule,
PACKET_RATE_LIMIT, PACKET_RATE_BURST, self.direction)])
vm.bridge.use_at_least_protocol(ovs_constants.OPENFLOW13)
if not vm.bridge.list_meter_features():
self.skip("Test ovs bridge %s does not support meter.",
vm.bridge.br_name)
pkt_rule = qos_policy['rules'][0]
self._wait_for_packet_rate_limit_rule_applied(
vm, self.direction)
qos_policy_id = qos_policy['id']
self.client.delete_packet_rate_limit_rule(pkt_rule['id'],
qos_policy_id)
self._wait_for_packet_rate_limit_rule_removed(vm, self.direction)
new_rule = self.safe_client.create_packet_rate_limit_rule(
self.tenant_id, qos_policy_id, new_limit, direction=self.direction)
self._wait_for_packet_rate_limit_rule_applied(
vm, self.direction)
# Update qos policy rule id
self.client.update_packet_rate_limit_rule(
new_rule['id'], qos_policy_id,
body={'packet_rate_limit_rule': {
'max_kpps': PACKET_RATE_LIMIT,
'max_burst_kpps': PACKET_RATE_BURST}})
self._wait_for_packet_rate_limit_rule_applied(
vm, self.direction)
# Remove qos policy from port
self.client.update_port(
vm.neutron_port['id'],
body={'port': {'qos_policy_id': None}})
self._wait_for_packet_rate_limit_rule_removed(vm, self.direction)
class TestPacketRateLimitQoSOvs(_TestPacketRateLimitQoS,
base.BaseFullStackTestCase):
l2_agent_type = constants.AGENT_TYPE_OVS
scenarios = [
('ingress', {'direction': constants.INGRESS_DIRECTION}),
('egress', {'direction': constants.EGRESS_DIRECTION})
]
class TestQoSWithL2Population(base.BaseFullStackTestCase):
scenarios = [
(constants.AGENT_TYPE_OVS,