#!/usr/bin/env python

import os
import sys
import time
import optparse
import unittest

FILE = '/proc/net/softnet_stat'


class SoftnetStat(object):
    def __init__(self, row, cpu):
        row = [int('0x' + x, 16) for x in row.strip().split()]
        self.total, self.dropped, self.time_squeeze = row[0:3]
        self.cpu_collision = row[6]
        self.received_rps = row[7]
        self.cpu = cpu

    def __repr__(self):
        return "CPU: {0} total: {1:11} dropped: {2} time_squeeze: {3:4} cpu_collision: {4} received_rps: {5}" \
            .format(self.cpu, self.total, self.dropped, self.time_squeeze, self.cpu_collision, self.received_rps)

    def __sub__(self, other):
        return "CPU: {0:2} total: {1:8} dropped: {2} time_squeeze: {3} cpu_collision: {4} received_rps: {5}" \
            .format(self.cpu,
                    self.total - other.total,
                    self.dropped - other.dropped,
                    self.time_squeeze - other.time_squeeze,
                    self.cpu_collision - other.cpu_collision,
                    self.received_rps - other.received_rps)


class SoftnetStatTests(unittest.TestCase):
    first = """
    9d3cbd5e 00000000 0000004d 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    301350a8 00000000 00000025 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    2102d7a3 00000000 00000021 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    1d208d3b 00000000 00000021 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    6ba194e0 00000000 0000002b 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    25ef7e5f 00000000 0000001f 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    178ea501 00000000 0000001e 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    16882427 00000000 00000029 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    """

    second = """
    9d3cebfe 00000000 0000004d 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    30135354 00000000 00000025 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    2102d995 00000000 00000021 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    1d208e70 00000000 00000021 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    6ba1984a 00000000 0000002b 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    25ef7f6f 00000000 0000001f 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    178ed754 00000000 0000001e 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    168824ff 00000000 00000029 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    """

    def test_delta(self):
        __first = [SoftnetStat(row, cpu) for cpu, row in enumerate(self.first.strip().split('\n'))]
        __second = [SoftnetStat(row, cpu) for cpu, row in enumerate(self.second.strip().split('\n'))]
        delta = __second[0] - __first[0]
        expected = 'CPU:  0 total:    11936 dropped: 0 time_squeeze: 0 cpu_collision: 0 received_rps: 0'
        self.assertEqual(delta, expected)


def make_options():
    parser = optparse.OptionParser()
    parser.add_option("-i", "--interval", dest='interval', default=1,
                      help="specifies the delay between screen updates")
    parser.add_option("-a", "--assert-mode", dest='assert_mode', action='store_true', default=False,
                      help="stops screen updates if there is packets drop")
    parser.add_option("-u", "--unit-tests", dest='unit_tests', action='store_true', default=False,
                      help="runs unit-tests instead of read anything from system")
    parser.add_option("-n", "--no-delta-mode", dest='delta_mode', action='store_false', default=True,
                      help="shows actual counters instead evaluating deltas")
    options, _ = parser.parse_args()
    return options


def main():
    options = make_options()
    if options.unit_tests:
        sys.argv = sys.argv[0:1]
        return unittest.main()
    prev = None
    stop_flag = False
    while True:
        with open(FILE) as softnet_stat:
            current_data = softnet_stat.read().strip().split('\n')
            softnet_stat.close()
        cur = [SoftnetStat(row, cpu) for cpu, row in enumerate(current_data)]
        if prev or not options.delta_mode:
            print "Press CTRL-C to exit."
            for cpu, data in enumerate(cur):
                if options.delta_mode:
                    print cur[cpu] - prev[cpu]
                    stop_flag = options.assert_mode and cur[cpu].dropped - prev[cpu].dropped > 0
                else:
                    print cur[cpu]
        prev = cur
        softnet_stat.close()
        if stop_flag:
            exit(0)
        time.sleep(options.interval)
        os.system('clear')

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        exit(0)
