Supporting a new queuing discipline¶
The following example illustrates how to add support for the pfifo
queuing discipline:
from linuxnet.qos.extension import (QDisc, unitstr2int,
QDiscParser, QDiscOutput)
class PFifoQDisc(QDisc):
"""The new Python class must be a child of QDisc
"""
def __init__(self, handle, parent_handle, packet_limit=None):
super().__init__(handle, parent_handle)
self.__packet_limit = packet_limit
def qdisc_creation_args(self):
"""This method is required; it should return the queuing
discipline-specific tc(8) arguments for creating this
queuing discipline
"""
args = ['pfifo']
if self.__packet_limit is not None:
args.extend(['limit', str(self.__packet_limit)])
return args
def get_description(self):
"""This method is optional but recommended; it returns a
description of the particular queuing discipline instantation
"""
retval = super().get_description()
if self.__packet_limit is not None:
retval += f' limit {self.__packet_limit} packets'
return retval
def get_packet_limit(self):
"""This is a pfifo-specific method
"""
return self.__packet_limit
@classmethod
def parse(cls, qdisc_output: QDiscOutput):
"""This method is required. It creates a PFifoQDisc instance
from the output of tc(8).
"""
field_iter = qdisc_output.get_field_iter()
packet_limit = None
for field in field_iter:
if field == 'limit':
limitstr = next(field_iter)
# limit value should have 'p' suffix
packet_limit = unitstr2int(limitstr, 'p')
return PFifoQDisc(qdisc_output.get_handle(),
qdisc_output.get_parent_handle(),
packet_limit)
#
# Register the new queueing discipline with the parser.
#
QDiscParser.register_qdisc('pfifo', PFifoQDisc)
The steps to add support for a new queuing discipline are:
Creation of a new Python class that inherits from
QDisc; the convention is to name such a classxxxQDiscwherexxxis the name of the queuing disciplineThe
xxxQDiscclass must implement theqdisc_creation_args()method; this method should return the queuing discipline-specific tc(8) arguments for creating the queuing disciplineThe
xxxQDiscclass must implement the class methodparse()to create anxxxQDiscinstance from a line of tc(8) output encapsulated in aQDiscOutputinstanceThe
xxxQDiscclass must be registered by invoking theQDiscParser.register_qdisc()method.The
xxxQDiscclass should implement the methodget_description()The
__init__()method ofxxxQDiscshould take as arguments the particular parameters of the new queuing disciplineThe
xxxQDiscclass should provide getter methods for the particular parameters of the queuing discipline
The argument to the xxxQDisc.parse() method is
a QDiscOutput instance which contains the unparsed portion of
the qdisc line and zero or more lines holding qdisc statistics.
For example:
qdisc sfq 103: parent 1:103 limit 127p quantum 1500b perturb 10sec
The line has already already been parsed up to and including the
parent 1:103 field; the rest of the line is specific to the
queuing discipline. The xxxQDisc.parse() method is responsible for
parsing those fields starting with limit. An iterator over those
fields can be obtained by invoking the QDiscOutput.get_field_iter()
method.
Supporting a classful queuing discipline¶
If the queuing discipline is classful then in addition to the creation of a Python class for the queuing discipline, a new Python class will also be needed to represent the queuing class. The steps are as follows:
Creation of a new Python class that inherits from
QClass; the convention is to name such a classxxxQClasswherexxxis the name of the queuing disciplineThe
xxxQClassclass must implement the methodqclass_creation_args(); this method should return the queuing class-specific tc(8) arguments for creating a class of the queuing discipline (example:HTBQClass.qclass_creation_args())The
xxxQDiscclass must implement the class methodparse()to create anxxxQClassinstance from a line of tc(8) output encapsulated in aQClassOutputinstanceThe
xxxQClassclass must be registered by invoking theQClassParser.register_qclass()method.The
xxxQClassclass should implement the methodget_description()(example:HTBQClass.get_description())The
__init__()method ofxxxQClassshould take as arguments the particular parameters of the queuing classThe
xxxQClassclass should provide getter methods for the particular parameters of the queuing class
The argument to the xxxQDisc.parse() method is
a QClassOutput instance which contains the unparsed portion of
the class line and zero or more lines holding class statistics.
For example:
class htb 1:1 root rate 2000Kbit ceil 2000Kbit burst 5000b cburst 5000b
The line has already already been parsed up to and including the
root field; the rest of the line is specific to the
queuing class. The xxxQClass.parse() method is responsible for
parsing those fields starting with rate. An iterator over those
fields can be obtained by invoking the QClassOutput.get_field_iter()
method.
If the queuing discipline is classful and its classes are
generated automatically upon instantiation in the kernel (e.g.
the PRIO queuing discipline), the
QDisc._instantiate_qdisc()
method must be
overriden to create the necessary subclasses of QClass upon
successful instantiation of the queuing discipline
(see PrioQDisc for an example).
Queuing statistics¶
If the new queuing discipline (or class) reports additional statistics, then a new class should be created to hold them. The steps needed are as follows:
Creation of a new Python class that inherits from
QStats; the convention is to name such a classxxxQDiscStats(for queuing discipline statistics) orxxxQClassStats(for queuing class statistics) wherexxxis the name of the queuing disciplinethe
xxxQDisc(orxxxQClass) class must implement theget_stats()method; this method should return an instance ofxxxQDiscStats(orxxxQClassStats)the
xxxQDisc(orxxxQClass) class must implement the_parse_stats()method; this method should create axxxQDiscStats(orxxxQClassStats) instance which should be stored in thexxxQDisc(orxxxQClass) instance. It should then invoke theinit_from_output()method of the newxxxQDiscStats(orxxxQClassStats) instanceThe argument to the
_parse_stats()method is aLineGroupIteratorwhich returns the output lines that contain the statistics. This iterator is passed to the invokedinit_from_output()method.the
xxxQDiscStats(orxxxQClassStats) class must implement theinit_from_output()method; this method should first invoke theQStats.init_from_output()method which will parse the output lines that contain the common queuing discipline statistics, and then it should proceed to parse the statistics lines that are specific to the new queuing discipline/classThe argument to this method is a
LineGroupIteratorinstance which provides access to the statistics lines.
Python Classes¶
QDisc¶
- class QDisc(handle: Handle, parent_handle: Optional[Handle])[source]¶
Bases:
QNodeBase class for all queueing discipline classes
- Parameters:
- _instantiate_qdisc(config) None[source]¶
Invoke the tc(8) command to create the queuing discipline described by this object.
- Parameters:
config – a
QDiscConfigobject
QClass¶
- class QClass(handle: Handle, parent_handle: Handle, class_name: Optional[str] = None)[source]¶
Bases:
QNodeUsed as a base class for all queuing discipline specific classes
A
QClassobject can either have a queuing discipline as a child, or it can have a set ofQClassobjects.- Parameters:
- dump(outfile: TextIO, level: int, qclass_map: Optional[Mapping[Handle, QClass]] = None)[source]¶
Recursively dump this
QClasstooutfile.The
qclass_map, if present, is used to determine the destinationQClassobjects of traffic filters.
- get_qdisc() Optional[QDisc][source]¶
Returns the
QDiscunder thisQClass; returnsNoneif there is noQDisc, or if this is not a leaf queuing class
QNode¶
- class QNode(handle: Handle, parent_handle: Optional[Handle])[source]¶
Used as a base class for the Python classes
QDiscandQClasswhich are the actual nodes of the traffic classification tree.A path from the root of the tree down to a leaf looks like this:
QDisc -> QClass+ -> QDisc
where the
QClass+ -> QDisccan repeat 0 or more times, and the finalQDiscmay not be present (pfifois implied)- Parameters:
- create_filter(traffic_filter: TrafficFilter) None[source]¶
Create the filter
traffic_filter.
- delete_filter(traffic_filter: TrafficFilter) None[source]¶
Delete the filter
traffic_filter.
- dump(outfile: TextIO, level: int, qclass_map: Mapping[Handle, QClass]) None[source]¶
Recursively dump this node and all its child classes to
outfile
- get_child_iter() Iterator[QClass][source]¶
Returns an iterator for the
QClasschildren of thisQDisc/QClass.
- get_config() QDiscConfig[source]¶
Returns the
QDiscConfigwhere thisQDisc/QClasshas been instantiated, orNoneif not instantiated.
- get_filters(refresh=False) List[TrafficFilter][source]¶
Returns (a copy of) the list of filters at this
QDisc/QClass- Parameters:
refresh – if
True, a fresh copy of the filter list is obtained using the tc(8) command.