Hey,
This is looking good. A few minor notes that will save you some effort
in the future:
+ There's some 'funny' 6-space indentation in this module. You can use
autotest/utils/reindend.py to fix this automatically.
+ We ship a wrapper for pylint in autotest/utils/run_pylint.py which can
help find problems automatically. It's very verbose by default, but
does find stuff our eyeballs miss sometimes. For example, here are some
things that should be investigated:
# ../utils/run_pylint.py tests/virt/libvirt/tests/virsh_cpu_stats.py
...
W0311:317: Bad indentation. Found 8 spaces, expected 12
...
W0613: 89:run_virsh_cpu_stats.getlist_expected_cpus: Unused argument
'actual_cmd_stdout'
...
W0622:100:run_virsh_cpu_stats.get_cpu_time: Redefining built-in 'type'
...
W0613:118:run_virsh_cpu_stats.get_cgroup_cputime: Unused argument
'actual_cmd_stdout'
...
W0101:165:run_virsh_cpu_stats.compute_and_test_results: Unreachable code
...
W0101:192:run_virsh_cpu_stats.test_total_cpustats: Unreachable code
...
E9900:305:run_virsh_cpu_stats: Unsupported format character "'" (0x27)
at index 26
On 09/13/2012 01:38 PM, Prem Karat wrote:
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.
Nice detailed commit message, well done.
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
+ """
Non-critical Suggestion: This can be less-verbose. Since params/options
are likely to change over time, no need to specify them. You could just
say "Invoke virsh cpu-stats with options"
+
+ # 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']:
Why not
vm = env.get_vm(params[vm_name])
vm.verify_alive()
?
+ 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="")
No need to specify an empty URI here since that's equivalent to the default.
+ 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())
Calls to this wrapper function can just be replaced with:
virsh.cpu_stats(vm_name, extra, debug=True)
I think we can stick the ignore_status=True into the virsh function
directly since that's probably how it will be called most of the time.
+ 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
Why is this /2? Could use a comment here if it's necessary.
+ 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)
Since this one-line function is only called once, consider just putting
it inline with the code.
+
+
+ # Generate a list of expected cpus from start_cpu to end_cpu
These descriptive comments for functions can be put into docstrings,
since that's what they're intended for.
+ 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]))
Why not save the result of the logging/exception string to a variable,
then reference it to logging.info() and exception? Since the format is
fairly complex, this will make maintaining it easier.
+ 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
+
Otherwise this is looking good. Please add this test to the tracking
issue here: https://github.com/autotest/autotest/issues/497 this way
someone else won't duplicate your work. Thanks.
--
Chris Evich, RHCA, RHCE, RHCDS, RHCSS
Quality Assurance Engineer
e-mail: cevich + `@' + redhat.com o: 1-888-RED-HAT1 x44214
_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel