Esempio n. 1
0
def setup():
    check_sysfs_bond_permission()
    if running_on_travis_ci():
        pytest.skip(
            'The sysfs access is not working inside container without '
            'proper bonding module loading'
        )
Esempio n. 2
0
def xfail_when_running_on_travis_with_centos():
    try:
        yield
    except AssertionError:
        if running_on_travis_ci() and running_on_centos():
            pytest.xfail('Unable to run OVS tests on travis-ci')
        else:
            raise
Esempio n. 3
0
IPV6_ADDR = '2607:f0d0:1002:51::4'
IPV6_ADDR_BASE = '2001:1:1:1'
IPV6_ADDR1 = f'{IPV6_ADDR_BASE}::1'
IPV6_NET_ADDR = f'{IPV6_ADDR_BASE}::'
IPV6_PREFIX_LENGTH = 64

IPV4_ADDR1_CIDR = f'{IPV4_ADDR1}/{IPV4_PREFIX_LENGTH}'
IPV4_ADDR2_CIDR = f'{IPV4_ADDR2}/{IPV4_PREFIX_LENGTH}'
IPV4_ADDR3_CIDR = f'{IPV4_ADDR3}/{32}'
IPV6_ADDR_CIDR = f'{IPV6_ADDR}/{IPV6_PREFIX_LENGTH}'

# speeds defined in ethtool
ETHTOOL_SPEEDS = set([10, 100, 1000, 2500, 10000])

ipv6_broken_on_travis_ci = pytest.mark.skipif(
    running_on_travis_ci(), reason='IPv6 not supported on travis')


@pytest.fixture
def nic0():
    with dummy_device() as nic:
        yield nic


@pytest.fixture
def dynamic_ipv6_iface():
    if running_on_ovirt_ci():
        pytest.skip('Using dnsmasq for ipv6 RA is unstable on CI')

    with veth_pair() as (server, client):
        with wait_for_ipv6(server, IPV6_ADDR1, IPV6_PREFIX_LENGTH):
