Source code for linuxnet.qos.tcunit

# Copyright (c) 2021, 2022, 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 functions that can process the output of tc
"""

import enum
import re

from .exceptions import TcParsingError

_int_unit_expr = re.compile(r'(\d+)(.*)')
_float_unit_expr = re.compile(r'(\d+([.]\d+)?)(.*)')

[docs]def bwstr2int(bwstr: str) -> int: """Convert a string of the form * 500000bit * 500000Kbit * 500000Mbit to the corresponding number. The unit of the return value is bits/sec. """ match = _int_unit_expr.match(bwstr) if match is None: raise TcParsingError(f'not a valid bandwidth string: {bwstr}') numstr = match.group(1) unit = match.group(2) if unit == 'bit': multiplier = 1 elif unit == 'Kbit': multiplier = 1000 elif unit == 'Mbit': multiplier = 1000 * 1000 else: raise TcParsingError(f'unknown bandwidth unit: {unit}') return int(numstr) * multiplier
[docs]def datastr2int(datastr: str) -> int: """Convert a string of the form * 500000b * 500000Kb * 500000Mb to the corresponding number. 'b' stands for 'byte'. The unit of the return value is bytes. """ match = _int_unit_expr.match(datastr) if match is None: raise TcParsingError(f'not a valid data string: {datastr}') numstr = match.group(1) unit = match.group(2) if unit == 'b': multiplier = 1 elif unit == 'Kb': multiplier = 1000 elif unit == 'Mb': multiplier = 1000 * 1000 else: raise TcParsingError(f'unknown data unit: {unit}') return int(numstr) * multiplier
[docs]def timestr2float(timestr: str) -> float: """Convert a string of the form * 10s * 11.3ms * 201.3us to the corresponding number. The unit of the return value is always milliseconds. """ match = _float_unit_expr.match(timestr) if match is None: raise TcParsingError(f'not a valid time string: {timestr}') numstr = match.group(1) unit = match.group(3) if unit == 's': multiplier = 1000.0 elif unit == 'ms': multiplier = 1.0 elif unit == 'us': multiplier = 0.001 else: raise TcParsingError(f'unknown time unit: {unit}') return float(numstr) * multiplier
[docs]def unitstr2int(unitstr: str, suffix: str) -> int: """Convert a string with an expected suffix to a number, e.g. ``unitstr2int("20sec", "sec")`` returns ``20``. :param unitstr: a string of the form ``<num><suffix>`` :param suffix: expected suffix A :exc:`ValueError` will be raised if ``unitstr`` does not end in ``suffix``. """ if not unitstr.endswith(suffix): raise ValueError(f"missing suffix '{suffix}': " + unitstr) return int(unitstr[:-len(suffix)])
class _TcBandwidthUnit(enum.IntEnum): """This class uses units as reported by the tc(8) command. kbit means kilobit per second mbit means megabit per second bps means bytes per second """ # pylint: disable=invalid-name mbit = 1000 * 1000 kbit = 1000 bps = 8 # pylint: enable=invalid-name
[docs]def rate2str(rate: int) -> str: """Convert the specified rate to a string suitable as an argument to the **tc(8)** command, using the maximum unit size possible. For example:: >>> print(rate2str(128), rate2str(1024), rate2str(10000)) 16bps 128bps 10kbit >>> print(rate2str(1000000),rate2str(1500000)) 1mbit 1500kbit bps stands for bytes-per-second. """ for unit in list(_TcBandwidthUnit): n_units = rate // unit if n_units == 0: continue if rate % unit == 0: return f'{n_units}{unit.name}' # pylint: disable=consider-using-f-string return "%.3fbps" % (rate / 8)