def test_stopped(self): with monitor.Monitor(timeout=self.TIMEOUT) as mon: dummy = Dummy() dummy_name = dummy.create() dummy.remove() found = any(event.get('name') == dummy_name for event in mon) self.assertTrue(found, 'Expected event was not caught.')
def test_iterate_after_events(self): with monitor.Monitor(timeout=self.TIMEOUT) as mon: dummy = Dummy() dummy_name = dummy.create() dummy.remove() for event in mon: if event.get('name') == dummy_name: break
def test_timeout_not_triggered(self): time_start = monotonic_time() with monitor.Monitor(timeout=self.TIMEOUT) as mon: dummy = Dummy() dummy.create() dummy.remove() for event in mon: break self.assertTrue((monotonic_time() - time_start) <= self.TIMEOUT) self.assertTrue(mon.is_stopped())
def test_events_keys(self): def _simplify_event(event): """ Strips event keys except event, address, name, destination, family. """ allow = set(['event', 'address', 'name', 'destination', 'family']) return {k: v for (k, v) in event.items() if k in allow} def _expected_events(nic, address, cidr): events_add = [ {'event': 'new_link', 'name': nic}, {'event': 'new_addr', 'address': address + '/' + cidr}, {'event': 'new_link', 'name': nic}] events_del = [ {'address': address + '/' + cidr, 'event': 'del_addr'}, {'destination': address, 'event': 'del_route'}, {'event': 'del_link', 'name': nic}] events_ipv6 = [ {'event': 'new_addr', 'family': 'inet6'}, {'event': 'del_neigh'}, {'event': 'del_addr', 'family': 'inet6'}] if is_disabled_ipv6(): return deque(events_add + events_del) else: return deque(events_add + events_ipv6 + events_del) with monitor.Monitor(timeout=self.TIMEOUT, silent_timeout=True) as mon: dummy = Dummy() dummy_name = dummy.create() dummy.set_ip(IP_ADDRESS, IP_CIDR) dummy.up() dummy.remove() expected_events = _expected_events(dummy_name, IP_ADDRESS, IP_CIDR) _expected = list(expected_events) _caught = [] expected = expected_events.popleft() for event in mon: _caught.append(event) if _is_subdict(expected, event): expected = expected_events.popleft() if len(expected_events) == 0: break self.assertEqual(0, len(expected_events), 'Expected events have not ' 'been caught (in the right order).\n' 'Expected:\n%s.\nCaught:\n%s.' % ('\n'.join([str(d) for d in _expected]), '\n'.join([str(_simplify_event(d)) for d in _caught])))
def test_iteration(self): with monitor.Monitor(timeout=self.TIMEOUT) as mon: iterator = iter(mon) # Generate events to avoid blocking dummy = Dummy() dummy.create() iterator.next() dummy.remove() iterator.next() with self.assertRaises(StopIteration): while True: iterator.next()
def test_event_groups(self): with monitor.Monitor(timeout=self.TIMEOUT, groups=('ipv4-ifaddr',)) as mon_a: with monitor.Monitor(timeout=self.TIMEOUT, groups=('link', 'ipv4-route')) as mon_l_r: dummy = Dummy() dummy.create() dummy.set_ip(IP_ADDRESS, IP_CIDR) dummy.up() dummy.remove() for event in mon_a: self.assertIn('_addr', event['event'], "Caught event '%s' is not " "related to address." % event['event']) for event in mon_l_r: link_or_route = ('_link' in event['event'] or '_route' in event['event']) self.assertTrue(link_or_route, "Caught event '%s' is not related " "to link or route." % event['event'])
def test_event_groups(self): with monitor.Monitor(timeout=self.TIMEOUT, groups=('ipv4-ifaddr', )) as mon_a: with monitor.Monitor(timeout=self.TIMEOUT, groups=('link', 'ipv4-route')) as mon_l_r: dummy = Dummy() dummy.create() dummy.set_ip(IP_ADDRESS, IP_CIDR) dummy.up() dummy.remove() for event in mon_a: self.assertIn( '_addr', event['event'], "Caught event '%s' is not " "related to address." % event['event']) for event in mon_l_r: link_or_route = ('_link' in event['event'] or '_route' in event['event']) self.assertTrue( link_or_route, "Caught event '%s' is not related " "to link or route." % event['event'])
class TestConfigureOutbound(TestCaseBase): def setUp(self): self.device = Dummy() self.device.create() self.device.up() self.device_name = self.device.devName # TODO: # test remove_outbound def tearDown(self): self.device.remove() def test_single_non_vlan(self): qos.configure_outbound(HOST_QOS_OUTBOUND, self.device_name, None) tc_classes, tc_filters, tc_qdiscs = \ self._analyse_qos_and_general_assertions() self.assertEqual(tc_classes.classes, []) self.assertEqual(len(tc_qdiscs.leaf_qdiscs), 1) self.assertIsNotNone(self._non_vlan_qdisc(tc_qdiscs.leaf_qdiscs)) self._assert_parent(tc_qdiscs.leaf_qdiscs, tc_classes.default_class) self.assertEqual(len(tc_filters.tagged_filters), 0) @permutations([[1], [2]]) def test_single_vlan(self, repeating_calls): with vlan_device(self.device_name) as vlan: for _ in range(repeating_calls): qos.configure_outbound(HOST_QOS_OUTBOUND, self.device_name, vlan.tag) tc_classes, tc_filters, tc_qdiscs = \ self._analyse_qos_and_general_assertions() self.assertEqual(len(tc_classes.classes), 1) self.assertEqual(len(tc_qdiscs.leaf_qdiscs), 2) vlan_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, vlan.tag) vlan_class = self._vlan_class(tc_classes.classes, vlan.tag) self._assert_parent([vlan_qdisc], vlan_class) self.assertEqual(len(tc_filters.tagged_filters), 1) self.assertEqual( int(tc_filters.tagged_filters[0]['basic']['value']), vlan.tag) def test_multiple_vlans(self): with vlan_device(self.device_name, tag=16) as vlan1: with vlan_device(self.device_name, tag=17) as vlan2: for v in (vlan1, vlan2): qos.configure_outbound(HOST_QOS_OUTBOUND, self.device_name, v.tag) tc_classes, tc_filters, tc_qdiscs = \ self._analyse_qos_and_general_assertions() self.assertEqual(len(tc_classes.classes), 2) self.assertEqual(len(tc_qdiscs.leaf_qdiscs), 3) v1_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, vlan1.tag) v2_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, vlan2.tag) v1_class = self._vlan_class(tc_classes.classes, vlan1.tag) v2_class = self._vlan_class(tc_classes.classes, vlan2.tag) self._assert_parent([v1_qdisc], v1_class) self._assert_parent([v2_qdisc], v2_class) self.assertEqual(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, v.tag) for v in (vlan1, vlan2)) self.assertEqual(current_tagged_filters_flow_id, expected_flow_ids) @stresstest @requires_iperf3 @requires_tc def test_iperf_upper_limit(self): # 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): linkSet(server_peer, ['up']) linkSet(client_peer, ['up']) # iperf server and its veth peer lie in a separate network # namespace link_set_netns(server_dev, ns) bridge.addIf(server_peer) bridge.addIf(client_peer) linkSet(client_dev, ['up']) netns_exec(ns, ['ip', 'link', 'set', 'dev', server_dev, 'up']) addrAdd(client_dev, client_ip, 24) 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']]) self.assertTrue(0 < max_rate < limit_kbps * 1.5) def _analyse_qos_and_general_assertions(self): tc_classes = self._analyse_classes() tc_qdiscs = self._analyse_qdiscs() tc_filters = self._analyse_filters() 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): all_classes = list(tc.classes(self.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): all_qdiscs = list(tc._qdiscs(self.device_name)) ingress_qdisc = self._ingress_qdisc(all_qdiscs) root_qdisc = self._root_qdisc(all_qdiscs) leaf_qdiscs = self._leaf_qdiscs(all_qdiscs) self.assertEqual(len(leaf_qdiscs) + 2, len(all_qdiscs)) return TcQdiscs(leaf_qdiscs, ingress_qdisc, root_qdisc) def _analyse_filters(self): filters = list(tc._filters(self.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): self.assertTrue(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): self.assertEqual(root_qdisc['kind'], qos._SHAPING_QDISC_KIND) self._assert_root_handle(root_qdisc) self.assertEqual(ingress_qdisc['handle'], tc.QDISC_INGRESS) def _assertions_on_filters(self, untagged_filters, tagged_filters): self.assertTrue(all(f['protocol'] == 'all' for f in tagged_filters)) self._assert_parent_handle(tagged_filters + untagged_filters, qos._ROOT_QDISC_HANDLE) self.assertEqual(len(untagged_filters), 1) self.assertEqual(untagged_filters[0]['protocol'], 'all') def _assert_upper_limit(self, default_class): self.assertEqual( default_class[qos._SHAPING_QDISC_KIND]['ul']['m2'], HOST_QOS_OUTBOUND['ul']['m2']) def _assertions_on_default_class(self, default_class): self._assert_parent_handle([default_class], qos._ROOT_QDISC_HANDLE) self.assertEqual(default_class['leaf'], qos._DEFAULT_CLASSID + ':') self.assertEqual(default_class[qos._SHAPING_QDISC_KIND]['ls'], HOST_QOS_OUTBOUND['ls']) def _assertions_on_root_class(self, root_class): self.assertIsNotNone(root_class) self._assert_root_handle(root_class) def _assert_root_handle(self, entity): self.assertEqual(entity['handle'], qos._ROOT_QDISC_HANDLE) def _assert_parent(self, entities, parent): self.assertTrue(all(e['parent'] == parent['handle'] for e in entities)) def _assert_parent_handle(self, entities, parent_handle): self.assertTrue(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 + qos._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 = qos._DEFAULT_CLASSID + ':' return _find_entity(lambda q: q['handle'] == handle, qdiscs)
def test_events_keys(self): def _simplify_event(event): """ Strips event keys except event, address, name, destination, family. """ allow = set(['event', 'address', 'name', 'destination', 'family']) return {k: v for (k, v) in event.items() if k in allow} def _expected_events(nic, address, cidr): events_add = [{ 'event': 'new_link', 'name': nic }, { 'event': 'new_addr', 'address': address + '/' + cidr }, { 'event': 'new_link', 'name': nic }] events_del = [{ 'address': address + '/' + cidr, 'event': 'del_addr' }, { 'destination': address, 'event': 'del_route' }, { 'event': 'del_link', 'name': nic }] events_ipv6 = [{ 'event': 'new_addr', 'family': 'inet6' }, { 'event': 'del_neigh' }, { 'event': 'del_addr', 'family': 'inet6' }] if is_disabled_ipv6(): return deque(events_add + events_del) else: return deque(events_add + events_ipv6 + events_del) with monitor.Monitor(timeout=self.TIMEOUT, silent_timeout=True) as mon: dummy = Dummy() dummy_name = dummy.create() dummy.set_ip(IP_ADDRESS, IP_CIDR) dummy.up() dummy.remove() expected_events = _expected_events(dummy_name, IP_ADDRESS, IP_CIDR) _expected = list(expected_events) _caught = [] expected = expected_events.popleft() for event in mon: _caught.append(event) if _is_subdict(expected, event): expected = expected_events.popleft() if len(expected_events) == 0: break self.assertEqual( 0, len(expected_events), 'Expected events have not ' 'been caught (in the right order).\n' 'Expected:\n%s.\nCaught:\n%s.' % ('\n'.join([str(d) for d in _expected]), '\n'.join( [str(_simplify_event(d)) for d in _caught])))
class TestConfigureOutbound(TestCaseBase): def setUp(self): self.device = Dummy() self.device.create() self.device.up() self.device_name = self.device.devName # TODO: # test remove_outbound def tearDown(self): self.device.remove() def test_single_non_vlan(self): qos.configure_outbound(HOST_QOS_OUTBOUND, self.device_name, None) tc_classes, tc_filters, tc_qdiscs = \ self._analyse_qos_and_general_assertions() self.assertEqual(tc_classes.classes, []) self.assertEqual(len(tc_qdiscs.leaf_qdiscs), 1) self.assertIsNotNone(self._non_vlan_qdisc(tc_qdiscs.leaf_qdiscs)) self._assert_parent(tc_qdiscs.leaf_qdiscs, tc_classes.default_class) self.assertEqual(len(tc_filters.tagged_filters), 0) @permutations([[1], [2]]) def test_single_vlan(self, repeating_calls): with vlan_device(self.device_name) as vlan: for _ in range(repeating_calls): qos.configure_outbound(HOST_QOS_OUTBOUND, self.device_name, vlan.tag) tc_classes, tc_filters, tc_qdiscs = \ self._analyse_qos_and_general_assertions() self.assertEqual(len(tc_classes.classes), 1) self.assertEqual(len(tc_qdiscs.leaf_qdiscs), 2) vlan_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, vlan.tag) vlan_class = self._vlan_class(tc_classes.classes, vlan.tag) self._assert_parent([vlan_qdisc], vlan_class) self.assertEqual(len(tc_filters.tagged_filters), 1) self.assertEqual( int(tc_filters.tagged_filters[0]['basic']['value']), vlan.tag) def test_multiple_vlans(self): with vlan_device(self.device_name, tag=16) as vlan1: with vlan_device(self.device_name, tag=17) as vlan2: for v in (vlan1, vlan2): qos.configure_outbound(HOST_QOS_OUTBOUND, self.device_name, v.tag) tc_classes, tc_filters, tc_qdiscs = \ self._analyse_qos_and_general_assertions() self.assertEqual(len(tc_classes.classes), 2) self.assertEqual(len(tc_qdiscs.leaf_qdiscs), 3) v1_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, vlan1.tag) v2_qdisc = self._vlan_qdisc(tc_qdiscs.leaf_qdiscs, vlan2.tag) v1_class = self._vlan_class(tc_classes.classes, vlan1.tag) v2_class = self._vlan_class(tc_classes.classes, vlan2.tag) self._assert_parent([v1_qdisc], v1_class) self._assert_parent([v2_qdisc], v2_class) self.assertEqual(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, v.tag) for v in (vlan1, vlan2)) self.assertEqual(current_tagged_filters_flow_id, expected_flow_ids) @stresstest @requires_iperf3 @requires_tc def test_iperf_upper_limit(self): # 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): linkSet(server_peer, ['up']) linkSet(client_peer, ['up']) # iperf server and its veth peer lie in a separate network # namespace link_set_netns(server_dev, ns) bridge.addIf(server_peer) bridge.addIf(client_peer) linkSet(client_dev, ['up']) netns_exec(ns, ['ip', 'link', 'set', 'dev', server_dev, 'up']) addrAdd(client_dev, client_ip, 24) 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'] ]) self.assertTrue(0 < max_rate < limit_kbps * 1.5) def _analyse_qos_and_general_assertions(self): tc_classes = self._analyse_classes() tc_qdiscs = self._analyse_qdiscs() tc_filters = self._analyse_filters() 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): all_classes = list(tc.classes(self.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): all_qdiscs = list(tc.qdiscs(self.device_name)) ingress_qdisc = self._ingress_qdisc(all_qdiscs) root_qdisc = self._root_qdisc(all_qdiscs) leaf_qdiscs = self._leaf_qdiscs(all_qdiscs) self.assertEqual(len(leaf_qdiscs) + 2, len(all_qdiscs)) return TcQdiscs(leaf_qdiscs, ingress_qdisc, root_qdisc) def _analyse_filters(self): filters = list(tc._filters(self.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): self.assertTrue( 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): self.assertEqual(root_qdisc['kind'], qos._SHAPING_QDISC_KIND) self._assert_root_handle(root_qdisc) self.assertEqual(ingress_qdisc['handle'], tc.QDISC_INGRESS) def _assertions_on_filters(self, untagged_filters, tagged_filters): self.assertTrue(all(f['protocol'] == 'all' for f in tagged_filters)) self._assert_parent_handle(tagged_filters + untagged_filters, qos._ROOT_QDISC_HANDLE) self.assertEqual(len(untagged_filters), 1) self.assertEqual(untagged_filters[0]['protocol'], 'all') def _assert_upper_limit(self, default_class): self.assertEqual(default_class[qos._SHAPING_QDISC_KIND]['ul']['m2'], HOST_QOS_OUTBOUND['ul']['m2']) def _assertions_on_default_class(self, default_class): self._assert_parent_handle([default_class], qos._ROOT_QDISC_HANDLE) self.assertEqual(default_class['leaf'], DEFAULT_CLASSID + ':') self.assertEqual(default_class[qos._SHAPING_QDISC_KIND]['ls'], HOST_QOS_OUTBOUND['ls']) def _assertions_on_root_class(self, root_class): self.assertIsNotNone(root_class) self._assert_root_handle(root_class) def _assert_root_handle(self, entity): self.assertEqual(entity['handle'], qos._ROOT_QDISC_HANDLE) def _assert_parent(self, entities, parent): self.assertTrue(all(e['parent'] == parent['handle'] for e in entities)) def _assert_parent_handle(self, entities, parent_handle): self.assertTrue(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)