Esempio n. 4
0
class TestConfigureOutbound(object):
    # TODO:
    # test remove_outbound

    def test_single_non_vlan(self, dummy):
        qos.configure_outbound(HOST_QOS_OUTBOUND, dummy, None)
        tc_entities = self._analyse_qos_and_general_assertions(dummy)
        tc_classes, tc_filters, tc_qdiscs = tc_entities
        assert tc_classes.classes == []

        assert len(tc_qdiscs.leaf_qdiscs) == 1
        assert self._non_vlan_qdisc(tc_qdiscs.leaf_qdiscs) is not None
        self._assert_parent(tc_qdiscs.leaf_qdiscs, tc_classes.default_class)

        assert len(tc_filters.tagged_filters) == 0

    @pytest.mark.xfail(
        condition=running_on_ovirt_ci() or running_on_travis_ci(),
        reason='does not work on CI with nmstate',
        strict=False,
    )
    @pytest.mark.parametrize('repeating_calls', [1, 2])
    @mock.patch('vdsm.network.netinfo.bonding.permanent_address', lambda: {})
    def test_single_vlan(self, dummy, vlan16, repeating_calls):
        for _ in range(repeating_calls):
            qos.configure_outbound(HOST_QOS_OUTBOUND, dummy, VLAN16_TAG)
        tc_entities = self._analyse_qos_and_general_assertions(dummy)
        tc_classes, tc_filters, tc_qdiscs = tc_entities
        assert len(tc_classes.classes) == 1

        assert len(tc_qdiscs.leaf_qdiscs) == 2
        vlan_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, VLAN16_TAG)
        vlan_class = self._vlan_class(tc_classes.classes, VLAN16_TAG)
        self._assert_parent([vlan_qdisc], vlan_class)

        tag_filters = tc_filters.tagged_filters
        assert len(tag_filters) == 1
        assert int(tag_filters[0]['basic']['value']) == VLAN16_TAG

    @pytest.mark.xfail(
        condition=running_on_ovirt_ci() or running_on_travis_ci(),
        reason='does not work on CI with nmstate',
        strict=False,
    )
    @mock.patch('vdsm.network.netinfo.bonding.permanent_address', lambda: {})
    def test_multiple_vlans(self, dummy, vlan16, vlan17):
        for vlan_tag in (VLAN16_TAG, VLAN17_TAG):
            qos.configure_outbound(HOST_QOS_OUTBOUND, dummy, vlan_tag)

        tc_entities = self._analyse_qos_and_general_assertions(dummy)
        tc_classes, tc_filters, tc_qdiscs = tc_entities
        assert len(tc_classes.classes) == 2

        assert len(tc_qdiscs.leaf_qdiscs) == 3
        v1_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, VLAN16_TAG)
        v2_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, VLAN17_TAG)
        v1_class = self._vlan_class(tc_classes.classes, VLAN16_TAG)
        v2_class = self._vlan_class(tc_classes.classes, VLAN17_TAG)
        self._assert_parent([v1_qdisc], v1_class)
        self._assert_parent([v2_qdisc], v2_class)

        assert len(tc_filters.tagged_filters) == 2
        current_tagged_filters_flow_id = set(
            f['basic']['flowid'] for f in tc_filters.tagged_filters)
        expected_flow_ids = set('%s%x' % (qos._ROOT_QDISC_HANDLE, vlan_tag)
                                for vlan_tag in (VLAN16_TAG, VLAN17_TAG))
        assert current_tagged_filters_flow_id == expected_flow_ids

    @requires_iperf3
    @pytest.mark.xfail(reason='Not maintained stress test', run=False)
    def test_iperf_upper_limit(self, requires_tc):
        # Upper limit is not an accurate measure. This is because it converges
        # over time and depends on current machine hardware (CPU).
        # Hence, it is hard to make hard assertions on it. The test should run
        # at least 60 seconds (the longer the better) and the user should
        # inspect the computed average rate and optionally the additional
        # traffic data that was collected in client.out in order to be
        # convinced QOS is working properly.
        limit_kbps = 1000  # 1 Mbps (in kbps)
        server_ip = '192.0.2.1'
        client_ip = '192.0.2.10'
        qos_out = {'ul': {'m2': limit_kbps}, 'ls': {'m2': limit_kbps}}
        # using a network namespace is essential since otherwise the kernel
        # short-circuits the traffic and bypasses the veth devices and the
        # classfull qdisc.
        with network_namespace(
                'server_ns') as ns, bridge_device() as bridge, veth_pair() as (
                    server_peer,
                    server_dev,
                ), veth_pair() as (
                    client_dev,
                    client_peer,
                ):
            # iperf server and its veth peer lie in a separate network
            # namespace
            link_set_netns(server_dev, ns)
            bridge.add_port(server_peer)
            bridge.add_port(client_peer)
            netns_exec(ns, ['ip', 'link', 'set', 'dev', server_dev, 'up'])
            Interface.from_existing_dev_name(client_dev).add_ip(
                client_ip, 24, IpFamily.IPv4)
            netns_exec(
                ns,
                [
                    'ip',
                    '-4',
                    'addr',
                    'add',
                    'dev',
                    server_dev,
                    '%s/24' % server_ip,
                ],
            )
            qos.configure_outbound(qos_out, client_peer, None)
            with running(IperfServer(server_ip, network_ns=ns)):
                client = IperfClient(server_ip, client_ip, test_time=60)
                client.start()
                max_rate = max([
                    float(interval['streams'][0]['bits_per_second']) // (2**10)
                    for interval in client.out['intervals']
                ])
                assert 0 < max_rate < limit_kbps * 1.5

    def _analyse_qos_and_general_assertions(self, device_name):
        tc_classes = self._analyse_classes(device_name)
        tc_qdiscs = self._analyse_qdiscs(device_name)
        tc_filters = self._analyse_filters(device_name)
        self._assertions_on_classes(tc_classes.classes,
                                    tc_classes.default_class,
                                    tc_classes.root_class)
        self._assertions_on_qdiscs(tc_qdiscs.ingress_qdisc,
                                   tc_qdiscs.root_qdisc)
        self._assertions_on_filters(tc_filters.untagged_filters,
                                    tc_filters.tagged_filters)
        return tc_classes, tc_filters, tc_qdiscs

    def _analyse_classes(self, device_name):
        all_classes = list(tc.classes(device_name))
        root_class = self._root_class(all_classes)
        default_class = self._default_class(all_classes)
        all_classes.remove(root_class)
        all_classes.remove(default_class)
        return TcClasses(all_classes, default_class, root_class)

    def _analyse_qdiscs(self, device_name):
        all_qdiscs = list(tc.qdiscs(device_name))
        ingress_qdisc = self._ingress_qdisc(all_qdiscs)
        root_qdisc = self._root_qdisc(all_qdiscs)
        leaf_qdiscs = self._leaf_qdiscs(all_qdiscs)
        assert len(leaf_qdiscs) + 2 == len(all_qdiscs)
        return TcQdiscs(leaf_qdiscs, ingress_qdisc, root_qdisc)

    def _analyse_filters(self, device_name):
        filters = list(tc._filters(device_name))
        untagged_filters = self._untagged_filters(filters)
        tagged_filters = self._tagged_filters(filters)
        return TcFilters(untagged_filters, tagged_filters)

    def _assertions_on_classes(self, all_classes, default_class, root_class):
        assert all(
            cls.get('kind') == qos._SHAPING_QDISC_KIND
            for cls in all_classes), str(all_classes)

        self._assertions_on_root_class(root_class)

        self._assertions_on_default_class(default_class)

        if not all_classes:  # only a default class
            self._assert_upper_limit(default_class)
        else:
            for cls in all_classes:
                self._assert_upper_limit(cls)

    def _assertions_on_qdiscs(self, ingress_qdisc, root_qdisc):
        assert root_qdisc['kind'] == qos._SHAPING_QDISC_KIND
        self._assert_root_handle(root_qdisc)
        assert ingress_qdisc['handle'] == tc.QDISC_INGRESS

    def _assertions_on_filters(self, untagged_filters, tagged_filters):
        assert all(f['protocol'] == 'all' for f in tagged_filters)
        self._assert_parent_handle(tagged_filters + untagged_filters,
                                   qos._ROOT_QDISC_HANDLE)
        assert len(untagged_filters) == 1, untagged_filters
        assert untagged_filters[0]['protocol'] == 'all'

    def _assert_upper_limit(self, default_class):
        dclass = default_class[qos._SHAPING_QDISC_KIND]['ul']['m2']
        assert dclass == HOST_QOS_OUTBOUND['ul']['m2']

    def _assertions_on_default_class(self, default_class):
        self._assert_parent_handle([default_class], qos._ROOT_QDISC_HANDLE)
        assert default_class['leaf'] == DEFAULT_CLASSID + ':'
        dclass = default_class[qos._SHAPING_QDISC_KIND]['ls']
        assert dclass == HOST_QOS_OUTBOUND['ls']

    def _assertions_on_root_class(self, root_class):
        assert root_class is not None
        self._assert_root_handle(root_class)

    def _assert_root_handle(self, entity):
        assert entity['handle'] == qos._ROOT_QDISC_HANDLE

    def _assert_parent(self, entities, parent):
        assert all(e['parent'] == parent['handle'] for e in entities)

    def _assert_parent_handle(self, entities, parent_handle):
        assert all(e['parent'] == parent_handle for e in entities)

    def _root_class(self, classes):
        return _find_entity(lambda c: c.get('root'), classes)

    def _default_class(self, classes):
        default_cls_handle = qos._ROOT_QDISC_HANDLE + DEFAULT_CLASSID
        return _find_entity(lambda c: c['handle'] == default_cls_handle,
                            classes)

    def _ingress_qdisc(self, qdiscs):
        return _find_entity(lambda q: q['kind'] == 'ingress', qdiscs)

    def _root_qdisc(self, qdiscs):
        return _find_entity(lambda q: q.get('root'), qdiscs)

    def _leaf_qdiscs(self, qdiscs):
        return [
            qdisc for qdisc in qdiscs if qdisc['kind'] == qos._FAIR_QDISC_KIND
        ]

    def _untagged_filters(self, filters):
        predicate = lambda f: f.get('u32', {}).get('match', {}) == {
            'mask': 0,
            'value': 0,
            'offset': 0,
        }
        return list(f for f in filters if predicate(f))

    def _tagged_filters(self, filters):
        def tagged(f):
            return f.get('basic', {}).get('object') == 'vlan'

        return list(f for f in filters if tagged(f))

    def _vlan_qdisc(self, qdiscs, vlan_tag):
        handle = '%x:' % vlan_tag
        return _find_entity(lambda q: q['handle'] == handle, qdiscs)

    def _vlan_class(self, classes, vlan_tag):
        handle = qos._ROOT_QDISC_HANDLE + '%x' % vlan_tag
        return _find_entity(lambda c: c['handle'] == handle, classes)

    def _non_vlan_qdisc(self, qdiscs):
        handle = DEFAULT_CLASSID + ':'
        return _find_entity(lambda q: q['handle'] == handle, qdiscs)
