Skip to content

Commit

Permalink
32bit without numa fix (#111)
Browse files Browse the repository at this point in the history
* Refactoring: split assessor in 3 files: grade/assessor/assessor_math
* Fixed: 32bit systems without NUMA support

#109
  • Loading branch information
strizhechenko authored Jul 1, 2017
1 parent 06d9b98 commit 3963fa8
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 140 deletions.
102 changes: 28 additions & 74 deletions netutils_linux_hardware/assessor.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import re
import math
import yaml


def round_(x, d=0):
p = 10 ** d
return float(math.floor((x * p) + math.copysign(0.5, x)))/p


def extract(dictionary, key_sequence):
key_sequence.reverse()
while dictionary and key_sequence:
dictionary = dictionary.get(key_sequence.pop())
return dictionary
from netutils_linux_hardware.assessor_math import extract
from netutils_linux_hardware.grade import Grade


class Assessor(object):
Expand All @@ -27,61 +15,27 @@ def __init__(self, data):
def __str__(self):
return yaml.dump(self.info, default_flow_style=False).strip()

@staticmethod
def any2int(value):
if isinstance(value, int):
return value
elif value is None:
return 0
elif isinstance(value, str):
v = re.sub(r'[^0-9]', '', value)
if v.isdigit():
return int(v)
elif isinstance(value, float):
return int(value)
return 0

@staticmethod
def grade_int(value, _min, _max, scale=10):
value = Assessor.any2int(value)
return min(scale, max(1, int(1 + round_((value - _min) * (scale - 1.) / (_max - _min)+.001))))

@staticmethod
def grade_str(value, good=None, bad=None):
if bad and value in bad:
return 1
if good and value in good:
return 10
return 2

@staticmethod
def grade_fact(value, mode=False):
return int((value is None) != mode) * 10 or 1

def grade_list(self, value, minimum, maxmimum, scale=10):
return self.grade_int(len(value), minimum, maxmimum, scale)

def assess_netdev(self, netdev):
netdevinfo = extract(self.data, ['net', netdev])
queues = sum(
len(extract(netdevinfo, ['queues', x])) for x in ('rx', 'rxtx'))
buffers = netdevinfo.get('buffers') or {}
return {
'queues': self.grade_int(queues, 2, 8),
'queues': Grade.int(queues, 2, 8),
'driver': {
'mlx5_core': 10, # 7500 mbit/s
'mlx4_en': 9, # 6500 mbit/s
'i40e': 8, # 6000 mbit/s
'ixgbe': 7, # 4000 mbit/s
'igb': 6, # 400 mbit/s
'bnx2x': 4, # 100 mbit/s
'e1000e': 3, # 80 mbit/s
'e1000': 3, # 50 mbit/s
'r8169': 1, 'ATL1E': 1, '8139too': 1, # real trash, you should never use it
'mlx5_core': 10, # 7500 mbit/s
'mlx4_en': 9, # 6500 mbit/s
'i40e': 8, # 6000 mbit/s
'ixgbe': 7, # 4000 mbit/s
'igb': 6, # 400 mbit/s
'bnx2x': 4, # 100 mbit/s
'e1000e': 3, # 80 mbit/s
'e1000': 3, # 50 mbit/s
'r8169': 1, 'ATL1E': 1, '8139too': 1, # real trash, you should never use it
}.get(netdevinfo.get('driver').get('driver'), 2),
'buffers': {
'cur': self.grade_int(buffers.get('cur'), 256, 4096),
'max': self.grade_int(buffers.get('max'), 256, 4096),
'cur': Grade.int(buffers.get('cur'), 256, 4096),
'max': Grade.int(buffers.get('max'), 256, 4096),
},
}

Expand All @@ -94,38 +48,38 @@ def assess_cpu(self):
cpuinfo = extract(self.data, ['cpu', 'info'])
if cpuinfo:
return {
'CPU MHz': self.grade_int(cpuinfo.get('CPU MHz'), 2000, 4000),
'BogoMIPS': self.grade_int(cpuinfo.get('BogoMIPS'), 4000, 8000),
'CPU(s)': self.grade_int(cpuinfo.get('CPU(s)'), 2, 32),
'Core(s) per socket': self.grade_int(cpuinfo.get('Core(s) per socket'), 1, 2),
'Socket(s)': self.grade_int(cpuinfo.get('Socket(s)'), 1, 2),
'Thread(s) per core': self.grade_int(cpuinfo.get('Thread(s) per core'), 2, 1),
'L3 cache': self.grade_int(cpuinfo.get('L3 cache'), 1000, 30000),
'Vendor ID': self.grade_str(cpuinfo.get('Vendor ID'), good=['GenuineIntel']),
'CPU MHz': Grade.int(cpuinfo.get('CPU MHz'), 2000, 4000),
'BogoMIPS': Grade.int(cpuinfo.get('BogoMIPS'), 4000, 8000),
'CPU(s)': Grade.int(cpuinfo.get('CPU(s)'), 2, 32),
'Core(s) per socket': Grade.int(cpuinfo.get('Core(s) per socket'), 1, 2),
'Socket(s)': Grade.int(cpuinfo.get('Socket(s)'), 1, 2),
'Thread(s) per core': Grade.int(cpuinfo.get('Thread(s) per core'), 2, 1),
'L3 cache': Grade.int(cpuinfo.get('L3 cache'), 1000, 30000),
'Vendor ID': Grade.str(cpuinfo.get('Vendor ID'), good=['GenuineIntel']),
}

def assess_memory(self):
meminfo = self.data.get('memory')
if meminfo:
return {
'MemTotal': self.grade_int(meminfo.get('MemTotal'), 2 * (1024 ** 2), 16 * (1024 ** 2)),
'SwapTotal': self.grade_int(meminfo.get('SwapTotal'), 512 * 1024, 4 * (1024 ** 2)),
'MemTotal': Grade.int(meminfo.get('MemTotal'), 2 * (1024 ** 2), 16 * (1024 ** 2)),
'SwapTotal': Grade.int(meminfo.get('SwapTotal'), 512 * 1024, 4 * (1024 ** 2)),
}

def assess_system(self):
cpuinfo = extract(self.data, ['cpu', 'info'])
if cpuinfo:
return {
'Hypervisor vendor': self.grade_fact(cpuinfo.get('Hypervisor vendor'), False),
'Virtualization type': self.grade_fact(cpuinfo.get('Hypervisor vendor'), False),
'Hypervisor vendor': Grade.fact(cpuinfo.get('Hypervisor vendor'), False),
'Virtualization type': Grade.fact(cpuinfo.get('Hypervisor vendor'), False),
}

def assess_disk(self, disk):
diskinfo = extract(self.data, ['disk', disk])
return {
'type': self.grade_str(diskinfo.get('type'), ['SDD'], ['HDD']),
'type': Grade.str(diskinfo.get('type'), ['SDD'], ['HDD']),
# 50Gb - good, 1Tb - good enough
'size': self.grade_int(diskinfo.get('size'), 50 * (1000 ** 3), 1000 ** 4),
'size': Grade.int(diskinfo.get('size'), 50 * (1000 ** 3), 1000 ** 4),
}

def assess_disks(self):
Expand Down
31 changes: 31 additions & 0 deletions netutils_linux_hardware/assessor_math.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
""" Help-functions for grade/assessor lib """
# coding=utf-8

import re
import math


def round_(x, d=0):
p = 10 ** d
return float(math.floor((x * p) + math.copysign(0.5, x))) / p


def extract(dictionary, key_sequence):
key_sequence.reverse()
while dictionary and key_sequence:
dictionary = dictionary.get(key_sequence.pop())
return dictionary


def any2int(value):
if isinstance(value, int):
return value
elif value is None:
return 0
elif isinstance(value, str):
v = re.sub(r'[^0-9]', '', value)
if v.isdigit():
return int(v)
elif isinstance(value, float):
return int(value)
return 0
65 changes: 0 additions & 65 deletions netutils_linux_hardware/assessor_test.py

This file was deleted.

20 changes: 20 additions & 0 deletions netutils_linux_hardware/grade.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# coding: utf-8
from netutils_linux_hardware.assessor_math import any2int, round_


class Grade(object):
""" Grade provides methods of grade values of any type in scale of 1..10 """

@staticmethod
def int(value, _min, _max, scale=10):
""" Well, it's tricky function writen by all the twitter """
value = any2int(value)
return min(scale, max(1, int(1 + round_((value - _min) * (scale - 1.) / (_max - _min) + .001))))

@staticmethod
def str(value, good=None, bad=None):
return 1 if bad and value in bad else 10 if good and value in good else 2

@staticmethod
def fact(value, mode=False):
return 10 if (value is None) != mode else 1
60 changes: 60 additions & 0 deletions netutils_linux_hardware/grade_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python
# coding=utf-8

""" Tests of grade in scale of 1..10 """

from unittest import TestCase, main
from six import iteritems
from netutils_linux_hardware.grade import Grade
from netutils_linux_hardware.assessor_math import any2int


class GradeTest(TestCase):
def test_grade_int(self):
self.longMessage = True
self.assertEqual(Grade.int(2000, 2000, 4000), 1)
self.assertEqual(Grade.int(1000, 2000, 4000), 1)
self.assertEqual(Grade.int(-1000, 2000, 4000), 1)
self.assertEqual(Grade.int(2200, 2000, 4000), 2)
self.assertEqual(Grade.int(3000, 2000, 4000), 6)
self.assertEqual(Grade.int(3999, 2000, 4000), 10)
self.assertEqual(Grade.int(4000, 2000, 4000), 10)
self.assertEqual(Grade.int(5000, 2000, 4000), 10)
self.assertEqual(Grade.int(5000, 2000, 4000, 15), 15)
self.assertEqual(Grade.int(4000, 2000, 4000, 15), 15)

def test_grade_str(self):
bad = ["Realtek", "Dlink"]
good = ["Intel", "Melanox"]
expected = {
"Melanox": 10,
"Intel": 10,
"Broadcom": 2,
"Dlink": 1,
"Realtek": 1,
}
for k, v in iteritems(expected):
self.assertEqual(Grade.str(k, good, bad), v)

def test_grade_fact(self):
self.assertEqual(Grade.fact(None, True), 1)
self.assertEqual(Grade.fact(None, False), 10)
self.assertEqual(Grade.fact("Anything", True), 10)
self.assertEqual(Grade.fact("Anything", False), 1)
self.assertEqual(Grade.fact(15, True), 10)
self.assertEqual(Grade.fact(15, False), 1)
self.assertEqual(Grade.fact({'x': 'y'}, True), 10)
self.assertEqual(Grade.fact({}, True), 10)

def test_any2int(self):
self.assertEqual(any2int(None), 0)
self.assertEqual(any2int(23), 23)
self.assertEqual(any2int(23.1), 23)
self.assertEqual(any2int("23"), 23)
self.assertEqual(any2int("23K"), 23)
self.assertEqual(any2int("23 K"), 23)
self.assertEqual(any2int(" "), 0)


if __name__ == '__main__':
main()
3 changes: 2 additions & 1 deletion netutils_linux_monitoring/numa.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
from subprocess import Popen, PIPE
from six import print_
from netutils_linux_hardware.assessor_math import any2int


class Numa(object):
Expand Down Expand Up @@ -74,7 +75,7 @@ def detect_layouts(self):
if process.returncode != 0:
return self.detect_layouts_fallback()
rows = [row for row in stdout.strip().split(b'\n') if not row.startswith(b'#')]
layouts = [list(map(int, row.split(b',')[2:4])) for row in rows]
layouts = [list(map(any2int, row.split(b',')[2:4])) for row in rows]
numa_layout, socket_layout = zip(*layouts)
self.numa_layout = dict(enumerate(numa_layout))
self.socket_layout = dict(enumerate(socket_layout))
Expand Down

0 comments on commit 3963fa8

Please sign in to comment.