Mini Shell
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
import logging
from lvestat import LVEStat
from lvestats.core.plugin import LveStatsPlugin
KB = 1024
FAULTS = ('cpu_fault', 'memphy_fault', 'mem_fault', 'io_fault', 'iops_fault', 'mep_fault', 'nproc_fault')
LIMITS = ('lcpu', 'lmemphy', 'lmem', 'io', 'liops', 'lep', 'lnproc')
STATS = ('cpu_usage', 'memphy', 'mem_usage', 'io_usage', 'iops', 'mep', 'nproc')
LVEUSAGESLOTS = ('lve_version', 'time', 'has_changed_limits', 'has_changed_nproc') + STATS + LIMITS + FAULTS
__author__ = 'iseletsk'
log = logging.getLogger('LVEUsage')
class LVEUsageAnalyzer(LveStatsPlugin):
"""
Calculates lve usage and updates it in lve_data, in field 'lve_usage'
"""
def set_config(self, config):
self.config = config # pylint: disable=attribute-defined-outside-init
def execute(self, lve_data):
"""
:param dict lve_data:
"""
# from /proc/lve
new_stats = lve_data['stats']
old_stats = lve_data['old_stats']
del lve_data['old_stats']
old_now = lve_data.get('old_now', None)
lve_data['old_now'] = self.now
total_hz = lve_data['totalHz']
procs = lve_data['procs']
result = {}
for _id, lve_stat in new_stats.items():
u = LVEUsage(lve_version=lve_data['LVE_VERSION'])
lve_stat_old = old_stats.get(_id, None)
u.calc(lve_stat_old, lve_stat, total_hz, self.now - old_now if old_now else None, procs)
# LVES-210: clear all data if EP and NPROC == 0 and all other data not changed
if _id == 0 \
or lve_stat.nproc > 0 \
or lve_stat.mep > 0 \
or (lve_stat_old is not None and u.is_lve_data_changed(lve_stat_old, lve_stat)):
result[_id] = u
lve_usages = lve_data.get('lve_usages', [])
lve_usages.append(result)
lve_data['lve_usages_5s'] = [result]
lve_data['lve_active_ids'] = list(result.keys())
lve_data['lve_usages'] = lve_usages
class LVEUsage(object):
__slots__ = LVEUSAGESLOTS
def __init__(self, lve_version):
self.lve_version = lve_version
self.time = None
self.has_changed_limits = False
self.has_changed_nproc = False
# if LVE_VERSION >= 4
self.cpu_usage = 0
self.cpu_fault = 0
self.mep = 0
self.mem_fault = 0
self.mep_fault = 0
self.mem_usage = 0
self.lep = 0
self.lcpu = 0
self.lmem = 0
# if LVE_VERSION >=6
self.io = 0
self.io_usage = 0
self.io_fault = 0
self.lmemphy = 0
self.memphy = 0
self.memphy_fault = 0
self.lnproc = 0
self.nproc = 0
self.nproc_fault = 0
# if LVE_VERSION >= 8
self.liops = 0
self.iops = 0
self.iops_fault = 0
def init_limits_from_lvestat(self, lvestat: LVEStat, procs):
"""
Prepares LVEUsage obj taking limits from LVEStat
"""
self.lep = lvestat.lep
self.lcpu = self.convert_lcpu_to_new_kernel_format(lvestat.cpu,
self.lve_version,
procs)
# CMT-509: the incorrect LVE I/O units on CMT dashboard
# Proc saves this value is in KBps,
# but we should send it is in Bps,
# because for an active user this value is sent in Bps
self.io = lvestat.io * KB
self.lmemphy = lvestat.lmemphy
self.lnproc = lvestat.lnproc
if self.lve_version >= 8:
self.liops = lvestat.liops
return self
def __repr__(self):
representation = '<'
for k in (k for k in dir(self) if not k.startswith("_")):
v = getattr(self, k)
if isinstance(v, (str, int, float, bool, type(None))):
representation += f'{k}:{v}, '
representation += '>'
return representation
def has_interesting_values(self):
if any((self.cpu_usage, self.nproc, self.io_usage, self.iops)):
return True
if any((getattr(self, k) for k in FAULTS)):
return True
return False
def _has_changed_limits(self, new, old):
"""
:type old: lvestat.LVEStat
:type new: lvestat.LVEStat
"""
for attr in LIMITS:
if getattr(new, attr, None) != getattr(old, attr, None):
return True
return False
def _has_changed_nproc(self, new, old):
"""
:type old: lvestat.LVEStat
:type new: lvestat.LVEStat
"""
return old.nproc != new.nproc
def convert_lcpu_to_new_kernel_format(self, lcpu, lve_ver, procs):
"""
New kernel format lcpu is 100 times bigger than previous. So that it's 0-10000
"""
if lve_ver >= 8:
return lcpu
else:
# new_kernel_format = (old_format * 10000 * procs / 100) = old_format * 100 * procs
return lcpu * procs * 100
def is_lve_data_changed(self, old, new):
"""
Check if LVE data is changed
:param old: Previous data
:param new: Current data
:return: True - changes, false - not
"""
# Common data
if old.cpu_usage != new.cpu_usage \
or old.mep != new.mep \
or old.mem_fault != new.mem_fault \
or old.mep_fault != new.mep_fault \
or old.mem_usage != new.mem_usage \
or old.lep != new.lep \
or old.io != new.io \
or old.io_usage != new.io_usage:
return True
# LVE6+ data
if (old.memphy != new.memphy or old.memphy_fault != new.memphy_fault or
old.nproc != new.nproc or old.nproc_fault != new.nproc_fault):
return True
# LVE8+ data
if self.lve_version >= 8 and old.iops != new.iops:
return True
return False
def calc(self, old, new, hz, time, procs):
self.time = time
# LCPU 100% now is 100*100 = 10 000
# LCPU 0% is 0
self.lcpu = self.convert_lcpu_to_new_kernel_format(new.cpu, self.lve_version, procs)
if bool(old):
# Previous data present
self.has_changed_limits = self._has_changed_limits(new, old)
self.has_changed_nproc = self._has_changed_nproc(new, old)
# CPU USAGE is 0-10000*procs
# new_kernel_format = (old_format * 10000 * procs / 100) = old_format * 100 * procs;
# (100 * (new.cpu_usage - self.cpu_usage) / (hz * time)) = old_format
assert time is not None, "oldstats is not None, but time is unknown"
assert time != 0, "oldstats is not None, but time is 0"
if new.cpu_usage > old.cpu_usage:
self.cpu_usage = int(round(10000 * (new.cpu_usage - old.cpu_usage) * procs / (hz * time)))
if new.mem_fault > old.mem_fault:
self.mem_fault = new.mem_fault - old.mem_fault
if new.mep_fault > old.mep_fault:
self.mep_fault = new.mep_fault - old.mep_fault
if new.memphy_fault > old.memphy_fault:
self.memphy_fault = new.memphy_fault - old.memphy_fault
if new.nproc_fault > old.nproc_fault:
self.nproc_fault = new.nproc_fault - old.nproc_fault
if new.io_usage > old.io_usage:
# bytes per second
self.io_usage = int(round(KB * (new.io_usage - old.io_usage) / time))
if self.lve_version >= 8:
if new.iops > old.iops:
self.iops = int(round((new.iops - old.iops) / time))
if self.cpu_usage < 0:
self.cpu_usage = 0
elif self.cpu_usage >= self.lcpu > 0:
self.cpu_usage = self.lcpu
self.cpu_fault += 1
self.lmem = new.lmem
if self.mem_fault > 0 or new.mem_usage > new.lmem > 0:
self.mem_usage = new.lmem
else:
self.mem_fault = 0
self.mem_usage = new.mem_usage
self.lep = new.lep
if self.mep_fault > 0 or new.mep > new.lep > 0:
self.mep = new.lep
else:
self.mep_fault = 0
self.mep = new.mep
if self.lve_version >= 8:
self.liops = new.liops
if self.iops >= self.liops > 0:
self.iops = self.liops
self.iops_fault += 1
self.io = KB * new.io # limit in bytes per second
if self.io_usage < 0:
self.io_usage = 0
elif self.io_usage >= self.io > 0:
self.io_usage = self.io
self.io_fault += 1
self.lmemphy = new.lmemphy
self.lnproc = new.lnproc
if self.memphy_fault > 0 or new.memphy > new.lmemphy > 0:
self.memphy = new.lmemphy
else:
self.memphy_fault = 0
self.memphy = new.memphy
if self.nproc_fault > 0 or new.nproc > new.lnproc > 0:
self.nproc = new.lnproc
else:
self.nproc_fault = 0
self.nproc = new.nproc
def __str__(self):
return "(cpu_usage=" + str(self.cpu_usage) + ", mep=" + str(self.mep) + ")"
def __getitem__(self, key):
return getattr(self, key)
Zerion Mini Shell 1.0