diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f2ba8ed..0000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: python - -os: linux - -python: - - "2.7" - - "3.4" - - "3.6" - -before_install: - - gem install fpm - - gem install package_cloud - - sudo apt-get install rpm - -install: pip install --upgrade -r requirements.txt && python setup.py install - -script: - - make test - - make lint - - make rpm_upload diff --git a/BUGS.md b/BUGS.md index 304c7e8..a88e1c7 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,3 +1,4 @@ These "bugs" will never be fixed: - No `bridge` util in CentOS 6.4 (`iproute-2.6.32-23.el6.x86_64`). Solution: `yum -y install iproute` +- If you try to run it on CentOS 7 with python2.7 you may get tracebacks with YAML. Solution is to install dependencies via Yum: `yum install PyYAML.x86_64 python2-pyyaml.noarch python-prettytable.noarch` diff --git a/README.rst b/README.rst index dece638..ada66ab 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,6 @@ netutils-linux ============== -.. |travis| image:: https://travis-ci.org/strizhechenko/netutils-linux.svg?branch=master - :target: https://travis-ci.org/strizhechenko/netutils-linux -.. |landscape| image:: https://landscape.io/github/strizhechenko/netutils-linux/master/landscape.svg?style=flat - :target: https://landscape.io/github/strizhechenko/netutils-linux/master .. |pypi| image:: https://badge.fury.io/py/netutils-linux.svg :target: https://badge.fury.io/py/netutils-linux .. |license| image:: https://img.shields.io/badge/License-MIT-yellow.svg?colorB=green @@ -16,9 +12,14 @@ netutils-linux .. |codeclimate| image:: https://img.shields.io/codeclimate/github/strizhechenko/netutils-linux.svg :target: https://codeclimate.com/github/strizhechenko/netutils-linux -|travis| |landscape| |pypi| |license| |pyversions| |codeclimate| |issues| +|pypi| |license| |pyversions| |codeclimate| |issues| -It's a useful utils to simplify Linux network troubleshooting and performance tuning, developed in order to help `Carbon Reductor`_ techsupport and automate the whole linux performance tuning process out of box (ok, except the best RSS layout detection with multiple network devices). These utils may be useful for datacenters and internet service providers with heavy network workload (you probably wouldn't see an effect at your desktop computer). It's now in production usage with 300+ deployment and save us a lot of time with hardware and software settings debugging. Inspired by `packagecloud's blog post`_. +Project is frozen +================= + +It's in licensing and intellectual property gray zone - previous employeer and me talked about it, but didn't fixed anything "on paper". I will not upgrade project, receive any patches, etc. Feel free to fork for own usage, drop py2 support, etc. + +It's a useful utils to simplify Linux network troubleshooting and performance tuning, developed in order to help `Carbon Reductor`_ techsupport and automate the whole linux performance tuning process out of box (ok, except the best RSS layout detection with multiple network devices). These utils may be useful for datacenters and internet service providers with heavy network workload (you probably wouldn't see an effect at your desktop computer). It's now in production usage with 2000+ deployment and save us a lot of time with hardware and software settings debugging. Inspired by `packagecloud's blog post`_. .. _packagecloud's blog post: https://blog.packagecloud.io/eng/2016/06/22/monitoring-tuning-linux-networking-stack-receiving-data/ .. _Carbon Reductor: http://www.carbonsoft.ru/products/carbon-reductor-5/ @@ -312,15 +313,21 @@ Device's detailed rating Hypervisor vendor: 1 Virtualization type: 1 +FAQ +=== + +Q: I see that workload is distributed fine, but there is a lot of workload. How to go deeper, how to understand what my system doing right now? + +A: Try + +.. code:: shell + + perf top + + How to contribute? ================== Close issues ------------ Any help is welcome. Just comment an issue with "I want to help, how can I solve this issue?" to start. - -or donate ---------- -- https://patreon.com/strizhechenko -- https://rocketbank.ru/oleg.strizhechenko -- https://paypal.me/weirded diff --git a/netutils_linux_hardware/disk.py b/netutils_linux_hardware/disk.py index ad86df3..d6d0c8c 100644 --- a/netutils_linux_hardware/disk.py +++ b/netutils_linux_hardware/disk.py @@ -68,7 +68,8 @@ def parse(text): types = ['SSD', 'HDD'] if not text: return dict() - data = yaml.load(text.replace(':', ': ').replace('/sys/block/', '').replace('/queue/rotational', '')) + _text = text.replace(':', ': ').replace('/sys/block/', '').replace('/queue/rotational', '') + data = yaml.load(_text, yaml.loader.SafeLoader) return dict((k, types[v]) for k, v in data.items()) class DiskSizeInfo(Parser): diff --git a/netutils_linux_hardware/memory.py b/netutils_linux_hardware/memory.py index 20c5af9..14ba619 100644 --- a/netutils_linux_hardware/memory.py +++ b/netutils_linux_hardware/memory.py @@ -59,7 +59,8 @@ class MemInfo(YAMLLike): ) def parse(self, text): - return dict((k, int(v.replace(' kB', ''))) for k, v in iteritems(yaml.load(text)) if k in self.keys_required) + data = yaml.load(text, yaml.loader.SafeLoader) + return dict((k, int(v.replace(' kB', ''))) for k, v in iteritems(data) if k in self.keys_required) class MemInfoDMIDevice(object): diff --git a/netutils_linux_hardware/parser.py b/netutils_linux_hardware/parser.py index d7ca3db..7f44de1 100644 --- a/netutils_linux_hardware/parser.py +++ b/netutils_linux_hardware/parser.py @@ -25,4 +25,4 @@ def parse_file_safe(self, filepath, **kwargs): class YAMLLike(Parser): @staticmethod def parse(text): - return yaml.load(text) + return yaml.load(text, yaml.loader.SafeLoader) diff --git a/netutils_linux_monitoring/base_top.py b/netutils_linux_monitoring/base_top.py index ee0ae16..e062aac 100644 --- a/netutils_linux_monitoring/base_top.py +++ b/netutils_linux_monitoring/base_top.py @@ -16,7 +16,7 @@ class BaseTop(object): current = None previous = None diff = None - header = Color.wrap('Press CTRL-C to exit...\n', Color.GREY) + header = None options = None file_arg = None file_value = None @@ -113,18 +113,19 @@ def spaces(self, number, sep=' '): def __repr_table__(self, table): if self.options.clear: - return BaseTop.header + str(table) + return self.header + str(table) return str(table) def default_init(self, topology=None): BaseTop.__init__(self) self.topology = topology - self.color = Color(self.topology) + # self.color = Color(self.topology, self.options.color) def default_post_optparse(self): if not self.topology: self.topology = Topology(fake=self.options.random) self.color = Color(self.topology, self.options.color) + self.header = self.color.wrap('Press CTRL-C to exit...\n', self.color.GREY) @abstractmethod def parse(self): diff --git a/netutils_linux_monitoring/colors.py b/netutils_linux_monitoring/colors.py index b0d8999..e40a809 100644 --- a/netutils_linux_monitoring/colors.py +++ b/netutils_linux_monitoring/colors.py @@ -6,23 +6,23 @@ class Color(object): """ Helper for highlighting a console tools """ try: - YELLOW = Fore.LIGHTYELLOW_EX - GREY = Fore.LIGHTBLACK_EX + CLS_YELLOW = Fore.LIGHTYELLOW_EX + CLS_GREY = Fore.LIGHTBLACK_EX except AttributeError: - YELLOW = Fore.YELLOW - GREY = Fore.CYAN + CLS_YELLOW = Fore.YELLOW + CLS_GREY = Fore.CYAN COLORS_NODE = { 0: Fore.GREEN, 1: Fore.RED, - 2: YELLOW, + 2: CLS_YELLOW, 3: Fore.BLUE, -1: Style.RESET_ALL, } COLORS_SOCKET = { 0: Fore.BLUE, - 1: YELLOW, + 1: CLS_YELLOW, 2: Fore.RED, 3: Fore.GREEN, -1: Style.RESET_ALL, @@ -30,40 +30,35 @@ class Color(object): COLOR_NONE = dict((key, "") for key in range(-1, 4)) - def __init__(self, topology, enabled=True): - self.enabled = enabled - self.topology = topology - if topology is not None: - self.color_scheme = self.__choose_color_scheme() + def __init__(self, topology, enabled): + self.enabled, self.topology = enabled, topology + self.RESET, self.RESET_ALL = Fore.RESET, Style.RESET_ALL + self.YELLOW, self.GREY = Color.CLS_YELLOW, Color.CLS_GREY + self.RED, self.BRIGHT = Fore.RED, Style.BRIGHT + self.color_scheme = self.__choose_color_scheme() def __choose_color_scheme(self): - if not self.enabled: - Style.BRIGHT = Style.RESET_ALL = Fore.RED = Fore.RESET = self.GREY = self.YELLOW = "" + if not self.enabled or not self.topology: + self.BRIGHT = self.RESET_ALL = self.RED = self.RESET = self.GREY = self.YELLOW = "" return self.COLOR_NONE if self.topology.layout_kind == 'NUMA': return self.COLORS_NODE return self.COLORS_SOCKET - @staticmethod - def wrap(word, color): + def wrap(self, word, color): """ wrap string in given color """ - return '{0}{1}{2}'.format(color, word, Style.RESET_ALL) + return '{0}{1}{2}'.format(color, word, self.RESET_ALL) - @staticmethod - def colorize(value, warning, error): - return Color.wrap(value, Fore.RED if value >= error else Color.YELLOW if value >= warning else Fore.RESET) + def colorize(self, value, warning, error): + return self.wrap(value, self.RED if value >= error else self.YELLOW if value >= warning else self.RESET) - @staticmethod - def bright(string): - return Color.wrap(string, Style.BRIGHT) + def bright(self, string): + return self.wrap(string, self.BRIGHT) - @staticmethod - def wrap_header(string): - return Color.wrap("# {0}\n".format(string), Style.BRIGHT) + def wrap_header(self, string): + return self.wrap("# {0}\n".format(string), self.BRIGHT) def colorize_cpu(self, cpu): - if not self.color_scheme: - self.color_scheme = self.__choose_color_scheme() if isinstance(cpu, str): cpu = int(cpu[3:]) return self.color_scheme.get(self.topology.layout.get(cpu)) diff --git a/netutils_linux_monitoring/irqtop.py b/netutils_linux_monitoring/irqtop.py index a2904e9..c22c46c 100644 --- a/netutils_linux_monitoring/irqtop.py +++ b/netutils_linux_monitoring/irqtop.py @@ -6,7 +6,6 @@ from six.moves import xrange from netutils_linux_monitoring.base_top import BaseTop -from netutils_linux_monitoring.colors import Color from netutils_linux_monitoring.layout import make_table @@ -59,10 +58,9 @@ def make_rows(self): output_lines.append(line) return output_lines, cpu_count - @staticmethod - def colorize_irq_per_cpu(irq_per_cpu): + def colorize_irq_per_cpu(self, irq_per_cpu): """ :returns: highlighted by warning/error irq string """ - return Color.colorize(irq_per_cpu, 40000, 80000) + return self.color.colorize(irq_per_cpu, 40000, 80000) def __repr__(self): output_lines, cpu_count = self.make_rows() diff --git a/netutils_linux_monitoring/link_rate.py b/netutils_linux_monitoring/link_rate.py index a94bbd0..880825b 100644 --- a/netutils_linux_monitoring/link_rate.py +++ b/netutils_linux_monitoring/link_rate.py @@ -73,9 +73,8 @@ def eval(self): def make_header(self): return ['Device'] + [stat.shortname for stat in self.stats] - @staticmethod - def colorize_stat(stat, value): - return Color.colorize(value, 1, 1) if 'errors' in stat.filename or 'dropped' in stat.filename else value + def colorize_stat(self, stat, value): + return self.color.colorize(value, 1, 1) if 'errors' in stat.filename or 'dropped' in stat.filename else value def colorize_stats(self, dev, repr_source): return [self.colorize_stat(stat, repr_source[dev][stat]) for stat in self.stats] @@ -86,7 +85,7 @@ def make_rows(self): repr_source = self.repr_source() for dev in self.options.devices: dev_node = self.pci.devices.get(dev) - dev_color = self.color.COLORS_NODE.get(dev_node) + dev_color = self.color.COLORS_NODE.get(dev_node) if self.options.color else "" _dev = self.color.wrap(dev, dev_color) yield [_dev] + self.colorize_stats(dev, repr_source) diff --git a/netutils_linux_monitoring/network_top.py b/netutils_linux_monitoring/network_top.py index cecf246..05c9388 100644 --- a/netutils_linux_monitoring/network_top.py +++ b/netutils_linux_monitoring/network_top.py @@ -23,7 +23,7 @@ def __init__(self): } self.parse_options() self.topology = Topology(fake=self.options.random, lscpu_output=self.options.lscpu_output) - self.color = Color(self.topology) + self.color = Color(self.topology, self.options.color) for top in self.tops.values(): top.topology = self.topology top.color = self.color @@ -50,7 +50,7 @@ def tick(self): def __repr__(self): output = [ - BaseTop.header, + self.header, self.__repr_irq(), self.__repr_cpu(), self.__repr_dev(), @@ -71,6 +71,7 @@ def parse_options(self): top.options = self.options if hasattr(top, 'post_optparse'): top.post_optparse() + self.default_post_optparse() def __repr_dev(self): top = self.tops.get('link-rate') @@ -101,21 +102,22 @@ def __repr_cpu(self): 'total', 'dropped', 'time_squeeze', 'cpu_collision', 'received_rps', ] fields = [self.color.bright(word) for word in fields] - rows = self.__repr_cpu_make_rows(irqtop, network_output, softirq_top, softnet_stat_top) + rows = self.__repr_cpu_make_rows(irqtop, network_output, softirq_top) table = make_table(fields, ['l'] + ['r'] * (len(fields) - 1), rows) return self.color.wrap_header('Load per cpu:') + str(table) - def __repr_cpu_make_rows(self, irqtop, network_output, softirq_top, softnet_stat_top): + def __repr_cpu_make_rows(self, irqtop, network_output, softirq_top): return [ [ self.color.wrap('CPU{0}'.format(stat.cpu), self.color.colorize_cpu(stat.cpu)), irqtop.colorize_irq_per_cpu(irq), softirq_top.colorize_net_rx(net_rx), softirq_top.colorize_net_tx(net_tx), - softnet_stat_top.colorize_total(stat.total), - softnet_stat_top.colorize_dropped(stat.dropped), - softnet_stat_top.colorize_time_squeeze(stat.time_squeeze), - softnet_stat_top.colorize_cpu_collision(stat.cpu_collision), + # Теперь я вспомнил зачем там были семантичные функции, чтобы не было дублирования + self.color.colorize(stat.total, 300000, 900000), + self.color.colorize(stat.dropped, 1, 1), + self.color.colorize(stat.time_squeeze, 1, 300), + self.color.colorize(stat.cpu_collision, 1, 1000), stat.received_rps ] for irq, net_rx, net_tx, stat in network_output diff --git a/netutils_linux_monitoring/softirqs.py b/netutils_linux_monitoring/softirqs.py index 6c0d51b..9b99922 100644 --- a/netutils_linux_monitoring/softirqs.py +++ b/netutils_linux_monitoring/softirqs.py @@ -2,7 +2,6 @@ from six import iteritems from netutils_linux_monitoring.base_top import BaseTop -from netutils_linux_monitoring.colors import Color from netutils_linux_monitoring.layout import make_table @@ -41,15 +40,13 @@ def __repr__(self): table = make_table(header, ['l', 'r', 'r'], rows) return self.__repr_table__(table) - @staticmethod - def colorize_net_rx(net_rx): + def colorize_net_rx(self, net_rx): """ :returns: highlighted by warning/error net_rx string """ - return Color.colorize(net_rx, 40000, 80000) + return self.color.colorize(net_rx, 40000, 80000) - @staticmethod - def colorize_net_tx(net_tx): + def colorize_net_tx(self, net_tx): """ :returns: highlighted by warning/error net_tx string """ - return Color.colorize(net_tx, 20000, 30000) + return self.color.colorize(net_tx, 20000, 30000) @staticmethod def __active_cpu_count__(data): diff --git a/netutils_linux_monitoring/softnet_stat.py b/netutils_linux_monitoring/softnet_stat.py index dceea35..56519cc 100644 --- a/netutils_linux_monitoring/softnet_stat.py +++ b/netutils_linux_monitoring/softnet_stat.py @@ -2,7 +2,6 @@ from random import randint from netutils_linux_monitoring.base_top import BaseTop -from netutils_linux_monitoring.colors import Color from netutils_linux_monitoring.layout import make_table @@ -84,29 +83,9 @@ def make_header(): def make_rows(self): return [[ self.color.wrap('CPU{0}'.format(stat.cpu), self.color.colorize_cpu(stat.cpu)), - self.colorize_total(stat.total), - self.colorize_dropped(stat.dropped), - self.colorize_time_squeeze(stat.time_squeeze), - self.colorize_cpu_collision(stat.cpu_collision), + self.color.colorize(stat.total, 300000, 900000), + self.color.colorize(stat.dropped, 1, 1), + self.color.colorize(stat.time_squeeze, 1, 300), + self.color.colorize(stat.cpu_collision, 1, 1000), stat.received_rps ] for stat in self.repr_source()] - - @staticmethod - def colorize_total(total): - """ :returns: highlighted by warning/error total string """ - return Color.colorize(total, 300000, 900000) - - @staticmethod - def colorize_dropped(dropped): - """ :returns: highlighted by warning/error dropped string """ - return Color.colorize(dropped, 1, 1) - - @staticmethod - def colorize_time_squeeze(time_squeeze): - """ :returns: highlighted by warning/error time_squeeze string """ - return Color.colorize(time_squeeze, 1, 300) - - @staticmethod - def colorize_cpu_collision(cpu_collision): - """ :returns: highlighted by warning/error cpu_collision string """ - return Color.colorize(cpu_collision, 1, 1000) diff --git a/netutils_linux_tuning/rss_ladder.py b/netutils_linux_tuning/rss_ladder.py index 27a8f7c..e8dd2d5 100644 --- a/netutils_linux_tuning/rss_ladder.py +++ b/netutils_linux_tuning/rss_ladder.py @@ -65,7 +65,7 @@ def apply(self, decision): if len(set(cpus)) != len(cpus): warning = 'WARNING: some CPUs process multiple queues, consider reduce queue count for this network device' if self.options.color: - print_(self.color.wrap(warning, Color.YELLOW)) + print_(self.color.wrap(warning, self.color.YELLOW)) else: print_(warning) for irq, queue_name, socket_cpu in affinity: diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..890ad25 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,6 @@ +pytest +flake8 +collective.checkdocs +coverage +pytest-cov +twine diff --git a/requirements.txt b/requirements.txt index a0633f3..926cf34 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,7 @@ prettytable -setuptools pyyaml +setuptools ipaddress six colorama -flake8 -pytest -collective.checkdocs -Pygments argparse -coverage -pytest-cov -twine diff --git a/setup.py b/setup.py index 6de94db..2dd61b2 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ def read(*paths): setuptools.setup( name='netutils-linux', - version='2.7.9', + version='2.8.0', author='Oleg Strizhechenko', author_email='oleg.strizhechenko@gmail.com', license='MIT', @@ -24,11 +24,13 @@ def read(*paths): keywords='linux network performanse utils troubleshooting irq interrupts softirqs proc', description='Bunch of utils to simplify linux network troubleshooting and performance tuning.', long_description=(read('README.rst')), + long_description_content_type='text/x-rst', packages=setuptools.find_packages(exclude=['tests*']), scripts=[os.path.join('utils/', script) for script in os.listdir('utils/')], install_requires=['pyyaml', 'ipaddress', 'six', 'colorama', 'prettytable', 'argparse'], classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', 'Intended Audience :: Developers', 'Operating System :: MacOS', 'Operating System :: POSIX', @@ -38,6 +40,8 @@ def read(*paths): 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Software Development', 'Topic :: Utilities', ], diff --git a/utils/autotune-reductor b/utils/autotune-reductor index 0ed2ca3..bf11761 100755 --- a/utils/autotune-reductor +++ b/utils/autotune-reductor @@ -1,24 +1,48 @@ -#!/usr/bin/env bash +#!/bin/bash -set -euo pipefail +set -euE + +if [ -f /usr/local/Reductor/etc/const ]; then + . /usr/local/Reductor/etc/const +elif [ -f /app/reductor/usr/local/Reductor/etc/const ]; then + . /app/reductor/usr/local/Reductor/etc/const +else + echo "$0 $@ [$$] FAILURE no etc/const" >&2 + exit 1 +fi + +echo "$0 $@ [$$] START" >&2 + + +if [ "${1:-}" = "--help" ]; then + echo "Info: автоматические настраивает сетевые карты для приёма зеркала" + echo "Usage: $0 без аргументов" + echo "Example: $0 без аргументов" + exit 0 +fi find_mirror_conf() { - local CR7=/usr/local/Reductor/userinfo/mirror_info.conf - local CR8_INNER=/cfg/userinfo/mirror_info.conf - local CR8_OUTER=/app/reductor/$CR8_INNER + set -e + local cr7="/usr/local/Reductor/userinfo/mirror_info.conf" + local cr8_inner="/cfg/userinfo/mirror_info.conf" + local cr8_outer="/app/reductor/$cr8_inner" local conf - for conf in $CR7 $CR8_INNER $CR8_OUTER; do - [ -f $conf ] || continue - echo $conf + for conf in "$cr7" "$cr8_inner" "$cr8_outer"; do + [ ! -f "$conf" ] && continue + echo "$conf" break done + return 0 } mirror_devices_bridge() { - brctl show | tail -n +2 | awk '{print $4}' + # skip strongbash034 + brctl show | tail -n +2 | awk '{print $4}' | cut -d '.' -f1 | sort -u + return 0 } mirror_devices() { + set -e local mirror_conf mirror_conf="$(find_mirror_conf)" if [ -n "${mirror_conf:-}" ]; then @@ -26,14 +50,39 @@ mirror_devices() { else mirror_devices_bridge fi + return 0 } main() { - maximize-cpu-freq + local device + # virtualenv нужен только для CR7 + if [ ! -f /cfg/config ]; then + if [ ! -f ${VENVBIN}/activate ]; then + echo "$0 $@ [$$] FAILURE no virtualenv" >&2 + exit 2 + fi + set +eu + . ${VENVBIN}/activate + set -eu + fi + maximize-cpu-freq || true + . "$CONFIG" for device in $(mirror_devices); do + echo "$0 $@ [$$] Настройка $device" >&2 autorps "$device" || true + if [ "${misc['rss']:-0}" = '1' ]; then + rss-ladder "$device" || true + fi rx-buffers-increase "$device" || true done + if [ ! -f /cfg/config ]; then + set +eu + deactivate + set -eu + fi + return 0 } main "$@" +echo "$0 $@ [$$] SUCCESS" >&2 +exit 0 diff --git a/utils/maximize-cpu-freq b/utils/maximize-cpu-freq index 18458de..f2e4e4a 100755 --- a/utils/maximize-cpu-freq +++ b/utils/maximize-cpu-freq @@ -52,7 +52,7 @@ maximize_freq() { main() { local cpu parse_params "$@" - for cpu in $(find /sys/devices/system/cpu/ -maxdepth 1 -type d | egrep 'cpu[0-9]+'); do + for cpu in $(find /sys/devices/system/cpu/ -maxdepth 1 -type d -name "cpu[0-9]*" | sort -V); do set_governor_performance "$cpu" maximize_freq "$cpu" done diff --git a/utils/netdev-model-list b/utils/netdev-model-list new file mode 100755 index 0000000..df4eee2 --- /dev/null +++ b/utils/netdev-model-list @@ -0,0 +1,51 @@ +#!/bin/bash + +set -eu +# echo "$0 $@ [$$] START" >&2 +declare DEVICE_REGEX="(eth|em|en|bond|p[0-9]*p)[0-9\.]+" + +if [ "{1:-}" == '--help' ]; then + echo "Info: $0 показывает модели сетевых карт в системе" + echo "Usage: $0 без параметров" + echo "Example: $0" + exit 0 +fi + +rx_buf() { + set -e + # skip strongbash034 + ethtool -g "$1" 2>/dev/null | tac | grep "RX:" | egrep -o "[0-9]+" | tr '\n' '/'| sed 's|/$||g' + return 0 +} + +show_device() { + local eth="$1" + local id="$2" + local is_vlan=0 + local output + output="$(ethtool -i "$eth")" + grep -q '802.1Q VLAN Support' <<< "$output" && is_vlan=1 + if [ "$is_vlan" = 0 ]; then + echo "$eth: $(grep -w "$id" /tmp/lspci.tmp) rx: $(rx_buf "$eth")" + else + echo "$eth: VLAN interface" + fi + return 0 +} + +main() { + local eth + local id + for eth in $(egrep -o "$DEVICE_REGEX" /proc/net/dev | sort -u || true); do + for id in $(ethtool -i "$eth" | grep bus-info | sed 's/.*0000://' || true); do + show_device "$eth" "$id" + done + done + return 0 +} + +lspci > /tmp/lspci.tmp +main + +# echo "$0 $@ [$$] SUCCESS" +exit 0