On 09/17/12 10:29am, Chris Evich wrote:
> 
> 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.
>

Thank You for reviewing. I have made the necessary changes and testing is
underway. I'll send you the v2 asap.

I have raised the topic/issues in github for this and things am working on.
 
-- 
        -prem

_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel

Reply via email to