
From: Aline Manera <alinefm@br.ibm.com> To avoid duplicating code in model and mockmodel, the common code related to host resource (and its sub-resources) was added to model_/host.py and the specific code for each backend (libvirt or mock) was added to model_/libvirtbackend.py and model_/mockbackend.py Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/model_/host.py | 49 +++++++++++++++++++ src/kimchi/model_/libvirtbackend.py | 89 +++++++++++++++++++++++++++++++++++ src/kimchi/model_/mockbackend.py | 25 ++++++++++ 3 files changed, 163 insertions(+) create mode 100644 src/kimchi/model_/host.py diff --git a/src/kimchi/model_/host.py b/src/kimchi/model_/host.py new file mode 100644 index 0000000..f5ef0d7 --- /dev/null +++ b/src/kimchi/model_/host.py @@ -0,0 +1,49 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi import disks + +class Host(object): + def __init__(self, backend): + self.info = backend.get_host() + + def lookup(self, ident): + return self.info + +class HostStats(object): + def __init__(self, backend): + self.backend = backend + + def lookup(self, ident): + return self.backend.host_stats + +class Partitions(object): + def get_list(self): + return disks.get_partitions_names() + +class Partition(object): + def lookup(self, ident): + if ident not in disks.get_partitions_names(): + raise NotFoundError("Partition %s not found in the host" % name) + + return disks.get_partition_details(ident) diff --git a/src/kimchi/model_/libvirtbackend.py b/src/kimchi/model_/libvirtbackend.py index 506b8c4..4ab4db6 100644 --- a/src/kimchi/model_/libvirtbackend.py +++ b/src/kimchi/model_/libvirtbackend.py @@ -26,8 +26,14 @@ import fnmatch import glob import logging import os +import platform +import psutil import shutil import subprocess +import time + +from cherrypy.process.plugins import BackgroundTask +from collections import defaultdict from kimchi import config from kimchi.asynctask import AsyncTask @@ -36,10 +42,16 @@ from kimchi.exception import OperationFailed from kimchi.objectstore import ObjectStore from kimchi.utils import kimchi_log +HOST_STATS_INTERVAL = 1 + class LibvirtBackend(object): def __init__(self, objstore_loc=None): self.objstore = ObjectStore(objstore_loc) self.next_taskid = 1 + self.host_stats = defaultdict(int) + self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL, + self._update_host_stats) + self.host_stats_thread.start() # Subscribe function to set host capabilities to be run when cherrypy # server is up @@ -73,6 +85,67 @@ class LibvirtBackend(object): 'screenshot': VMScreenshot.get_stream_test_result(), 'system_report_tool': bool(report_tool)} + def _update_host_stats(self): + preTimeStamp = self.host_stats['timestamp'] + timestamp = time.time() + # FIXME when we upgrade psutil, we can get uptime by psutil.uptime + # we get uptime by float(open("/proc/uptime").readline().split()[0]) + # and calculate the first io_rate after the OS started. + seconds = (timestamp - preTimeStamp if preTimeStamp else + float(open("/proc/uptime").readline().split()[0])) + + self.host_stats['timestamp'] = timestamp + self._get_host_disk_io_rate(seconds) + self._get_host_network_io_rate(seconds) + + # get cpu utilization + self.host_stats['cpu_utilization'] = psutil.cpu_percent(None) + + # get memory stats + virt_mem = psutil.virtual_memory() + self.host_stats['memory'] = {'total': virt_mem.total, + 'free': virt_mem.free, + 'cached': virt_mem.cached, + 'buffers': virt_mem.buffers, + 'avail': virt_mem.available} + + def _get_host_disk_io_rate(self, seconds): + prev_read_bytes = self.host_stats['disk_read_bytes'] + prev_write_bytes = self.host_stats['disk_write_bytes'] + + disk_io = psutil.disk_io_counters(False) + read_bytes = disk_io.read_bytes + write_bytes = disk_io.write_bytes + + rd_rate = int(float(read_bytes - prev_read_bytes) / seconds + 0.5) + wr_rate = int(float(write_bytes - prev_write_bytes) / seconds + 0.5) + + self.host_stats.update({'disk_read_rate': rd_rate, + 'disk_write_rate': wr_rate, + 'disk_read_bytes': read_bytes, + 'disk_write_bytes': write_bytes}) + + def _get_host_network_io_rate(self, seconds): + prev_recv_bytes = self.host_stats['net_recv_bytes'] + prev_sent_bytes = self.host_stats['net_sent_bytes'] + + net_ios = psutil.network_io_counters(True) + recv_bytes = 0 + sent_bytes = 0 + + ifaces = set(netinfo.nics() + netinfo.wlans()) & set(net_ios.iterkeys()) + for key in ifaces: + recv_bytes = recv_bytes + net_ios[key].bytes_recv + sent_bytes = sent_bytes + net_ios[key].bytes_sent + + rx_rate = int(float(recv_bytes - prev_recv_bytes) / seconds + 0.5) + tx_rate = int(float(sent_bytes - prev_sent_bytes) / seconds + 0.5) + + self.host_stats.update({'net_recv_rate': rx_rate, + 'net_sent_rate': tx_rate, + 'net_recv_bytes': recv_bytes, + 'net_sent_bytes': sent_bytes}) + def gen_debugreport_file(self, name): gen_cmd = self._get_system_report_tool() if gen_cmd is None: @@ -139,3 +212,19 @@ class LibvirtBackend(object): self.next_taskid += 1 task = AsyncTask(id, target_uri, fn, self.objstore, opaque) return id + + def get_host(self): + res = {} + with open('/proc/cpuinfo') as f: + for line in f.xreadlines(): + if "model name" in line: + res['cpu'] = line.split(':')[1].strip() + break + + res['memory'] = psutil.TOTAL_PHYMEM + distro, version, codename = platform.linux_distribution() + res['os_distro'] = distro + res['os_version'] = version + res['os_codename'] = unicode(codename,"utf-8") + + return res diff --git a/src/kimchi/model_/mockbackend.py b/src/kimchi/model_/mockbackend.py index d6b8925..5fb332e 100644 --- a/src/kimchi/model_/mockbackend.py +++ b/src/kimchi/model_/mockbackend.py @@ -32,6 +32,21 @@ class MockBackend(object): def __init__(self, objstore_loc=None): self.objstore = ObjectStore(objstore_loc) self.next_taskid = 1 + self.host_stats = self._get_host_stats() + + def _get_host_stats(self): + memory_stats = {'total': 3934908416L, + 'free': round(random.uniform(0, 3934908416L), 1), + 'cached': round(random.uniform(0, 3934908416L), 1), + 'buffers': round(random.uniform(0, 3934908416L), 1), + 'avail': round(random.uniform(0, 3934908416L), 1)} + + return {'cpu_utilization': round(random.uniform(0, 100), 1), + 'memory': memory_stats, + 'disk_read_rate': round(random.uniform(0, 4000), 1), + 'disk_write_rate': round(random.uniform(0, 4000), 1), + 'net_recv_rate': round(random.uniform(0, 4000), 1), + 'net_sent_rate': round(random.uniform(0, 4000), 1)} def get_capabilities(self): protocols = ['http', 'https', 'ftp', 'ftps', 'tftp'] @@ -60,3 +75,13 @@ class MockBackend(object): self.next_taskid += 1 task = AsyncTask(id, target_uri, fn, self.objstore, opaque) return id + + def get_host(self): + res = {} + res['memory'] = 6114058240 + res['cpu'] = 'Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz' + res['os_distro'] = 'Red Hat Enterprise Linux Server' + res['os_version'] = '6.4' + res['os_codename'] = 'Santiago' + + return res -- 1.7.10.4