diff --git a/neutron/tests/common/agents/l2_extensions.py b/neutron/tests/common/agents/l2_extensions.py index 85188e619f8..158daaa8971 100644 --- a/neutron/tests/common/agents/l2_extensions.py +++ b/neutron/tests/common/agents/l2_extensions.py @@ -13,9 +13,19 @@ # License for the specific language governing permissions and limitations # under the License. +import re + +from neutron.agent.linux import async_process from neutron.common import utils as common_utils +IPv4_ADDR_REGEX = r"(\d{1,3}\.){3}\d{1,3}" + + +class TcpdumpException(Exception): + pass + + def extract_mod_nw_tos_action(flows): tos_mark = None if flows: @@ -52,3 +62,32 @@ def wait_until_dscp_marking_rule_applied(bridge, port_vif, rule): return dscp_mark == expected common_utils.wait_until_true(_dscp_marking_rule_applied) + + +def wait_for_dscp_marked_packet(sender_vm, receiver_vm, dscp_mark): + cmd = ["tcpdump", "-i", receiver_vm.port.name, "-nlt"] + if dscp_mark: + cmd += ["(ip[1] & 0xfc == %s)" % (dscp_mark << 2)] + tcpdump_async = async_process.AsyncProcess(cmd, run_as_root=True, + namespace=receiver_vm.namespace) + tcpdump_async.start() + sender_vm.block_until_ping(receiver_vm.ip) + try: + tcpdump_async.stop() + except async_process.AsyncProcessException: + # If it was already stopped than we don't care about it + pass + + pattern = (r"IP (?P%(ip_addr_regex)s) > " + "(?P%(ip_addr_regex)s): ICMP .*$" % { + 'ip_addr_regex': IPv4_ADDR_REGEX}) + for line in tcpdump_async.iter_stdout(): + m = re.match(pattern, line) + if m and (m.group("src_ip") == sender_vm.ip and + m.group("dst_ip") == receiver_vm.ip): + return + raise TcpdumpException( + "No packets marked with DSCP = %(dscp_mark)s received from %(src)s " + "to %(dst)s" % {'dscp_mark': dscp_mark, + 'src': sender_vm.ip, + 'dst': receiver_vm.ip}) diff --git a/neutron/tests/fullstack/test_qos.py b/neutron/tests/fullstack/test_qos.py index 422ece386b0..58dd4286746 100644 --- a/neutron/tests/fullstack/test_qos.py +++ b/neutron/tests/fullstack/test_qos.py @@ -176,6 +176,29 @@ class TestDscpMarkingQoSOvs(BaseQoSRuleTestCase, base.BaseFullStackTestCase): scenarios = fullstack_utils.get_ovs_interface_scenarios() l2_agent_type = constants.AGENT_TYPE_OVS + def setUp(self): + host_desc = [ + environment.HostDescription( + l3_agent=False, + of_interface=self.of_interface, + ovsdb_interface=self.ovsdb_interface, + l2_agent_type=self.l2_agent_type + ) for _ in range(2)] + env_desc = environment.EnvironmentDescription( + qos=True) + env = environment.Environment(env_desc, host_desc) + super(BaseQoSRuleTestCase, self).setUp(env) + + self.tenant_id = uuidutils.generate_uuid() + self.network = self.safe_client.create_network(self.tenant_id, + 'network-test') + self.subnet = self.safe_client.create_subnet( + self.tenant_id, self.network['id'], + cidr='10.0.0.0/24', + gateway_ip='10.0.0.1', + name='subnet-test', + enable_dhcp=False) + def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark): l2_extensions.wait_until_dscp_marking_rule_applied( vm.bridge, vm.port.name, dscp_mark) @@ -223,6 +246,31 @@ class TestDscpMarkingQoSOvs(BaseQoSRuleTestCase, base.BaseFullStackTestCase): body={'port': {'qos_policy_id': None}}) self._wait_for_dscp_marking_rule_removed(vm) + def test_dscp_marking_packets(self): + # Create port (vm) which will be used to received and test packets + receiver_port = self.safe_client.create_port( + self.tenant_id, self.network['id'], + self.environment.hosts[1].hostname) + + receiver = self.useFixture( + machine.FakeFullstackMachine( + self.environment.hosts[1], + self.network['id'], + self.tenant_id, + self.safe_client, + neutron_port=receiver_port)) + + # Create port with qos policy attached + sender, qos_policy = self._prepare_vm_with_qos_policy( + [functools.partial(self._add_dscp_rule, DSCP_MARK)]) + + sender.block_until_boot() + receiver.block_until_boot() + + self._wait_for_dscp_marking_rule_applied(sender, DSCP_MARK) + l2_extensions.wait_for_dscp_marked_packet( + sender, receiver, DSCP_MARK) + class TestQoSWithL2Population(base.BaseFullStackTestCase):