def report_network_qos(netinfo): """Augment netinfo information with QoS data for the engine""" qdiscs = defaultdict(list) for qdisc in tc.qdiscs(dev=None): # None -> all dev qdiscs qdiscs[qdisc['dev']].append(qdisc) for net, attrs in netinfo['networks'].iteritems(): iface = attrs['iface'] if iface in netinfo['bridges']: host_ports = [port for port in attrs['ports'] if not port.startswith('vnet')] if not host_ports: # Port-less bridge continue iface, = host_ports if iface in netinfo['vlans']: vlan_id = netinfo['vlans'][iface]['vlanid'] iface = netinfo['vlans'][iface]['iface'] iface_qdiscs = qdiscs.get(iface) if iface_qdiscs is None: continue class_id = (get_root_qdisc(iface_qdiscs)['handle'] + '%x' % vlan_id) else: iface_qdiscs = qdiscs.get(iface) if iface_qdiscs is None: continue class_id = (get_root_qdisc(iface_qdiscs)['handle'] + DEFAULT_CLASSID) # Now that iface is either a bond or a nic, let's get the QoS info classes = [cls for cls in tc.classes(iface, classid=class_id) if cls['kind'] == 'hfsc'] if classes: cls, = classes attrs['hostQos'] = {'out': cls['hfsc']}
def remove_outbound(device, vlan_tag): """Removes the qosOutbound configuration from the device and restores pfifo_fast if it was the last QoSed network on the device. vlan_tag can be None""" class_id = '%x' % (_NON_VLANNED_ID if vlan_tag is None else vlan_tag) MISSING_OBJ_ERR_CODES = (errno.EINVAL, errno.ENOENT, errno.EOPNOTSUPP) try: tc.filter.delete( device, pref=_NON_VLANNED_ID if vlan_tag is None else vlan_tag) except tc.TrafficControlException as tce: if tce.errCode not in MISSING_OBJ_ERR_CODES: # No filter exists raise device_qdiscs = list(tc.qdiscs(device)) if not device_qdiscs: return root_qdisc_handle = netinfo_qos.get_root_qdisc(device_qdiscs)['handle'] try: tc.cls.delete(device, classid=root_qdisc_handle + class_id) except tc.TrafficControlException as tce: if tce.errCode not in MISSING_OBJ_ERR_CODES: # No class exists raise if not _uses_classes(device, root_qdisc_handle=root_qdisc_handle): try: tc._qdisc_del(device) tc._qdisc_del(device, kind='ingress') except tc.TrafficControlException as tce: if tce.errCode not in MISSING_OBJ_ERR_CODES: # No qdisc raise
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 configure_outbound(qosOutbound, device, vlan_tag): """Adds the qosOutbound configuration to the backing device (be it bond or nic). Adds a class and filter for default traffic if necessary. vlan_tag can be None""" root_qdisc = netinfo_qos.get_root_qdisc(tc.qdiscs(device)) class_id = '%x' % (_NON_VLANNED_ID if vlan_tag is None else vlan_tag) if not root_qdisc or root_qdisc['kind'] != _SHAPING_QDISC_KIND: _fresh_qdisc_conf_out(device, vlan_tag, class_id, qosOutbound) else: _qdisc_conf_out(device, root_qdisc['handle'], vlan_tag, class_id, qosOutbound)
def _uses_classes(device, root_qdisc_handle=None): """Returns true iff there's traffic classes in the device, ignoring the root class and a default unused class""" if root_qdisc_handle is None: root_qdisc = netinfo_qos.get_root_qdisc(tc.qdiscs(device)) root_qdisc_handle = root_qdisc['handle'] classes = [cls for cls in tc.classes(device, parent=root_qdisc_handle) if not cls.get('root')] return (classes and not(len(classes) == 1 and not ifaceUsed(device) and classes[0]['handle'] == root_qdisc_handle + _DEFAULT_CLASSID))
def _uses_classes(device, root_qdisc_handle=None): """Returns true iff there's traffic classes in the device, ignoring the root class and a default unused class""" if root_qdisc_handle is None: root_qdisc = netinfo_qos.get_root_qdisc(tc.qdiscs(device)) root_qdisc_handle = root_qdisc['handle'] classes = [ cls for cls in tc.classes(device, parent=root_qdisc_handle) if not cls.get('root') ] return (classes and not (len(classes) == 1 and not ifaceUsed(device) and classes[0]['handle'] == root_qdisc_handle + _DEFAULT_CLASSID))
def test_qdiscs(self): data = '\n'.join(( 'qdisc hfsc 1: root refcnt 2 default 5000', 'qdisc sfq 10: parent 1:10 limit 127p quantum 1514b', 'qdisc sfq 20: parent 1:20 limit 127p quantum 1514b', 'qdisc sfq 30: parent 1:30 limit 127p quantum 30Kb perturb 3sec', 'qdisc sfq 40: parent 1:40 limit 127p quantum 20Mb perturb 5sec', 'qdisc ingress ffff: parent ffff:fff1 ----------------', 'qdisc mq 0: dev wlp3s0 root', 'qdisc ingress ffff: dev vdsmtest-Z2TMO parent ffff:fff1 ' '----------------', # end of previous line 'qdisc pfifo_fast 0: dev em1 root refcnt 2 bands 3 priomap ' '1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1', # end of previous line 'qdisc pfifo_fast 0: dev wlp3s0 parent :1 bands 3 priomap ' '1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1', # end of previous line 'qdisc fq_codel 801e: root refcnt 2 limit 132p flows 15 quantum ' '400 target 5.0ms interval 150.0ms ecn', # end of previous line )) qdiscs = ( {'kind': 'hfsc', 'root': True, 'handle': '1:', 'refcnt': 2, 'hfsc': {'default': 0x5000}}, {'kind': 'sfq', 'handle': '10:', 'parent': '1:10', 'sfq': {'limit': 127, 'quantum': 1514}}, {'kind': 'sfq', 'handle': '20:', 'parent': '1:20', 'sfq': {'limit': 127, 'quantum': 1514}}, {'kind': 'sfq', 'handle': '30:', 'parent': '1:30', 'sfq': {'limit': 127, 'quantum': 30 * 1024, 'perturb': 3}}, {'kind': 'sfq', 'handle': '40:', 'parent': '1:40', 'sfq': {'limit': 127, 'quantum': 20 * 1024 ** 2, 'perturb': 5}}, {'kind': 'ingress', 'handle': 'ffff:', 'parent': 'ffff:fff1'}, {'kind': 'mq', 'handle': '0:', 'dev': 'wlp3s0', 'root': True}, {'kind': 'ingress', 'handle': 'ffff:', 'dev': 'vdsmtest-Z2TMO', 'parent': 'ffff:fff1'}, {'kind': 'pfifo_fast', 'handle': '0:', 'dev': 'em1', 'root': True, 'refcnt': 2, 'pfifo_fast': { 'bands': 3, 'priomap': [1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1]}}, {'kind': 'pfifo_fast', 'handle': '0:', 'dev': 'wlp3s0', 'parent': ':1', 'pfifo_fast': { 'bands': 3, 'priomap': [1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1]}}, {'kind': 'fq_codel', 'handle': '801e:', 'root': True, 'refcnt': 2, 'fq_codel': {'limit': 132, 'flows': 15, 'quantum': 400, 'target': 5000.0, 'interval': 150000.0, 'ecn': True}}, ) for parsed, correct in izip_longest(tc.qdiscs(None, out=data), qdiscs): self.assertEqual(parsed, correct)
def test_qdiscs(self): data = '\n'.join(( 'qdisc hfsc 1: root refcnt 2 default 5000', 'qdisc sfq 10: parent 1:10 limit 127p quantum 1514b', 'qdisc sfq 20: parent 1:20 limit 127p quantum 1514b', 'qdisc sfq 30: parent 1:30 limit 127p quantum 30Kb perturb 3sec', 'qdisc sfq 40: parent 1:40 limit 127p quantum 20Mb perturb 5sec', 'qdisc ingress ffff: parent ffff:fff1 ----------------', 'qdisc mq 0: dev wlp3s0 root', 'qdisc ingress ffff: dev vdsmtest-Z2TMO parent ffff:fff1 ' '----------------', # end of previous line 'qdisc pfifo_fast 0: dev em1 root refcnt 2 bands 3 priomap ' '1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1', # end of previous line 'qdisc pfifo_fast 0: dev wlp3s0 parent :1 bands 3 priomap ' '1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1', # end of previous line 'qdisc fq_codel 801e: root refcnt 2 limit 132p flows 15 quantum ' '400 target 5.0ms interval 150.0ms ecn', # end of previous line )) qdiscs = ( { 'kind': 'hfsc', 'root': True, 'handle': '1:', 'refcnt': 2, 'hfsc': { 'default': 0x5000 } }, { 'kind': 'sfq', 'handle': '10:', 'parent': '1:10', 'sfq': { 'limit': 127, 'quantum': 1514 } }, { 'kind': 'sfq', 'handle': '20:', 'parent': '1:20', 'sfq': { 'limit': 127, 'quantum': 1514 } }, { 'kind': 'sfq', 'handle': '30:', 'parent': '1:30', 'sfq': { 'limit': 127, 'quantum': 30 * 1024, 'perturb': 3 } }, { 'kind': 'sfq', 'handle': '40:', 'parent': '1:40', 'sfq': { 'limit': 127, 'quantum': 20 * 1024**2, 'perturb': 5 } }, { 'kind': 'ingress', 'handle': 'ffff:', 'parent': 'ffff:fff1' }, { 'kind': 'mq', 'handle': '0:', 'dev': 'wlp3s0', 'root': True }, { 'kind': 'ingress', 'handle': 'ffff:', 'dev': 'vdsmtest-Z2TMO', 'parent': 'ffff:fff1' }, { 'kind': 'pfifo_fast', 'handle': '0:', 'dev': 'em1', 'root': True, 'refcnt': 2, 'pfifo_fast': { 'bands': 3, 'priomap': [1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1] } }, { 'kind': 'pfifo_fast', 'handle': '0:', 'dev': 'wlp3s0', 'parent': ':1', 'pfifo_fast': { 'bands': 3, 'priomap': [1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1] } }, { 'kind': 'fq_codel', 'handle': '801e:', 'root': True, 'refcnt': 2, 'fq_codel': { 'limit': 132, 'flows': 15, 'quantum': 400, 'target': 5000.0, 'interval': 150000.0, 'ecn': True } }, ) for parsed, correct in izip_longest(tc.qdiscs(None, out=data), qdiscs): self.assertEqual(parsed, correct)