def make_rows(self): repr_source = self.repr_source() for dev in self.options.devices: dev_node = self.numa.devices.get(dev) dev_color = ColorsNode.get(dev_node) _dev = wrap(dev, dev_color) yield [_dev] + self.colorize_stats(dev, repr_source)
def cpu_colorize(self, cpu): """ :param cpu: cpu number (0) :return: highlighted by NUMA-node cpu number. """ if not self.topology or not self.options.color: return cpu return wrap(cpu, cpu_color(cpu, topology=self.topology))
def dev_colorize(self): """ :return: highlighted by NUMA-node name of the device """ if not self.pci or not self.options.color: return self.options.dev color = COLORS_NODE.get(self.pci.devices.get(self.options.dev)) return wrap(self.options.dev, color)
def make_rows(self): return [[ wrap("CPU{0}".format(stat.cpu), cpu_color(stat.cpu, self.topology)), self.colorize_total(stat.total), self.colorize_dropped(stat.dropped), self.colorize_time_squeeze(stat.time_squeeze), self.colorize_cpu_collision(stat.cpu_collision), stat.received_rps ] for stat in self.repr_source()]
def make_rows(self): if not self.numa.devices: self.numa.devices = self.numa.node_dev_dict(self.options.devices, self.options.random) repr_source = self.repr_source() for dev in self.options.devices: dev_node = self.numa.devices.get(dev) dev_color = COLORS_NODE.get(dev_node) _dev = wrap(dev, dev_color) yield [_dev] + self.colorize_stats(dev, repr_source)
def __repr__(self): active_cpu_count = self.__active_cpu_count__(self.current) header = ["CPU", "NET_RX", "NET_TX"] net_rx = self.repr_source().get('NET_RX')[:active_cpu_count] net_tx = self.repr_source().get('NET_TX')[:active_cpu_count] rows = [[ wrap("CPU{0}".format(n), cpu_color(n, self.numa)), v[0], v[1] ] for n, v in enumerate(zip(net_rx, net_tx))] table = make_table(header, ['l', 'r', 'r'], rows) return self.__repr_table__(table)
def make_rows(self): return [[ wrap("CPU{0}".format(stat.cpu), cpu_color(stat.cpu, self.numa)), colorize(stat.total, self.total_warning, self.total_error), colorize(stat.dropped, self.dropped_warning, self.dropped_error), colorize(stat.time_squeeze, self.time_squeeze_warning, self.time_squeeze_error), colorize(stat.cpu_collision, self.cpu_collision_warning, self.cpu_collision_error), stat.received_rps ] for stat in self.repr_source()]
def __repr__(self): active_cpu_count = self.__active_cpu_count__(self.current) header = ['CPU', 'NET_RX', 'NET_TX'] net_rx = self.repr_source().get('NET_RX')[:active_cpu_count] net_tx = self.repr_source().get('NET_TX')[:active_cpu_count] rows = [[ wrap('CPU{0}'.format(n), cpu_color(n, self.topology)), self.colorize_net_rx(v[0]), self.colorize_net_tx(v[1]) ] for n, v in enumerate(zip(net_rx, net_tx))] table = make_table(header, ['l', 'r', 'r'], rows) return self.__repr_table__(table)
def __repr_cpu_make_rows(self, irqtop, network_output, softirq_top, softnet_stat_top): return [[ wrap("CPU{0}".format(stat.cpu), cpu_color(stat.cpu, self.topology)), 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), stat.received_rps ] for irq, net_rx, net_tx, stat in network_output]
def __repr_cpu_make_rows(self, irqtop, network_output, softirq_top, softnet_stat_top): rows = [ [ wrap("CPU{0}".format(stat.cpu), cpu_color(stat.cpu, self.numa)), colorize(irq, irqtop.irq_warning, irqtop.irq_error), colorize(softirq_rx, softirq_top.net_rx_warning, softirq_top.net_rx_error), colorize(softirq_tx, softirq_top.net_tx_warning, softirq_top.net_tx_error), colorize(stat.total, softnet_stat_top.total_warning, softnet_stat_top.total_error), colorize(stat.dropped, softnet_stat_top.dropped_warning, softnet_stat_top.dropped_error), colorize(stat.time_squeeze, softnet_stat_top.time_squeeze_warning, softnet_stat_top.time_squeeze_error), colorize(stat.cpu_collision, softnet_stat_top.cpu_collision_warning, softnet_stat_top.cpu_collision_error), stat.received_rps ] for irq, softirq_rx, softirq_tx, stat in network_output ] return rows
def apply(self, decision): """ '* 4' is in case of NIC has more queues than socket has CPUs :param decision: list of tuples(irq, queue_name, socket) """ affinity = list(decision) cpus = [socket_cpu for irq, queue, socket_cpu in affinity] 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_(wrap(warning, YELLOW)) else: print_(warning) for irq, queue_name, socket_cpu in affinity: print_(" - {0}: irq {1} {2} -> {3}".format( self.dev_colorize(), irq, queue_name, self.cpu_colorize(socket_cpu))) if self.options.dry_run: continue filename = "/proc/irq/{0}/smp_affinity_list".format(irq) with open(filename, 'w') as irq_file: irq_file.write(str(socket_cpu))
class BaseTop(object): """ Base class for all these top-like utils. """ current = None previous = None diff = None header = wrap("Press CTRL-C to exit...\n", Fore.LIGHTBLACK_EX) options = None def __init__(self): """ Base __init__() should only list common options that will be used in all specific top-like utils. All specific top-like utils should extend, but NOT OVERRIDE self.specific_options. """ self.specific_options = [ Option('-i', '--interval', default=1, type=int, help='Interval between screen renew in seconds.'), Option('-n', '--iterations', dest='iterations', default=60, type=int, help="Count of screen's renews, -1 - infinite loop."), Option('--no-delta-mode', action='store_false', dest='delta_mode', default=True, help="Shows metrics' values instead of growth."), Option('--no-delta-small-hide', action='store_false', dest='delta_small_hide', default=True, help="Prevent lines with only small changes or without" "changes at all from hiding."), Option('-l', '--delta-small-hide-limit', default=80, type=int, help='Hides lines with only changes less than this limit'), Option('--no-color', dest='color', default=True, action='store_false', help="Don't highlight NUMA nodes or sockets"), Option( '--spaces', default=False, action='store_true', help="Add spaces in numbers' representation, e.g. '1234567' " "will be '1 234 567'"), Option('--random', default=False, action='store_true', help="Shows random diff data instead of real evaluation. " "Helpful for testing on static files"), Option( '--no-clear', default=True, dest='clear', action='store_false', help= "Don't clear screen after each iteration. May be useful in scripts/logging to file." ), ] def parse_options(self, options=None): """ That should be explicitly called in __main__ part of any top-like utils """ parser = OptionParser() for opt in self.specific_options: try: parser.add_option(opt) except OptionConflictError: pass self.options, _ = parser.parse_args() if options: for name, value in iteritems(options): setattr(self.options, name, value) if hasattr(self, 'post_optparse'): # pylint: disable=E1101 self.post_optparse() def tick(self): """ Gathers new data + evaluate diff between current & previous data """ self.previous = self.current self.current = self.parse() if all((self.previous, self.current)): self.eval() def list_diff(self, current, previous): """ It's strange that there is no [3,4,3] - [1,2,1] -> [2,2,2] in standard library """ if self.options.random: return [randint(0, 10000) for _ in current] return [data - previous[n] for n, data in enumerate(current)] def run(self): """ Default main()-like function for specific top-like utils except meta-utils. """ infinite = -1 if self.options.iterations != infinite: self.options.iterations += 1 try: while self.options.iterations > 0 or self.options.iterations == infinite: if self.options.iterations != infinite: self.options.iterations -= 1 sleep(self.options.interval) self.tick() if self.options.clear: system('clear') if self.diff: print_(self) except KeyboardInterrupt: print_() exit(0) def repr_source(self): return self.diff if self.options.delta_mode else self.current @staticmethod def int(item): return int(item) if item.isdigit() else item def spaces(self, number, sep=' '): """ 1234567 -> 1 234 567 """ if not self.options.spaces: return number output = str() while number / 1000 > 0: output = str(number % 1000).zfill(3) + sep + output number /= 1000 return (str(number % 1000) + sep + output).strip() def __repr_table__(self, table): if self.options.clear: return BaseTop.header + str(table) return str(table) def parse(self): """ Should read some file(s) into python structure (dict/list) """ raise NotImplementedError def eval(self): """ Should evaluate self.diff using self.previous / self.current """ raise NotImplementedError def __repr__(self): """ Should return string, representing self.diff """ raise NotImplementedError
class BaseTop(object): """ Base class for all these top-like utils. """ current = None previous = None diff = None header = wrap('Press CTRL-C to exit...\n', GREY) options = None file_arg = None file_value = None @staticmethod def make_base_parser(parser=None): """ That should be explicitly called in __main__ part of any top-like utils """ if not parser: parser = argparse.ArgumentParser() parser.add_argument('-i', '--interval', default=1, type=int, help='Interval between screen renew in seconds.') parser.add_argument('-n', '--iterations', dest='iterations', default=60, type=int, help='Count of screen\'s renews, -1 - infinite loop.') parser.add_argument('--no-delta-mode', action='store_false', dest='delta_mode', default=True, help="Shows metrics' values instead of growth.") parser.add_argument('--no-delta-small-hide', action='store_false', dest='delta_small_hide', default=True, help='Prevent lines with only small changes or without' 'changes at all from hiding.') parser.add_argument('-l', '--delta-small-hide-limit', default=80, type=int, help='Hides lines with only changes less than this limit') parser.add_argument('--no-color', dest='color', default=True, action='store_false', help="Don't highlight NUMA nodes or sockets") parser.add_argument('--spaces', default=False, action='store_true', help="Add spaces in numbers' representation, e.g. '1234567' " "will be '1 234 567'") parser.add_argument('--random', default=False, action='store_true', help='Shows random diff data instead of real evaluation. ' 'Helpful for testing on static files') parser.add_argument('--no-clear', default=True, dest='clear', action='store_false', help="Don't clear screen after each iteration. " "May be useful in scripts/logging to file.") parser.add_argument('--lscpu-output', help='Specify file with lscpu -p output') return parser def make_parser(self, parser=None): if type(self) == BaseTop: raise TypeError('make_parser should not be called directly by BaseTop') if not parser: parser = BaseTop.make_base_parser() parser.add_argument(self.file_arg, default=self.file_value, help='Option for testing on MacOS purpose.') return parser def tick(self): """ Gathers new data + evaluate diff between current & previous data """ self.previous = self.current self.current = self.parse() if all((self.previous, self.current)): self.eval() def list_diff(self, current, previous): """ It's strange that there is no [3,4,3] - [1,2,1] -> [2,2,2] in standard library """ if self.options.random: return [randint(0, 10000) for _ in current] return [data - previous[n] for n, data in enumerate(current)] def run(self): """ Default main()-like function for specific top-like utils except meta-utils. """ infinite = -1 if self.options.iterations != infinite: self.options.iterations += 1 try: while self.options.iterations > 0 or self.options.iterations == infinite: if self.options.iterations != infinite: self.options.iterations -= 1 sleep(self.options.interval) self.tick() if self.options.clear: system('clear') if self.diff: print_(self) except KeyboardInterrupt: print_() exit(0) def repr_source(self): return self.diff if self.options.delta_mode else self.current @staticmethod def int(item): return int(item) if item.isdigit() else item def spaces(self, number, sep=' '): """ 1234567 -> 1 234 567 """ if not self.options.spaces: return number output = str() while number / 1000 > 0: output = str(number % 1000).zfill(3) + sep + output number /= 1000 return (str(number % 1000) + sep + output).strip() def __repr_table__(self, table): if self.options.clear: return BaseTop.header + str(table) return str(table) @abstractmethod def parse(self): """ Should read some file(s) into python structure (dict/list) """ @abstractmethod def eval(self): """ Should evaluate self.diff using self.previous / self.current """ @abstractmethod def __repr__(self): """ Should return string, representing self.diff """ def main(self): """ Default entry point for most of top-like utils """ self.options = self.make_parser().parse_args() if hasattr(self, 'post_optparse'): self.post_optparse() self.run()