Source code for linuxnet.qos.qdiscs.sfq
# Copyright (c) 2021, 2022, 2023, Panagiotis Tsirigotis
# This file is part of linuxnet-qos.
#
# linuxnet-qos is free software: you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public
# License as published by the Free Software Foundation.
#
# linuxnet-qos 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 Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General
# Public License along with linuxnet-qos. If not, see
# <https://www.gnu.org/licenses/>.
"""This module provides access to the SFQ queueing discipline
"""
from typing import List, Optional, TextIO
from ..exceptions import TcParsingError
from ..handle import Handle
from ..tcunit import unitstr2int, datastr2int
from ..parsers import QDiscParser
from .qdisc import QDisc, QStats
[docs]class SFQQDiscStats(QStats):
"""SFQDisc stats
"""
@property
def byte_backlog(self) -> int:
"""Backlog in bytes.
This value is not a statistic; it reflects the qdisc state at
the time the stats were obtained.
"""
return self.get_byte_backlog()
@property
def packet_backlog(self) -> int:
"""Backlog in packets.
This value is not a statistic; it reflects the qdisc state at
the time the stats were obtained.
"""
return self.get_byte_backlog()
[docs] def dump(self, outfile: TextIO, width: Optional[int] =None) -> None:
"""Dump stats to ``outfile``.
There is one stat per line output. Each line has the format::
header: value
The ``header:`` part occupies at least ``width`` characters.
"""
super().dump(outfile, width)
width = width or self.HEADER_WIDTH
print(f"{'Backlog:':{width}} "
f'{self.__byte_backlog} bytes / {self.__packet_backlog} packets',
file=outfile)
[docs]class SFQQDisc(QDisc):
"""This class provides access to the Stochastic Fairness Queueing
queueing discipline of Linux (see **tc-sfq(8)**).
"""
def __init__(self, qdisc_handle: Handle, parent_handle: Optional[Handle],
perturb: Optional[int] =None, quantum: Optional[int] =None):
"""
:param qdisc_handle: handle of this :class:`SFQQDisc`
:param parent_handle: handle of parent, ``None`` if this is a
root queuing discipline
:param perturb: as documented in **tc-sfq(8)**
:param quantum: as documented in **tc-sfq(8)**
"""
super().__init__(qdisc_handle, parent_handle)
self.__perturb = perturb # seconds
self.__quantum = quantum # bytes
self.__stats = None
def __str__(self):
return f"SFQQDisc({self.get_handle()})"
[docs] def get_description(self) -> str:
"""Returns a string describing the queuing discipline and
its attributes
"""
retval = super().get_description()
if self.__perturb is not None:
retval += f' perturb {self.__perturb:d}s'
if self.__quantum is not None:
retval += f' quantum {self.__quantum:d}B'
return retval
[docs] def get_perturb(self) -> Optional[int]:
"""Returns the perturb value (in seconds), or ``None``.
"""
return self.__perturb
[docs] def get_quantum(self) -> Optional[int]:
"""Returns the quantum value (in bytes), or ``None``.
"""
return self.__quantum
[docs] def get_stats(self) -> Optional[SFQQDiscStats]:
"""Returns the qdisc stats (an :class:`SFQQDiscStats` instance) or
``None`` if no stats are available.
"""
return self.__stats
[docs] def qdisc_creation_args(self) -> List[str]:
"""Returns the arguments expected by the **tc(8)** command to create
a SFQ qdisc
"""
args = ['sfq']
if self.__perturb is not None:
args += ['perturb', str(self.__perturb)]
if self.__quantum is not None:
args += ['quantum', str(self.__quantum)]
return args
def _parse_stats(self, line_group_iter) -> None:
"""Parse queuing stats.
"""
stats = SFQQDiscStats()
if stats.init_from_output(line_group_iter):
self.__stats = stats
@classmethod
def parse(cls, qdisc_output) -> 'SFQQDisc':
"""Create a :class:`SFQQDisc` object from the output of
the **tc(8)** command.
:meta private:
"""
field_iter = qdisc_output.get_field_iter()
#
# The fields are generated from a split of a line like this:
#
# qdisc sfq 101: parent 1:101 limit 127p quantum 1514b perturb 10sec
#
# The next field to be returned from field_iter is 'limit'
#
perturb = None
quantum = None
for field in field_iter:
if field == 'limit':
#
# 'limit' is not documented in tc-sfq(8), so we ignore it.
#
_ = next(field_iter)
elif field == 'perturb':
perturb = unitstr2int(next(field_iter), 'sec')
elif field == 'quantum':
quantum = datastr2int(next(field_iter))
elif field == 'depth':
_ = next(field_iter)
elif field == 'divisor':
_ = next(field_iter)
else:
raise TcParsingError(f"unknown argument '{field}'")
return SFQQDisc(qdisc_output.get_handle(),
qdisc_output.get_parent_handle(), perturb, quantum)
QDiscParser.register_qdisc('sfq', SFQQDisc)