Advertisement
Guest User

HDStats

a guest
Dec 8th, 2016
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.01 KB | None | 0 0
  1. from collections import OrderedDict
  2. from time import sleep
  3. import glob
  4. import re
  5. import os
  6. import curses
  7. import argparse
  8. from subprocess import PIPE, run
  9. import json
  10.  
  11. parser = argparse.ArgumentParser()
  12. parser.add_argument("-s", type=float, help="Sample size in seconds", default=1)
  13. parser.add_argument("-p", help="Show disk partition activity", action="store_true")
  14. args = parser.parse_args()
  15.  
  16. sample_size = args.s
  17.  
  18.  
  19. def human_readable(b):
  20.     r = b / 1024 / sample_size
  21.     if r < 1024:  # KB/sec
  22.         return "{0:.3f} Kb".format(r)
  23.  
  24.     r = b / 1024 / 1024 / sample_size
  25.     if r < 1024:  # MiB/sec
  26.         return "{0:.3f} Mb".format(r)
  27.  
  28.     r = b / 1024 / 1024 / 1024 / sample_size
  29.     return "{0:.3f} Gb".format(r)
  30.  
  31.  
  32. def human_readable_fix(b):
  33.     r = b / 1024 / sample_size
  34.     if r < 1024:  # KB/sec
  35.         return [r, "Kb"]
  36.  
  37.     r = b / 1024 / 1024 / sample_size
  38.     if r < 1024:  # MiB/sec
  39.         return [r, "Mb"]
  40.  
  41.     r = b / 1024 / 1024 / 1024 / sample_size
  42.     return [r, "Gb"]
  43.  
  44.  
  45. class NetDev:
  46.     name = ""
  47.     tx = 0
  48.     rx = 0
  49.  
  50.  
  51. class NetStat:
  52.     stats = dict()
  53.     infs_stat = dict()
  54.     infs_stats1 = dict()
  55.     infs_stats2 = dict()
  56.  
  57.     def get_inf_stats(self):
  58.         inf_pattern = ['en.*', 'wl.*']
  59.         stat = open('/proc/net/dev').read()
  60.         for pattern in inf_pattern:
  61.             regx = re.compile(pattern)
  62.             res = regx.finditer(stat)
  63.             for i in res:
  64.                 td = i.group(0).split()
  65.                 td[0] = td[0][:-1]
  66.                 self.stats[td[0]] = td[1:]
  67.  
  68.     def start_rx_tx_record(self):
  69.         self.get_inf_stats()  # update info
  70.         for inf in self.stats:
  71.             ndev = NetDev()
  72.             ndev.name = inf
  73.             ndev.rx = self.stats[inf][0]
  74.             ndev.tx = self.stats[inf][8]
  75.             self.infs_stats1[inf] = ndev
  76.  
  77.     def stop_rx_tx_record(self):
  78.         self.get_inf_stats()  # update info
  79.         for inf in self.stats:
  80.             ndev = NetDev()
  81.             ndev.name = inf
  82.             ndev.rx = self.stats[inf][0]
  83.             ndev.tx = self.stats[inf][8]
  84.             self.infs_stats2[inf] = ndev
  85.  
  86.     def compute_rx_tx_spd(self):
  87.         infs_stat_sum = list()
  88.         for inf1, inf2 in zip(self.infs_stats1, self.infs_stats2):
  89.             if inf1 == inf2:
  90.                 ndev = NetDev()
  91.                 ndev.name = inf1
  92.                 ndev.rx = int(self.infs_stats2[inf1].rx) - int(self.infs_stats1[inf1].rx)
  93.                 ndev.tx = int(self.infs_stats2[inf1].tx) - int(self.infs_stats1[inf1].tx)
  94.                 infs_stat_sum.append(ndev)
  95.  
  96.         return infs_stat_sum
  97.  
  98.  
  99. class DiskFree:
  100.     def __init__(self):
  101.         self.cmd = ['df', '--block-size=1']
  102.         res = run(self.cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
  103.         out = str(res.stdout)
  104.         self.dftab = dict()
  105.         for line in out.splitlines()[1:]:
  106.             sline = line.split()
  107.             self.dftab[sline[0]] = sline[1:]
  108.  
  109.     def get_total(self, dev):
  110.         dpath = '/dev/' + dev.name
  111.         if dpath in self.dftab:
  112.             return int(self.dftab[dpath][0])
  113.         else:
  114.             return None
  115.  
  116.     def get_used(self, dev):
  117.         dpath = '/dev/' + dev.name
  118.         if dpath in self.dftab:
  119.             return int(self.dftab[dpath][1])
  120.         else:
  121.             return None
  122.  
  123.     def get_mount(self, dev):
  124.         dpath = '/dev/' + dev.name
  125.         if dpath in self.dftab:
  126.             if self.dftab[dpath][4] == '/':
  127.                 return '/'
  128.             else:
  129.                 return self.dftab[dpath][4]
  130.         else:
  131.             return ""
  132.  
  133.  
  134. class LsBlkB:
  135.     def __init__(self):
  136.         self.cmd = ['lsblk', '-b', '-J']
  137.         res = run(self.cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
  138.         out = str(res.stdout)
  139.  
  140.         js = json.loads(out)
  141.         self.js = js['blockdevices']
  142.  
  143.     def get_size(self, dev):
  144.         for i in range(0, len(self.js)):
  145.             if self.js[i]['name'] == dev.name:
  146.                 return int(self.js[i]['size'])
  147.  
  148.         return 'unknown'
  149.  
  150.     def get_type(self, dev):
  151.         for i in range(0, len(self.js)):
  152.             if self.js[i]['name'] == dev.name:
  153.                 return self.js[i]['type']
  154.  
  155.         return 'null'
  156.  
  157.  
  158. class LsBlkFS:
  159.     def __init__(self):
  160.         self.cmd = ['lsblk', '-fs', '-J']
  161.         res = run(self.cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
  162.         out = str(res.stdout)
  163.  
  164.         js = json.loads(out)
  165.         self.js = js['blockdevices']
  166.  
  167.     def get_fs(self, dev):
  168.         for i in range(0, len(self.js)):
  169.             if self.js[i]['name'] == dev.name:
  170.                 return self.js[i]['fstype']
  171.  
  172.         return ''
  173.  
  174.     def get_mount(self, dev):
  175.         for i in range(0, len(self.js)):
  176.             if self.js[i]['name'] == dev.name:
  177.                 return self.js[i]['mountpoint']
  178.  
  179.         return 'null'
  180.  
  181.  
  182. class SwapInfo:
  183.     def __init__(self):
  184.         self.cmd = ['swapon', '-l']
  185.         res = run(self.cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
  186.         out = str(res.stdout)
  187.         self.dftab = dict()
  188.         for line in out.splitlines()[1:]:
  189.             sline = line.split()
  190.             self.dftab[sline[0]] = sline[1:]
  191.  
  192.     def get_swap_info(self, dev):
  193.         dpath = '/dev/' + dev.name
  194.         if dpath in self.dftab:
  195.             return [int(self.dftab[dpath][2]), int(self.dftab[dpath][3])]
  196.         else:
  197.             return None
  198.  
  199.  
  200. class Device:
  201.     def __init__(self):
  202.         self.name = ""
  203.         self.stats = list()
  204.         self.size = 0
  205.         self.used = 0
  206.         self.perc = 0
  207.         self.fs = ''
  208.         self.mount = ''
  209.         self.sysfolder = ''
  210.         self.model = ''
  211.         self.child = 0
  212.         self.childs = list()
  213.  
  214.     def __str__(self):
  215.         return "name:{0}\nstats:{1}\nsize:{2}\nused:{3}\nperc:{4}\nfs:{5}\nmount:{6}\nsysfolder:{7}\n" \
  216.                "model:{8}\nchild:{9}".format(self.name, self.stats, self.size, self.used,
  217.                                              self.perc, self.fs, self.mount, self.sysfolder,
  218.                                              self.model, self.child)
  219.  
  220.  
  221. class PermonmanceDevice(Device):
  222.     perf = {}
  223.  
  224.     def __init__(self):
  225.         Device.__init__(self)
  226.  
  227.         self.perf = {'read': 0, 'write': 0, 'size': ''}
  228.  
  229.     def compute(self, dev1):
  230.         self.stats[2] = (self.stats[2] - dev1.stats[2])
  231.         self.stats[6] = (self.stats[6] - dev1.stats[6])
  232.         self.perf['read'] = human_readable(self.stats[2])
  233.         self.perf['write'] = human_readable(self.stats[6])
  234.  
  235.  
  236. class HumanReadableDevice(PermonmanceDevice):
  237.     def __init__(self):
  238.         PermonmanceDevice.__init__(self)
  239.  
  240.     def compute(self, dev1):
  241.         PermonmanceDevice.compute(self, dev1)
  242.         size_p = human_readable_fix(self.size)
  243.         used_p = human_readable_fix(self.used)
  244.         perc_p = human_readable_fix(self.perc)
  245.  
  246.         used_s = "{0:.2f} {1}".format(round(used_p[0], 2), used_p[1])
  247.         size_s = "{0:.2f} {1}".format(round(size_p[0], 2), size_p[1])
  248.         perc_s = "{0:.2f}%".format(round(perc_p[0], 1))
  249.  
  250.         s = "{0}/{1} {2}".format(used_s, size_s, perc_s)
  251.  
  252.         PermonmanceDevice.perf['size'] = s
  253.  
  254.     def __str__(self):
  255.         print(PermonmanceDevice.perf)
  256.  
  257.  
  258. class DeviceManager:
  259.     def __init__(self):
  260.         self.stack = list()
  261.         self.devs = list()
  262.         self.df = None
  263.         self.lsblkfs = None
  264.         self.lsblkb = None
  265.         # self.swapinf = None
  266.  
  267.     def update(self):
  268.         self.df = DiskFree()
  269.         self.lsblkfs = LsBlkFS()
  270.         self.lsblkb = LsBlkB()
  271.         # self.swapinf = SwapInfo()
  272.  
  273.  
  274.         if len(self.stack) >= 2:
  275.             self.devs = list()
  276.             self.stack = list()
  277.  
  278.         self.stack.append(self.devs)
  279.  
  280.         dev_pattern = ['sd.*']
  281.         for device in glob.glob('/sys/block/*'):
  282.             for pattern in dev_pattern:
  283.                 if re.compile(pattern).match(os.path.basename(device)):
  284.                     tdev = device.split('/')[-1]
  285.  
  286.                     dev = HumanReadableDevice()
  287.                     dev.name = tdev
  288.                     dev.sysfolder = device
  289.                     self.devs.append(dev)
  290.  
  291.                     for part in glob.glob('/sys/block/' + dev.name + '/*'):
  292.                         if re.compile(tdev + '[1-9]*').match(os.path.basename(part)):
  293.                             tpart = part.split('/')[-1]
  294.                             dev_part = HumanReadableDevice()
  295.                             dev_part.child = True
  296.                             dev_part.name = tpart
  297.                             dev_part.sysfolder = '/sys/block/' + dev.name + '/' + tpart
  298.                             dev.childs.append(dev_part)
  299.  
  300.         self.enrich_devices()
  301.  
  302.     def _enrich_model(self, dev):
  303.         try:
  304.             dev.model = open(dev.sysfolder + '/device/model').read().strip('\n').strip(' ')
  305.         except:
  306.             dev.model = 'inherit'
  307.  
  308.     def _enrich_mount(self, dev):
  309.         dev.mount = self.lsblkfs.get_mount(dev)
  310.  
  311.     def _enrich_fs(self, dev):
  312.         dev.fs = self.lsblkfs.get_fs(dev)
  313.  
  314.     def _enrich_size_(self, dev):
  315.         res = self.df.get_total(dev)
  316.         if res is not None:
  317.             dev.size = res
  318.             return True
  319.  
  320.         res = self.df.get_total(dev)
  321.         if res is not None:
  322.             dev.size = self.lsblkb.get_size(dev)
  323.             return False
  324.  
  325.         return False
  326.  
  327.     def _enrich_used_(self, dev):
  328.         res = self.df.get_used(dev)
  329.         if res is not None:
  330.             dev.used = res
  331.             return True
  332.  
  333.         return False
  334.  
  335.     def _enrich_size(self, dev):
  336.         if self._enrich_size_(dev) and self._enrich_used_(dev):
  337.             dev.perc = dev.used / dev.size
  338.         else:
  339.             dev.size = self.lsblkb.get_size(dev)
  340.  
  341.     def _enrich_swapinfo(self, dev):
  342.         swpinf = self.swapinf.get_swap_info(dev)
  343.         if swpinf is not None:
  344.             dev.size = swpinf[0]
  345.             dev.used = swpinf[1]
  346.             dev.perc = dev.used / dev.size
  347.         else:
  348.             return
  349.  
  350.     def _enrich_stats(self, dev):
  351.         with open(dev.sysfolder + '/stat') as f:
  352.             line = f.readline()
  353.             dev.stats = [int(i) for i in line.split()]
  354.  
  355.     def _enrich_device(self, dev):
  356.         self._enrich_stats(dev)
  357.         self._enrich_size(dev)
  358.         # self._enrich_swapinfo(dev)
  359.         self._enrich_fs(dev)
  360.         self._enrich_mount(dev)
  361.         self._enrich_model(dev)
  362.  
  363.         for x in dev.childs:
  364.             self._enrich_device(x)
  365.  
  366.     def enrich_devices(self):
  367.         for dev in self.devs:
  368.             self._enrich_device(dev)
  369.  
  370.     def _traverse(self, dev, depth):
  371.         print(str(dev) + "\n")
  372.  
  373.         for x in dev.childs:
  374.             self._traverse(x, depth + 1)
  375.  
  376.     def traverse(self):
  377.         for dev in self.devs:
  378.             self._traverse(dev, 0)
  379.  
  380.     def _get_device(self, devs, devs1, devs2):
  381.         for dev1, dev2 in zip(devs1, devs2):
  382.             dev2.compute(dev1)
  383.             devs.append(dev2)
  384.  
  385.     def get_device(self):
  386.         devs2 = self.stack.pop()
  387.         devs1 = self.stack.pop()
  388.         stat = OrderedDict()
  389.  
  390.         devs = list()
  391.         self._get_device(devs, devs1, devs2)
  392.  
  393.         return devs
  394.  
  395.  
  396. ddd = DeviceManager()
  397.  
  398. ddd.enrich_devices()
  399.  
  400. ddd.traverse()
  401.  
  402. stdscr = curses.initscr()
  403. curses.start_color()
  404. if not __debug__:
  405.     stdscr.border()
  406.  
  407.     curses.noecho()
  408.     curses.cbreak()
  409.  
  410. curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
  411. curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK)
  412. curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_BLACK)
  413.  
  414. stdscr.addstr(1, 1, "Devmon 0.1", curses.color_pair(3) | curses.A_BOLD)
  415. stdscr.addstr(2, 1, "Sample size {0} sec".format(sample_size), curses.color_pair(3) | curses.A_BOLD)
  416.  
  417.  
  418. class Row:
  419.     def __init__(self):
  420.         self.row = 4
  421.  
  422.     def next(self):
  423.         tmp = self.row
  424.         self.row += 1
  425.         return tmp
  426.  
  427.     def current(self):
  428.         return self.row
  429.  
  430.  
  431. class Col:
  432.     @staticmethod
  433.     def offset():
  434.         return 1
  435.  
  436.     @staticmethod
  437.     def device():
  438.         return 0 + Col.offset()
  439.  
  440.     @staticmethod
  441.     def read():
  442.         return 12 + Col.offset()
  443.  
  444.     @staticmethod
  445.     def write():
  446.         return 25 + Col.offset()
  447.  
  448.     @staticmethod
  449.     def size():
  450.         return 40 + Col.offset()
  451.  
  452.     @staticmethod
  453.     def filesystem():
  454.         return 58 + Col.offset()
  455.  
  456.     @staticmethod
  457.     def mount():
  458.         return 64 + Col.offset()
  459.  
  460.     @staticmethod
  461.     def model():
  462.         return 75 + Col.offset()
  463.  
  464.  
  465. class Color:
  466.     @staticmethod
  467.     def autocolor(child):
  468.         if not child:
  469.             return Color.green()
  470.         else:
  471.             return Color.cyan()
  472.  
  473.     @staticmethod
  474.     def green():
  475.         return curses.color_pair(1) | curses.A_BOLD
  476.  
  477.     @staticmethod
  478.     def cyan():
  479.         return curses.color_pair(2) | curses.A_BOLD
  480.  
  481.     @staticmethod
  482.     def white():
  483.         return curses.color_pair(3) | curses.A_BOLD
  484.  
  485.  
  486. while True:
  487.     # try:
  488.     devmg = DeviceManager()
  489.     ns = NetStat()
  490.     row = Row()
  491.     dev_stat = dict()
  492.     dev_part_stat = dict()
  493.  
  494.     ns.start_rx_tx_record()
  495.     devmg.update()
  496.  
  497.     sleep(sample_size)
  498.  
  499.     devmg.update()
  500.  
  501.     ns.stop_rx_tx_record()
  502.     netdevs = ns.compute_rx_tx_spd()
  503.  
  504.     stdscr.clrtoeol()
  505.     stdscr.addstr(row.current(), Col.device(), "Device", curses.color_pair(3) | curses.A_BOLD)
  506.     stdscr.clrtoeol()
  507.     stdscr.addstr(row.current(), Col.read(), "Read", curses.color_pair(3) | curses.A_BOLD)
  508.     stdscr.clrtoeol()
  509.     stdscr.addstr(row.current(), Col.write(), "Write", curses.color_pair(3) | curses.A_BOLD)
  510.     stdscr.clrtoeol()
  511.     stdscr.addstr(row.current(), Col.size(), "Size", curses.color_pair(3) | curses.A_BOLD)
  512.     stdscr.clrtoeol()
  513.     stdscr.addstr(row.current(), Col.filesystem(), "FS", curses.color_pair(3) | curses.A_BOLD)
  514.     stdscr.clrtoeol()
  515.     stdscr.addstr(row.current(), Col.mount(), "Mount", curses.color_pair(3) | curses.A_BOLD)
  516.     stdscr.clrtoeol()
  517.     stdscr.addstr(row.current(), Col.model(), "Model", curses.color_pair(3) | curses.A_BOLD)
  518.     stdscr.clrtoeol()
  519.  
  520.     row.next()
  521.  
  522.     devs = devmg.get_device()
  523.  
  524.     for dev in devs:
  525.         stdscr.clrtoeol()
  526.         stdscr.addstr(row.current(), Col.device(), "[/dev/{0}]".format(dev.name), Color.autocolor(dev.child))
  527.         stdscr.clrtoeol()
  528.         stdscr.addstr(row.current(), Col.read(), "{0}".format(dev.perf['read']), Color.autocolor(dev.child))
  529.         stdscr.clrtoeol()
  530.         stdscr.addstr(row.current(), Col.write(), "{0}".format(dev.perf['write']), Color.autocolor(dev.child))
  531.         stdscr.clrtoeol()
  532.         stdscr.addstr(row.current(), Col.size(), "{0}".format(dev.perf['size']), Color.autocolor(dev.child))
  533.         stdscr.clrtoeol()
  534.         stdscr.addstr(row.current(), Col.filesystem(), "{0}".format(dev.fs), Color.autocolor(dev.child))
  535.         stdscr.clrtoeol()
  536.         stdscr.addstr(row.current(), Col.mount(), "{0}".format(dev.mount), Color.autocolor(dev.child))
  537.         stdscr.clrtoeol()
  538.         stdscr.addstr(row.current(), Col.model(), "[{0}]".format(dev.model), Color.autocolor(dev.child))
  539.         stdscr.clrtoeol()
  540.  
  541.         row.next()
  542.  
  543.     row.next()
  544.  
  545.     for netdev in netdevs:
  546.         stdscr.clrtoeol()
  547.         stdscr.addstr(row.current(), Col.device(), "[{0}]".format(netdev.name),
  548.                       curses.color_pair(1) | curses.A_BOLD)
  549.         stdscr.clrtoeol()
  550.         stdscr.addstr(row.current(), Col.read(), "{0}/s".format(human_readable(netdev.rx)),
  551.                       curses.color_pair(1) | curses.A_BOLD)
  552.         stdscr.clrtoeol()
  553.         stdscr.addstr(row.current(), Col.write(), "{0}/s".format(human_readable(netdev.tx)),
  554.                       curses.color_pair(1) | curses.A_BOLD)
  555.         stdscr.clrtoeol()
  556.         row.next()
  557.  
  558.     stdscr.refresh()
  559.     # except KeyboardInterrupt:
  560.     #     break
  561.     # except:
  562.     #     sys.stderr.write("Unexpected error: {0}\n".format(sys.exc_info()[0]))
  563.     #     break
  564.  
  565. if not __debug__:
  566.     curses.echo()
  567.     curses.cbreak()
  568.     curses.endwin()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement