Antoni Segura Puimedon has uploaded a new change for review.

Change subject: tc: [WIP] addNetwork support
......................................................................

tc: [WIP] addNetwork support

Missing api schema and tests

Change-Id: I4a5378870a3dac9307a6eef8d9937a9c03a0c819
Signed-off-by: Antoni S. Puimedon <[email protected]>
---
M vdsm/network/api.py
M vdsm/network/configurators/__init__.py
A vdsm/network/configurators/qos.py
M vdsm/network/models.py
4 files changed, 173 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/67/30467/1

diff --git a/vdsm/network/api.py b/vdsm/network/api.py
index ff08308..aba08b1 100755
--- a/vdsm/network/api.py
+++ b/vdsm/network/api.py
@@ -324,6 +324,7 @@
 
     netEnt.configure(**options)
     configurator.configureLibvirtNetwork(network, netEnt)
+    configurator.configureQoS(qosOutbound, netEnt)
 
 
 def assertBridgeClean(bridge, vlan, bonding, nics):
diff --git a/vdsm/network/configurators/__init__.py 
b/vdsm/network/configurators/__init__.py
index 6e2f969..e2290d1 100644
--- a/vdsm/network/configurators/__init__.py
+++ b/vdsm/network/configurators/__init__.py
@@ -25,6 +25,7 @@
 from vdsm.netconfpersistence import RunningConfig
 
 from . import libvirt
+from . import qos
 from ..models import Bond, Bridge
 from ..sourceroute import StaticSourceRoute
 
@@ -111,6 +112,12 @@
     def removeLibvirtNetwork(self, network):
         self.configApplier.removeLibvirtNetwork(network)
 
+    def configureQoS(self, qosOutbound, top_device):
+        qos.configure_outbound(qosOutbound, top_device)
+
+    def removeQoS(self, qosOutbound, top_device):
+        qos.configure_outbound(qosOutbound, top_device)
+
     def _addSourceRoute(self, netEnt):
         ip = netEnt.ipConfig
         # bootproto is None for both static and no bootproto
