Add virsh_cpu_stats.py to test virsh cpu-stats command
Test the virsh cpu-stats command
(1) Invoke virsh cpu-stats <domain> <options>
(2) Invoke virsh cpu-stats with following possible combination of options
a. None e. --start --count
b. --start f. --start --total
c. --count g. --count --total
d. --total h. --start --count --total
(3) Invoke virsh cpu-stats with a numeric argument
(4) Invoke virsh cpu-stats with an invalid string "xyz"
(5) Invoke virsh cpu-stats with above combinations with libvitrd service
stop
Pass Condition:
==============
1. When option = 'total'
a. Displayed Total cpu_time > User cpu_time > System cpu_time
b. Displayed Total cpu_time >= User + System cpu_time
2. When option = 'start' or 'count'
a. Displayed CPU's == Expected CPU's
b. Cgroup cpu_time* >= sum of cpu_time of each cpu's displayed
3. When option has a combination of 'start', 'count', 'total'
All the above conditions 1 & 2 are checked.
4. When option is None
a. Displayed Total cpu_time >= sum of cpu_time of all cpu's displayed
b. Pass condition when option = 'total' (1) is checked.
*Cgroup cpu_time is computed by reading domain specific cpuacct.usage_percpu
Delay in reading the percpu file will lead up to cgropu cpu_time being slightly
higher (in ns) than the displayed time.
Signed-off-by: Prem Karat <[email protected]>
---
client/tests/virt/libvirt/tests/virsh_cpu_stats.py | 318 ++++++++++++++++++++
1 file changed, 318 insertions(+)
create mode 100644 client/tests/virt/libvirt/tests/virsh_cpu_stats.py
diff --git a/client/tests/virt/libvirt/tests/virsh_cpu_stats.py
b/client/tests/virt/libvirt/tests/virsh_cpu_stats.py
new file mode 100644
index 0000000..5ed3a5d
--- /dev/null
+++ b/client/tests/virt/libvirt/tests/virsh_cpu_stats.py
@@ -0,0 +1,318 @@
+import re, logging
+from autotest.client.shared import utils, error
+from virttest import libvirt_vm, virsh, utils_test
+from autotest.client import utils
+
+def run_virsh_cpu_stats(test, params, env):
+ """
+ Test the virsh cpu-stats command
+
+ (1) Invoke virsh cpu-stats <domain> <options>
+ (2) Invoke virsh cpu-stats with following possible combination of options
+ a. None e. --start --count
+ b. --start f. --start --total
+ c. --count g. --count --total
+ d. --total h. --start --count --total
+ (3) Invoke virsh cpu-stats with a numeric argument
+ (4) Invoke virsh cpu-stats with an invalid string "xyz"
+ (5) Invoke virsh cpu-stats with above combinations libvitrd service stop
+ """
+
+ # Prepare libvirtd service
+ libvirtd = params.get("libvirtd")
+ if libvirtd == "off":
+ libvirt_vm.service_libvirtd_control("stop")
+ elif not libvirt_vm.service_libvirtd_control("status"):
+ libvirt_vm.service_libvirtd_control("start")
+
+ # Get the vm name and check if its active. Run the test only if its active
+ vm_name = params.get("main_vm")
+ if libvirtd == "on":
+ state = virsh.domstate(vm_name, ignore_status=True)
+ if state not in ['running', 'paused', 'idle']:
+ raise error.TestFail("Cannot Execute Test. "
+ "%s domain not active" % vm_name)
+
+
+ # Get the actual output by running cpu-stats command
+ def run_cpu_stat_cmd(parameters):
+ cmd_result = virsh.cpu_stats(vm_name, extra=parameters, \
+ ignore_status=True, uri="")
+ logging.info("Command Output:\n%s", cmd_result.stdout.strip())
+ logging.info("Command Exit Status: %d", cmd_result.exit_status)
+ logging.error("Command Error:\n\t %s", cmd_result.stderr.strip())
+ return cmd_result
+
+
+ # Compute start and end values for options
+ # Build the command output with final options. Pass appropriate
+ # param values
+ def build_output(option):
+ possible_options = ['--start', '--count', '--start --count', \
+ '--start --total', '--count --total', \
+ '--start --count --total']
+ if option in possible_options:
+ start_value = utils.count_cpus()/2
+ count_value = start_value - 1
+ else:
+ count_value = utils.count_cpus()/2 - 1
+ if option == "--start":
+ option = option + " " + str(start_value)
+ return run_cpu_stat_cmd(option)
+ elif option == "--count":
+ option = option + " " + str(count_value)
+ return run_cpu_stat_cmd(option)
+ elif option == "--start --count":
+ option = "--start " + str(start_value) + " --count " \
+ + str(count_value)
+ return run_cpu_stat_cmd(option)
+ elif option == "--start --total":
+ option = "--start " + str(start_value) + " --total"
+ return run_cpu_stat_cmd(option)
+ elif option == "--count --total":
+ option = "--count " + str(count_value) + " --total"
+ return run_cpu_stat_cmd(option)
+ elif option == "--start --count --total":
+ option = "--start " + str(start_value) + " --count " \
+ + str(count_value) + " --total"
+ return run_cpu_stat_cmd(option)
+ else:
+ return run_cpu_stat_cmd(option)
+
+
+ # Generate a list of displayed cpus in cmd stdout
+ def getlist_displayed_cpus(actual_cmd_stdout):
+ return re.findall(r"(CPU\d+):", actual_cmd_stdout)
+
+
+ # Generate a list of expected cpus from start_cpu to end_cpu
+ def getlist_expected_cpus(actual_cmd_stdout, start_cpu, end_cpu):
+ expected_cpus = []
+ for index in range(start_cpu, end_cpu):
+ cpu = "CPU" + str(index)
+ expected_cpus.append(cpu)
+ return expected_cpus
+
+
+ # compute cpu time based on the type of cpu_time requested
+ # Type = Total or sum of each cpus. If Total cpu time is
+ # requested compute user & system time
+ def get_cpu_time(type, actual_cmd_stdout, condition):
+ cpu_time = re.findall(r"%s\s*cpu_time\s*(\d*.\d*)\sseconds" %condition,
+ actual_cmd_stdout)
+ if type == "Total":
+ user_time = re.findall(r"\s*user_time\s*(\d*.\d*)\sseconds", \
+ actual_cmd_stdout)
+ system_time = re.findall(r"\s*system_time\s*(\d*.\d*)\sseconds", \
+ actual_cmd_stdout)
+ return cpu_time + user_time + system_time
+ elif type == "sum_of_each":
+ cmdout_cpu_time = 0.0
+ for time in cpu_time:
+ cmdout_cpu_time += float(time)
+ return cmdout_cpu_time
+
+
+ # compute the cpu_time of start_cpu to end_cpu by reading the cgroup
+ # cpuacct.usage_percpu file from cgroup cpuacct controller mount point
+ def get_cgroup_cputime(actual_cmd_stdout, start_cpu, end_cpu):
+ cgrp_cpu_time = 0.0
+ # Generate a list of cpu_time of corresponding cpus from cgroup file
+ cpuacct_percpu = utils_test.domstat_cgroup_cpuacct_percpu(vm_name)
+ # Compute the sum of cpu_time of correspondig cpus from cgroup file
+ for cpu_time in cpuacct_percpu[start_cpu: end_cpu ]:
+ cgrp_cpu_time += float(float(cpu_time)/float(1000000000))
+ return cgrp_cpu_time
+
+
+ # Extract the displayed cpu's in cmd stdout
+ # Build the expected cpus list using start_cpu and end_cpu param
+ # Extract and compute the sum of cpu_time of each cpus
+ # Extract and compute the sum of corresponding cpus from cgroup cpuacct
+ # controller file (cpuacct.usage_percpu)
+ # Displayed cpus should be equal to expected cpus
+ # cgrop cpu time >= displayed cpu time (the value could increase due the
+ # delay in reading the file
+ def compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu):
+ # Gather the actual and expected output
+ displayed_cpus = getlist_displayed_cpus(actual_cmd_stdout)
+ expected_cpus = getlist_expected_cpus(actual_cmd_stdout, \
+ start_cpu, end_cpu)
+ cmdout_cpu_time = get_cpu_time("sum_of_each", actual_cmd_stdout, \
+ "(?<=CPU)\d*:\n")
+ cgrp_cpu_time = get_cgroup_cputime(actual_cmd_stdout, \
+ start_cpu, end_cpu)
+ logging.info("\n\t\tPass condition:" \
+ "\n\t\t\tDisplayed cpus == Expected cpus" \
+ "\n\t\t\tCgroup cpu_time >= Displayed cpu_time")
+ # compare the actual and expected output
+ if expected_cpus == displayed_cpus and \
+ cgrp_cpu_time >= cmdout_cpu_time:
+ logging.info("\n\t\t\tDisplayed cpus = %s" \
+ "\n\t\t\tExpected cpus = %s" \
+ "\n\t\t\tCgroup cpu_time = %s" \
+ "\n\t\t\tDisplayed cpu_time = %s" \
+ %(displayed_cpus, expected_cpus, cgrp_cpu_time, \
+ cmdout_cpu_time))
+ return True
+ else:
+ raise error.TestFail("\n\t\t\tDisplayed cpus = %s" \
+ "\n\t\t\tExpected cpus = %s" \
+ "\n\t\t\tCgroup cpu_time = %s\n" \
+ "\n\t\t\tDisplayed cpu_time = %s" \
+ %(displayed_cpus, expected_cpus, cgrp_cpu_time, \
+ cmdout_cpu_time))
+ return False
+
+
+ # The Total cpu time should be always greater than the sum of User time
+ # (time spend in user mode) and system time (kernel mode). The total cpu
+ # time will be a sum of user + system + steal time + irq etc..
+ # The user time will always be more than the system time.
+ def test_total_cpustats(cpu_stat):
+ logging.info("\n\t\tPass Condition:" \
+ "\n\t\t\tTotal cpu_time > User cpu_time > " \
+ "System cpu_time" \
+ "\n\t\t\tTotal cpu_time > User cpu_time + " \
+ "System cpu_time")
+ if float(cpu_stat[0]) > float(cpu_stat[1]) and \
+ float(cpu_stat[0]) > float(cpu_stat[2]) and \
+ float(cpu_stat[1]) > float(cpu_stat[2]) and \
+ float(cpu_stat[0]) >= (float(cpu_stat[1]) + float(cpu_stat[2])):
+ logging.info("\n\t\t\tTotal cpu_time = %s" \
+ "\n\t\t\tUser cpu_time = %s" \
+ "\n\t\t\tSystem cpu_time = %s" \
+ %(cpu_stat[0], cpu_stat[1], cpu_stat[2]))
+ return True
+ else:
+ raise error.TestFail("\n\t\t\tTotal cpu_time = %s" \
+ "\n\t\t\tUser cpu_time = %s" \
+ "\n\t\t\tSystem cpu_time = %s" \
+ %(cpu_stat[0], cpu_stat[1], cpu_stat[2]))
+ return False
+
+ def validate_output(actual_cmd_stdout, option):
+ option_has_total = ['', '--total', '--start --total', '--count --total',
+ '--start --count --total']
+ if option in option_has_total:
+ cpu_stat = get_cpu_time("Total", actual_cmd_stdout, "(?<=Total:)")
+
+ # If option == null, total_precpu_time = sum of cpu_time of all cpus
+ # (count_cpus, i.e CPU0-N) in cmd_stdout. For test to pass,
+ # Total cpu_time (displayed in stdout) >= total_percpu_time
+ # Tota cpu_time > user_time > system_time
+ # Total_cpu_time >= user_time + system_time.
+ # user_time > system_time. Else o/p is invalid
+
+ if option == "":
+ total_percpu_time = get_cpu_time("sum_of_each", actual_cmd_stdout, \
+ "(?<=CPU)\d*:\n")
+ if float(cpu_stat[0]) >= total_percpu_time and \
+ test_total_cpustats(cpu_stat):
+ logging.info( "Total cpu_time >= Sum of each cpu_time")
+ logging.info("\n\t\t\tTotal cpu_time = %s" \
+ "\n\t\t\tSum of each cpu_time = %s" \
+ %(cpu_stat[0], total_percpu_time))
+ return True
+ else:
+ raise error.TestFail("\n\t\t\tTotal cpu_time = %s" \
+ "\n\t\t\tUser cpu_time = %s" \
+ "\n\t\t\tSystem cpu_time = %s" \
+ "\n\t\t\tSum of cpu_time of individual cpus = %s" \
+ %(cpu_stat[0], cpu_stat[1], cpu_stat[2], total_percpu_time))
+
+ # if option == start || count, then test if expected cpus are
+ # displayed in command stdout.
+ # compute the sum of cpu_time of displayed cpus in stdout and compare
+ # the value with sum of cpu_time computed from cgroup
+ # cpuacct.usage_percpu file for the corresponding cpus
+
+ elif option == "--start":
+ start_cpu = utils.count_cpus()/2
+ if start_cpu < 0:
+ start_cpu = 0
+ end_cpu = len(utils_test.domstat_cgroup_cpuacct_percpu(vm_name))
+ compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu)
+
+ elif option == "--count":
+ start_cpu = 0
+ end_cpu = utils.count_cpus()/2 - 1
+ if end_cpu < 0:
+ end_cpu = len(utils_test.domstat_cgroup_cpuacct_percpu(vm_name))
+ compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu)
+
+ # if option == total, for test to pass, values from stdout should be
+ # Tota cpu_time > user_time > system_time
+ # Total_cpu_time >= user_time + system_time.
+ # user_time > system_time. Else o/p is invalid
+
+ elif option == "--total":
+ test_total_cpustats(cpu_stat)
+
+ elif option == "--start --count":
+ start_cpu = utils.count_cpus()/2
+ if start_cpu < 0:
+ start_cpu = 0
+ end_cpu = start_cpu + start_cpu - 1
+ if end_cpu < 0:
+ end_cpu = len(utils_test.domstat_cgroup_cpuacct_percpu(vm_name))
+ compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu)
+
+ elif option == "--start --total":
+ start_cpu = utils.count_cpus()/2
+ if start_cpu < 0:
+ start_cpu = 0
+ end_cpu = len(utils_test.domstat_cgroup_cpuacct_percpu(vm_name))
+ compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu)
+ test_total_cpustats(cpu_stat)
+
+ elif option == "--count --total":
+ start_cpu = 0
+ end_cpu = utils.count_cpus()/2 - 1
+ if end_cpu < 0:
+ end_cpu = len(utils_test.domstat_cgroup_cpuacct_percpu(vm_name))
+ compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu)
+ test_total_cpustats(cpu_stat)
+
+ elif option == "--start --count --total":
+ start_cpu = utils.count_cpus()/2
+ if start_cpu < 0:
+ start_cpu = 0
+ end_cpu = start_cpu + start_cpu - 1
+ if end_cpu < 0:
+ end_cpu = len(utils_test.domstat_cgroup_cpuacct_percpu(vm_name))
+ compute_and_test_results(actual_cmd_stdout, start_cpu, end_cpu)
+ test_total_cpustats(cpu_stat)
+
+ return True
+
+ # Run the test cases based on the appropriate cmd options.
+ option = params.get("virsh_cpu_stats_options")
+
+ actual_cmd_output = build_output(option)
+ actual_cmd_stdout = actual_cmd_output.stdout.strip()
+ actual_status = actual_cmd_output.exit_status
+
+ # Recover libvirtd Service which will be turned off by error check code.
+ if libvirtd == "off":
+ libvirt_vm.service_libvirtd_control("start")
+
+ # Check status_error
+ status_error = params.get("status_error")
+ if status_error == "yes":
+ if actual_status == 0:
+ if libvirtd == "off":
+ raise error.TestFail("Command 'virsh cpu-stats %' succeeded "
+ "with libvirtd service stopped, "
+ "incorrect" % option)
+ else:
+ raise error.TestFail("Command 'virsh cpu-stats %s' succeeded"
+ " (incorrect command)" % option)
+ else:
+ if actual_status != 0:
+ raise error.TestFail("Command 'virsh cpu-stats %s' failed "
+ "(correct command)" % option)
+ else:
+ validate_output(actual_cmd_stdout, option)
+ return True
+
--
1.7.10.4
--
-prem
_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel