#!/usr/bin/python # -*- coding: utf-8 -*- # This file is part of Cockpit. # # Copyright (C) 2016 Red Hat, Inc. # # Cockpit 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. # # Cockpit 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 Cockpit; If not, see . import os import parent from testlib import * def read_mem_info(machine): info = { } for l in machine.execute("cat /proc/meminfo").splitlines(): (name, value) = l.strip().split(":") if value.endswith("kB"): info[name] = int(value[:-2])*1024 else: info[name] = int(value) return info class TestMetrics(MachineCase): def check_host_metrics(self): b = self.browser m = self.machine b.eval_js(""" ph_plot_timestamp = function (sel) { var data = $(sel).data("flot_data")[0]['data']; return data[data.length-1][0]; } """) b.eval_js(""" ph_plot_timestamp_is = function (sel, ts) { return ph_plot_timestamp(sel, 0) >= ts; } """) def wait_for_plot(sel, seconds): now = b.call_js_func("ph_plot_timestamp", sel) b.wait_js_func("ph_plot_timestamp_is", sel, now+20*1000) # When checking whether the plots show the expected results, # we look for a segment of the data of a certain duration # whose average is in a certain range. Otherwise any short # outlier will make us miss the expected plateau. Such # outliers happen frequently with the CPU plot. b.eval_js(""" ph_plateau = function (data, min, max, duration, label) { var i, j; var sum; // sum of data[i..j] sum = 0; i = 0; for (j = 0; j < data.length; j++) { sum += data[j][1]; while (i < j && (data[j][0] - data[i][0]) > duration * 1000) { avg = sum / (j - i + 1); if ((min === null || avg >= min) && (max === null || avg <= max)) return true; sum -= data[i][1]; i++; } } return false; } """) b.eval_js(""" ph_flot_data_plateau = function (sel, min, max, duration, label) { return ph_plateau($(sel).data("flot_data")[0]['data'], min, max, duration, label); } """) # CPU. We have to measure the actual percentage independently # and check whether the plot matches it. This is because we # can not reliably create 100% load in a virtual machine since # we only get as much as the qemu process itself gets. m.upload([ "verify/files/measure-cpu" ], "/var/lib/testvm/") cpu_start = m.execute("/var/lib/testvm/measure-cpu start") cpu_pid = m.spawn("while true; do true; done", "cpu-load.log") wait_for_plot("#server_cpu_graph", 20) m.execute("kill %d" % cpu_pid) cpu_percentage = float(m.execute("/var/lib/testvm/measure-cpu stop '%s'" % cpu_start)) print "Measured CPU", cpu_percentage self.assertTrue(b.call_js_func("ph_flot_data_plateau", "#server_cpu_graph", cpu_percentage*0.50, cpu_percentage*1.50, 15, "cpu")); # Memory. We assume that the machine has had a reasonably # stable memory situation for the last 20 seconds and we just # check whether we see that in the plot. # meminfo = read_mem_info(m) mem_used = meminfo['MemTotal'] - meminfo['MemFree'] b.wait_js_func("ph_flot_data_plateau", "#server_memory_graph", mem_used*0.95, mem_used*1.05, 15, "mem"); # Disk. Anything above 100 kiB/s is good for now. # m.add_disk("10M", serial="MYSERIAL") disk_pid = m.spawn("while true; do dd if=/dev/sda of=/dev/sda; done", "load-disk.log") b.wait_js_func("ph_flot_data_plateau", "#server_disk_io_graph", 100*1024, None, 5, "disk io"); m.execute("kill %d" % disk_pid) # Network. Anything above 300 kb/s is good for now. # identity = m._calc_identity() m.upload([ identity ], "/var/tmp") net_pid = m.spawn("ssh -o StrictHostKeyChecking=no -i /var/tmp/{1} {0} cat /dev/zero >/dev/null".format(m.address, os.path.basename(identity)), "load-net.log") b.wait_js_func("ph_flot_data_plateau", "#server_network_traffic_graph", 300*1000, None, 5, "net io"); m.execute("kill %d" % net_pid) @skipImage("No PCP available on Atomic", "fedora-atomic", "rhel-atomic", "continuous-atomic") @skipImage("No PCP available on Debian testing/unstable", "debian-testing") def testPcp(self): m = self.machine self.login_and_go("/system") self.check_host_metrics() m.execute("pgrep cockpit-pcp") def testInternal(self): m = self.machine m.execute("! [ -f /usr/libexec/cockpit-pcp ] || rm /usr/libexec/cockpit-pcp") m.execute("! [ -f /usr/lib/cockpit/cockpit-pcp ] || rm /usr/lib/cockpit/cockpit-pcp") self.login_and_go("/system") self.check_host_metrics() m.execute("! pgrep cockpit-pcp") if __name__ == '__main__': test_main()