diff --git a/vdsm/network/configurators/qos.py 
b/vdsm/network/configurators/qos.py
new file mode 100644
index 0000000..ff976e8
--- /dev/null
+++ b/vdsm/network/configurators/qos.py
@@ -0,0 +1,139 @@
+# Copyright 2014 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+import errno
+import os
+from distutils.version import StrictVersion
+
+from .. import tc
+_NON_VLANNED_ID = '%x' % 5000
+_ROOT_QDISC_HANDLE = '%x:' % 5001  # Leave 0 free for leaf qdisc of vlan tag 0
+_FAIR_QDISC_KIND = 'fq_codel' if (StrictVersion(os.uname()[2].split('-')[0]) >
+                                  StrictVersion('3.5.0')) else 'sfq'
+
+
+def configure_outbound(qosOutbound, top_device):
+    description = top_device.description
+    vlan_tag = description.get('vlan_tag')
+    device = description['log_dev']
+    qdiscs = list(tc._qdiscs(device))
+    root_qdisc = _root_qdisc(qdiscs)
+    class_id = _NON_VLANNED_ID if vlan_tag is None else '%x' % vlan_tag
+    if root_qdisc['kind'] != 'hfsc':
+        _fresh_qdisc_conf_out(device, vlan_tag, class_id,
+                              _qos_to_str_dict(qosOutbound))
+    else:
+        _qdisc_conf_out(device, root_qdisc['handle'], vlan_tag, class_id,
+                        _qos_to_str_dict(qosOutbound))
+
+
+def unconfigure_outbound(qosOutbound, top_device):
+    pass
+
+
+def _fresh_qdisc_conf_out(dev, vlan_tag, class_id, qos):
+    """Replaces the dev qdisc with hfsc and sets up the shaping"""
+    # Use deletion + addition to flush children classes and filters
+    try:
+        tc.qdisc.delete(dev)  # Deletes the root qdisc by default
+    except tc.TrafficControlException as tce:
+        if tce.errCode != tc.ERR_DEV_NOEXIST:
+            raise
+    tc.qdisc.add(dev, 'hfsc', root=True, handle='0x' + _ROOT_QDISC_HANDLE,
+                 default='0x' + _NON_VLANNED_ID)
+
+    tc.cls.add(dev, 'hfsc', parent=_ROOT_QDISC_HANDLE,
+               classid=_ROOT_QDISC_HANDLE, **qos)
+
+    # Add traffic classes
+    _add_hfsc_cls(dev, class_id, **qos)
+    if class_id != _NON_VLANNED_ID:  # We need to add a default class
+        _add_hfsc_cls(dev, _NON_VLANNED_ID, ls=qos['ls'])
+
+    # Add filters to move the traffic into the classes we just created
+    _add_non_vlanned_filter(dev)
+    if class_id != _NON_VLANNED_ID:
+        _add_vlan_filter(dev, vlan_tag, class_id)
+
+    # Add inside intra-class fairness qdisc (fq_codel/sfq)
+    _add_fair_qdisc(dev, class_id)
+    if class_id != _NON_VLANNED_ID:
+        _add_fair_qdisc(dev, _NON_VLANNED_ID)
+
+
+def _qdisc_conf_out(dev, root_qdisc_handle, vlan_tag, class_id, qos):
+    """Adds the traffic class and filtering to the current hfsc qdisc"""
+    filters = [filt for filt in tc._filters(dev, parent=root_qdisc_handle) if
+               'u32' in filt and filt['u32'].get('flowid') ==
+               _ROOT_QDISC_HANDLE + class_id]
+
+    # Clear up any previous filters to the class
+    for filt in filters:
+        try:
+            tc.filter.delete(dev, filt['pref'], parent=root_qdisc_handle)
+        except tc.TrafficControlException as tce:
+            if tce.errCode != errno.EINVAL:  # no filters exist -> EINVAL
+                raise
+
+    # Clear the class in case it exists
+    try:
+        tc.cls.delete(dev, classid=root_qdisc_handle + class_id)
+    except tc.TrafficControlException as tce:
+        if tce.errCode != errno.ENOENT:
+            raise
+
+    _add_hfsc_cls(dev, _NON_VLANNED_ID, **qos)
+    _add_vlan_filter(dev, vlan_tag, class_id)
+    _add_fair_qdisc(dev, class_id)
+
+
+def _add_vlan_filter(dev, vlan_tag, class_id):
+    tc.filter.replace(dev, parent=_ROOT_QDISC_HANDLE, protocol='802.1q',
+                      u32=['match', 'u16', '0x%x' % vlan_tag, '0xFFF', 'at',
+                           '-4', 'flowid', '0x' + class_id])
+
+
+def _add_non_vlanned_filter(dev):
+    tc.filter.replace(dev, parent=_ROOT_QDISC_HANDLE, protocol='all',
+                      u32=['match', 'u8', '0', '0', 'flowid',
+                           '0x' + _NON_VLANNED_ID])
+
+
+def _add_fair_qdisc(dev, class_id):
+    tc.qdisc.add(dev, _FAIR_QDISC_KIND, parent=_ROOT_QDISC_HANDLE + class_id,
+                 handle=class_id + ':')
+
+
+def _add_hfsc_cls(dev, class_id, *qos_opts):
+    tc.cls.add(dev, 'hfsc', parent=_ROOT_QDISC_HANDLE,
+               classid=_ROOT_QDISC_HANDLE + class_id, **qos_opts)
+
+
+def _qos_to_str_dict(qos):
+    data = {}
+    for curve in qos:
+        data[curve] = ['m1', '%sbit' % curve.get('m1', '0'),
+                       'd', '%sus' % curve.get('d', '0'),
+                       'm2', '%sbit' % curve.get('m2', '0')]
+    return data
+
+
+def _root_qdisc(qdiscs):
+    for qdisc in qdiscs:
+        if 'root' in qdiscs:
+            return qdisc
diff --git a/vdsm/network/models.py b/vdsm/network/models.py
index 402a4b5..5ba6f41 100644
--- a/vdsm/network/models.py
+++ b/vdsm/network/models.py
@@ -111,6 +111,12 @@
     def __repr__(self):
         return 'Nic(%s)' % self.name
 
+    @property
+    def description(self):
+        """Returns a dictionary with information about the underlying hierarchy
+        of the device"""
+        return {'log_dev': self.name}
+
 
 class Vlan(NetDevice):
     MAX_ID = 4094
@@ -147,6 +153,14 @@
                                      'number between 0 and %s' %
                                      cls.MAX_ID)
 
+    @property
+    def description(self):
+        """Returns a dictionary with information about the underlying hierarchy
+        of the device"""
+        description = {'vlan_tag': self.vlan}
+        description.update(self.device.description)
+        return description
+
 
 class Bridge(NetDevice):
     '''This class represents traditional kernel bridges.'''
@@ -179,6 +193,12 @@
                 not name.startswith('-')):
             raise ConfigNetworkError(ne.ERR_BAD_BRIDGE,
                                      "Bridge name isn't valid: %r" % name)
+
+    @property
+    def description(self):
+        """Returns a dictionary with information about the underlying hierarchy
+        of the device"""
+        return self.port.description
 
 
 class Bond(NetDevice):
@@ -324,6 +344,12 @@
 
         return ' '.join((opt + '=' + val for (opt, val) in opts))
 
+    @property
+    def description(self):
+        """Returns a dictionary with information about the underlying hierarchy
+        of the device"""
+        return {'log_dev': self.name}
+
 
 class IPv4(object):
     def __init__(self, address=None, netmask=None, gateway=None,


-- 
To view, visit http://gerrit.ovirt.org/30467
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I4a5378870a3dac9307a6eef8d9937a9c03a0c819
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Antoni Segura Puimedon <[email protected]>
_______________________________________________
vdsm-patches mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches

Reply via email to