strizhechenko/netutils-linux

View on GitHub
netutils_linux_monitoring/network_top.py

Summary

Maintainability
A
0 mins
Test Coverage
#!/usr/bin/env python
# coding: utf-8

from six import iteritems, itervalues

from netutils_linux_monitoring import IrqTop, Softirqs, SoftnetStatTop, LinkRateTop
from netutils_linux_monitoring.base_top import BaseTop
from netutils_linux_monitoring.colors import Color
from netutils_linux_monitoring.layout import make_table
from netutils_linux_monitoring.topology import Topology


class NetworkTop(BaseTop):
    """ Global top-like util that combine information from 4 other tops """

    def __init__(self):
        BaseTop.__init__(self)
        self.tops = {
            'irqtop': IrqTop(),
            'softnet_stat_top': SoftnetStatTop(),
            'softirq_top': Softirqs(),
            'link-rate': LinkRateTop(),
        }
        self.parse_options()
        self.topology = Topology(fake=self.options.random, lscpu_output=self.options.lscpu_output)
        self.color = Color(self.topology, self.options.color)
        for top in self.tops.values():
            top.topology = self.topology
            top.color = self.color

    def parse(self):
        """
        :return: dict with parsed results for each top-like object.
        """
        return dict((top_name, _top.parse()) for top_name, _top in iteritems(self.tops))

    def eval(self):
        """
        :return: evaluates diff for each top-like object.
        """
        if all((self.current, self.previous)):
            self.diff = dict((top_name, _top.diff) for top_name, _top in iteritems(self.tops))

    def tick(self):
        self.previous = self.current
        self.current = self.parse()
        for _top in itervalues(self.tops):
            _top.tick()
        self.eval()

    def __repr__(self):
        output = [
            self.header,
            self.__repr_irq(),
            self.__repr_cpu(),
            self.__repr_dev(),
        ]
        if not self.options.clear:
            del output[0]
        return '\n'.join(output)

    def parse_options(self):
        """ Tricky way to gather all options in one util without conflicts, parse them and do some logic after parse """
        parser = BaseTop.make_base_parser()
        for top in itervalues(self.tops):
            parser = top.make_parser(parser)
        self.options = parser.parse_args()
        if self.options.lscpu_output:
            self.options.lscpu_output = open(self.options.lscpu_output).read()
        for top in itervalues(self.tops):
            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')
        table = make_table(top.make_header(), top.align_map, top.make_rows())
        return self.color.wrap_header('Network devices') + str(table)

    def __repr_irq(self):
        top = self.tops.get('irqtop')
        output_lines, cpu_count = top.make_rows()
        align_map = top.make_align_map(cpu_count)
        table = make_table(output_lines[0], align_map, output_lines[1:])
        return self.color.wrap_header('/proc/interrupts') + str(table)

    def __repr_cpu(self):
        irqtop = self.tops.get('irqtop')
        softirq_top = self.tops.get('softirq_top')
        softnet_stat_top = self.tops.get('softnet_stat_top')
        # all these evaluations are better to put in softirqs.parse()
        active_cpu = softirq_top.__active_cpu_count__(
            softirq_top.current)
        softnet_stat_top_output = softnet_stat_top.repr_source()
        network_output = zip(irqtop.diff_total,
                             softirq_top.repr_source()['NET_RX'][:active_cpu],
                             softirq_top.repr_source()['NET_TX'][:active_cpu],
                             softnet_stat_top_output)
        fields = [
            'CPU', 'Interrupts', 'NET RX', 'NET TX',
            '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)
        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):
        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),
                # Теперь я вспомнил зачем там были семантичные функции, чтобы не было дублирования
                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
        ]