Esempio n. 5
0
class TestPortMirror(object):
    """
    Tests port mirroring of IP traffic between two bridges.

    This test brings up two tap devices and attaches every device to a
    separate bridge. Then mirroring of IP packets between the two bridges is
    enabled. If sent through _tap0 the packet _ICMP arrives on _tap1 the test
    succeeds. The tap devices are needed because the tc filter rules only
    become active when the bridge is ready, and the bridge only becomes ready
    when it is attached to an active device.
    """

    #  [ Ethernet ]
    #  dst       = 00:1c:c0:d0:44:dc
    #  src       = 00:21:5c:4d:42:75
    #  type      = IPv4
    #     [ IP ]
    #     version   = 4L
    #     ihl       = 5L
    #     tos       = 0x0
    #     len       = 33
    #     id        = 1
    #     flags     =
    #     frag      = 0L
    #     ttl       = 64
    #     proto     = icmp
    #     chksum    = 0xf953
    #     src       = 192.168.0.52
    #     dst       = 192.168.0.3
    #     \options   \
    #        [ ICMP ]
    #        type      = echo-request
    #        code      = 0
    #        chksum    = 0x2875
    #        id        = 0x0
    #        seq       = 0x0
    #           [ Raw ]
    #           load      = '\x01#Eg\x89'
    _ICMP = unhexlify('001cc0d044dc'
                      '00215c4d4275'
                      '0800'  # Ethernet
                      '45000021000100004001f953'
                      'c0a80034'
                      'c0a80003'  # IP
                      '080028750000000'  # ICMP
                      '00123456789')  # Payload

    @pytest.fixture(autouse=True)
    def setup(self, requires_tc):
        self._tap0 = Tap()
        self._tap1 = Tap()
        self._tap2 = Tap()
        self._bridge0 = Bridge('src-')
        self._bridge1 = Bridge('target-')
        self._bridge2 = Bridge('target2-')
        self._devices = [
            self._tap0,
            self._tap1,
            self._tap2,
            self._bridge0,
            self._bridge1,
            self._bridge2,
        ]

        cleanup = []
        try:
            for iface in self._devices:
                iface.create()
                cleanup.append(iface)
            self._bridge0.add_port(self._tap0.dev_name)
            self._bridge1.add_port(self._tap1.dev_name)
            self._bridge2.add_port(self._tap2.dev_name)

            yield
        finally:
            failed = []
            for iface in cleanup:
                try:
                    iface.remove()
                except Exception:
                    failed.append(str(iface))
            if failed:
                raise RuntimeError(f'Error removing devices: {failed}')

    def _send_ping(self):
        self._tap1.start_listener(self._ICMP)
        self._tap0.write_to_device(self._ICMP)

        if _wait_for_func(lambda: not self._tap1.is_listener_alive(),
                          timeout=2):
            return True
        else:
            self._tap1.stop_listener()
            return False

    @pytest.mark.xfail(
        condition=running_on_travis_ci(),
        reason='does not work on  Travis CI with nmstate',
        strict=False,
    )
    def test_mirroring(self):
        tc.setPortMirroring(self._bridge0.dev_name, self._bridge1.dev_name)
        assert self._send_ping(), 'Bridge received no mirrored ping requests.'

        tc.unsetPortMirroring(self._bridge0.dev_name, self._bridge1.dev_name)
        msg = 'Bridge received mirrored ping requests, but mirroring is unset.'
        assert not self._send_ping(), msg

    def test_mirroring_with_distraction(self):
        # setting another mirror action should not obstract the first one
        tc.setPortMirroring(self._bridge0.dev_name, self._bridge2.dev_name)
        self.test_mirroring()
        tc.unsetPortMirroring(self._bridge0.dev_name, self._bridge2.dev_name)