def get_cpustats(vm, cpu=None): """ Get the cpustats output of a given domain :param vm: VM domain :param cpu: Host cpu index, default all cpus :return: dict of cpu stats values result format: {0:[vcputime,emulatortime,cputime] .. 'total':[cputime]} """ host_cpu_online = utils.cpu_online_list() cpustats = {} if cpu: cpustats[cpu] = [] option = "--start %s --count 1" % cpu result = virsh.cpu_stats(vm.name, option) if result.exit_status != 0: logging.error("cpu stats command failed: %s", results_stderr_52lts(result)) return None output = results_stdout_52lts(result).strip().split() if re.match("CPU%s" % cpu, output[0]): cpustats[cpu] = [ float(output[5]), # vcputime float(output[2]) - float(output[5]), # emulator float(output[2]) ] # cputime else: for i in range(len(host_cpu_online)): cpustats[host_cpu_online[i]] = [] option = "--start %s --count 1" % host_cpu_online[i] result = virsh.cpu_stats(vm.name, option) if result.exit_status != 0: logging.error("cpu stats command failed: %s", results_stderr_52lts(result)) return None output = results_stdout_52lts(result).strip().split() if re.match("CPU%s" % host_cpu_online[i], output[0]): cpustats[host_cpu_online[i]] = [ float(output[5]), float(output[2]) - float(output[5]), float(output[2]) ] result = virsh.cpu_stats(vm.name, "--total") cpustats["total"] = [] if result.exit_status != 0: logging.error("cpu stats command failed: %s", results_stderr_52lts(result)) return None output = results_stdout_52lts(result).strip().split() cpustats["total"] = [float(output[2])] # cputime return cpustats
def get_cpustats(vm, cpu=None): """ Get the cpustats output of a given domain :param vm: VM domain :param cpu: Host cpu index, default all cpus :return: dict of cpu stats values result format: {0:[vcputime,emulatortime,cputime] .. 'total':[cputime]} """ host_cpu_online = utils.cpu_online_list() cpustats = {} if cpu: cpustats[cpu] = [] option = "--start %s --count 1" % cpu result = virsh.cpu_stats(vm.name, option) if result.exit_status != 0: logging.error("cpu stats command failed: %s", results_stderr_52lts(result)) return None output = results_stdout_52lts(result).strip().split() if re.match("CPU%s" % cpu, output[0]): cpustats[cpu] = [float(output[5]), # vcputime float(output[2]) - float(output[5]), # emulator float(output[2])] # cputime else: for i in range(len(host_cpu_online)): cpustats[host_cpu_online[i]] = [] option = "--start %s --count 1" % host_cpu_online[i] result = virsh.cpu_stats(vm.name, option) if result.exit_status != 0: logging.error("cpu stats command failed: %s", results_stderr_52lts(result)) return None output = results_stdout_52lts(result).strip().split() if re.match("CPU%s" % host_cpu_online[i], output[0]): cpustats[host_cpu_online[i]] = [float(output[5]), float(output[2]) - float(output[5]), float(output[2])] result = virsh.cpu_stats(vm.name, "--total") cpustats["total"] = [] if result.exit_status != 0: logging.error("cpu stats command failed: %s", results_stderr_52lts(result)) return None output = results_stdout_52lts(result).strip().split() cpustats["total"] = [float(output[2])] # cputime return cpustats
# Check vcpu number inside the domian if vm.state() == "running": session = vm.wait_for_login() cmd = "cat /proc/cpuinfo | grep processor | wc -l" try: output = session.cmd_output(cmd, timeout=10).strip() finally: session.close() if output != expect_vcpu_num[-1]: raise error.TestFail("Find %s CPUs in domain but expect %s" % (output, expect_vcpu_num[-1])) else: logging.debug("Find %s CPUs in domian as expected", output) # Check cpu-stats command, only for running domian result = virsh.cpu_stats(vm.name, "", ignore_status=True, debug=True) libvirt.check_exit_status(result) def manipulate_domain(vm_name, vm_operation, recover=False): """ Operate domain to given state or recover it. """ save_file = os.path.join(data_dir.get_tmp_dir(), vm_name + ".save") if not recover: if vm_operation == "save": save_option = "" result = virsh.save(vm_name, save_file, save_option, ignore_status=True,
def run(test, params, env): """ Test virsh cpu-stats command. The command can display domain per-CPU and total statistics. 1. Call virsh cpu-stats [domain] 2. Call virsh cpu-stats [domain] with valid options 3. Call virsh cpu-stats [domain] with invalide options """ if not virsh.has_help_command('cpu-stats'): raise error.TestNAError("This version of libvirt does not support " "the cpu-stats test") vm_name = params.get("main_vm", "vm1") vm_ref = params.get("cpu_stats_vm_ref") status_error = params.get("status_error", "no") options = params.get("cpu_stats_options") logging.debug("options are %s", options) if vm_ref == "name": vm_ref = vm_name # get host cpus num cpus = multiprocessing.cpu_count() logging.debug("host cpu num is %s", cpus) # get options and put into a dict get_total = re.search('total', options) get_start = re.search('start', options) get_count = re.search('count', options) # command without options get_noopt = 0 if not get_total and not get_start and not get_count: get_noopt = 1 # command with only --total option get_totalonly = 0 if not get_start and not get_count and get_total: get_totalonly = 1 option_dict = {} if options.strip(): option_list = options.split('--') logging.debug("option_list is %s", option_list) for match in option_list[1:]: if get_start or get_count: option_dict[match.split(' ')[0]] = match.split(' ')[1] # Run virsh command cmd_result = virsh.cpu_stats(vm_ref, options, ignore_status=True, debug=True) output = cmd_result.stdout.strip() status = cmd_result.exit_status # check status_error if status_error == "yes": if status == 0: raise error.TestFail("Run successfully with wrong command!") elif status_error == "no": if status != 0: raise error.TestFail("Run failed with right command") else: # Get cgroup cpu_time if not get_totalonly: vm = env.get_vm(vm_ref) cgpath = utils_cgroup.resolve_task_cgroup_path( vm.get_pid(), "cpuacct") # When a VM has an 'emulator' child cgroup present, we must # strip off that suffix when detecting the cgroup for a machine if os.path.basename(cgpath) == "emulator": cgpath = os.path.dirname(cgpath) usage_file = os.path.join(cgpath, "cpuacct.usage_percpu") cgtime = file(usage_file).read().strip().split() logging.debug("cgtime get is %s", cgtime) # Cut CPUs from output and format to list output = re.sub(r'\.', '', output) if get_total: mt_start = re.search('Total', output).start() else: mt_start = len(output) output_cpus = " ".join(output[:mt_start].split()) cpus_list = re.compile(r'CPU\d+:').split(output_cpus) # conditions that list total time info if get_noopt or get_total: mt_end = re.search('Total', output).end() total_list = output[mt_end + 1:].split() total_time = int(total_list[1]) user_time = int(total_list[4]) system_time = int(total_list[7]) # check Total cpu_time >= User + System cpu_time if user_time + system_time >= total_time: raise error.TestFail("total cpu_time < user_time + " "system_time") logging.debug("Check total cpu_time %d >= user + system " "cpu_time %d", total_time, user_time + system_time) start_num = 0 if get_start: start_num = int(option_dict["start"]) end_num = int(cpus) if get_count: count_num = int(option_dict["count"]) if end_num > start_num + count_num: end_num = start_num + count_num # for only give --total option it only shows "Total" cpu info if get_totalonly: end_num = -1 # find CPU[N] in output and sum the cpu_time and cgroup cpu_time sum_cputime = 0 sum_cgtime = 0 logging.debug("start_num %d, end_num %d", start_num, end_num) for i in range(start_num, end_num): if not re.search('CPU' + "%i" % i, output): raise error.TestFail("Fail to find CPU" + "%i" % i + "in " "result") logging.debug("Check CPU" + "%i" % i + " exist") sum_cputime += int(cpus_list[i - start_num + 1].split()[1]) sum_cgtime += int(cgtime[i]) # check cgroup cpu_time > sum of cpu_time if end_num >= 0: if sum_cputime > sum_cgtime: raise error.TestFail("Check sum of cgroup cpu_time < sum " "of output cpu_time") logging.debug("Check sum of cgroup cpu_time %d >= cpu_time %d", sum_cgtime, sum_cputime) # check Total cpu_time >= sum of cpu_time when no options if get_noopt: if total_time < sum_cputime: raise error.TestFail("total time < sum of output cpu_time") logging.debug("Check total time %d >= sum of output cpu_time" " %d", total_time, sum_cputime)
# Check vcpu number inside the domian if vm.state() == "running": session = vm.wait_for_login() cmd = "cat /proc/cpuinfo | grep processor | wc -l" try: output = session.cmd_output(cmd, timeout=10).strip() finally: session.close() if output != expect_vcpu_num[-1]: raise error.TestFail("Find %s CPUs in domain but expect %s" % (output, expect_vcpu_num[-1])) else: logging.debug("Find %s CPUs in domian as expected", output) # Check cpu-stats command, only for running domian result = virsh.cpu_stats(vm.name, "", ignore_status=True, debug=True) libvirt.check_exit_status(result) def manipulate_domain(vm_name, vm_operation, recover=False): """ Operate domain to given state or recover it. """ save_file = os.path.join(data_dir.get_tmp_dir(), vm_name + ".save") if not recover: if vm_operation == "save": save_option = "" result = virsh.save(vm_name, save_file, save_option, ignore_status=True, debug=True) libvirt.check_exit_status(result) elif vm_operation == "managedsave":
def run(test, params, env): """ Test virsh cpu-stats command. The command can display domain per-CPU and total statistics. 1. Call virsh cpu-stats [domain] 2. Call virsh cpu-stats [domain] with valid options 3. Call virsh cpu-stats [domain] with invalid options """ def get_cpuacct_info(suffix): """ Get the CPU accounting info within the vm :param suffix: str, suffix of the CPU accounting.(stat/usage/usage_percpu) :return: list, the list of CPU accounting info """ if 'cg_obj' not in locals(): return # On cgroup v2 use cpu.stat as a substitute if cg_obj.is_cgroup_v2_enabled(): cg_path = cg_obj.get_cgroup_path("cpu") para = ('cpu.%s' % suffix) else: cg_path = cg_obj.get_cgroup_path("cpuacct") para = ('cpuacct.%s' % suffix) # We only need the info in file which "emulator" is not in path if os.path.basename(cg_path) == "emulator": cg_path = os.path.dirname(cg_path) usage_file = os.path.join(cg_path, para) with open(usage_file, 'r') as f: cpuacct_info = f.read().strip().split() logging.debug("cpuacct info %s", cpuacct_info) return cpuacct_info def check_user_and_system_time(total_list): user_time = float(total_list[4]) system_time = float(total_list[7]) # Check libvirt user and system time between pre and next cgroup time # Unit conversion (Unit: second) # Default time unit is microseconds on cgroup v2 while 1/100 second on v1 if cg_obj.is_cgroup_v2_enabled(): pre_user_time = float(cpuacct_res_pre[3]) / 1000000 pre_sys_time = float(cpuacct_res_pre[5]) / 1000000 next_user_time = float(cpuacct_res_next[3]) / 1000000 next_sys_time = float(cpuacct_res_next[5]) / 1000000 else: pre_user_time = float(cpuacct_res_pre[1]) / 100 pre_sys_time = float(cpuacct_res_pre[3]) / 100 next_user_time = float(cpuacct_res_next[1]) / 100 next_sys_time = float(cpuacct_res_next[3]) / 100 # check user_time if next_user_time >= user_time >= pre_user_time: logging.debug("Got the expected user_time: %s", user_time) else: test.fail("Got unexpected user_time: %s, " % user_time + "should between pre_user_time:%s " % pre_user_time + "and next_user_time:%s" % next_user_time) # check system_time if next_sys_time >= system_time >= pre_sys_time: logging.debug("Got the expected system_time: %s", system_time) else: test.fail("Got unexpected system_time: %s, " % system_time + "should between pre_sys_time:%s " % pre_sys_time + "and next_sys_time:%s" % next_sys_time) if not virsh.has_help_command('cpu-stats'): test.cancel("This version of libvirt does not support " "the cpu-stats test") vm_name = params.get("main_vm", "vm1") vm_ref = params.get("cpu_stats_vm_ref") status_error = params.get("status_error", "no") options = params.get("cpu_stats_options") error_msg = params.get("error_msg", "") logging.debug("options are %s", options) if vm_ref == "name": vm_ref = vm_name vm = env.get_vm(vm_ref) if vm and vm.get_pid(): cg_obj = libvirt_cgroup.CgroupTest(vm.get_pid()) # get host cpus num cpus = cpu.online_cpus_count() logging.debug("host online cpu num is %s", cpus) # get options and put into a dict get_total = re.search('total', options) get_start = re.search('start', options) get_count = re.search('count', options) # command without options get_noopt = 0 if not get_total and not get_start and not get_count: get_noopt = 1 # command with only --total option get_totalonly = 0 if not get_start and not get_count and get_total: get_totalonly = 1 option_dict = {} if options.strip(): option_list = options.split('--') logging.debug("option_list is %s", option_list) for match in option_list[1:]: if get_start or get_count: option_dict[match.split(' ')[0]] = match.split(' ')[1] # check if cpu is enough,if not cancel the test if (status_error == "no"): cpu_start = int(option_dict.get("start", "0")) if cpu_start == 32: cpus = cpu.total_cpus_count() logging.debug("Host total cpu num: %s", cpus) if (cpu_start >= cpus): test.cancel("Host cpus are not enough") # get CPU accounting info twice to compare with user_time and system_time cpuacct_res_pre = get_cpuacct_info('stat') # Run virsh command cmd_result = virsh.cpu_stats(vm_ref, options, ignore_status=True, debug=True) output = cmd_result.stdout.strip() status = cmd_result.exit_status cpuacct_res_next = get_cpuacct_info('stat') # check status_error if status_error == "yes": if status == 0: test.fail("Run successfully with wrong command! Output: {}".format( output)) else: # Check error message is expected if not re.search(error_msg, cmd_result.stderr.strip()): test.fail("Error message is not expected! " "Expected: {} Actual: {}".format( error_msg, cmd_result.stderr.strip())) elif status_error == "no": if status != 0: test.fail("Run failed with right command! Error: {}".format( cmd_result.stderr.strip())) else: # Get cgroup cpu_time if not get_totalonly: cgtime = get_cpuacct_info('usage_percpu') # Cut CPUs from output and format to list if get_total: mt_start = re.search('Total', output).start() else: mt_start = len(output) output_cpus = " ".join(output[:mt_start].split()) cpus_list = re.compile(r'CPU\d+:').split(output_cpus) # conditions that list total time info if get_noopt or get_total: mt_end = re.search('Total', output).end() total_list = output[mt_end + 1:].split() total_time = float(total_list[1]) check_user_and_system_time(total_list) start_num = 0 if get_start: start_num = int(option_dict["start"]) end_num = int(cpus) if get_count: count_num = int(option_dict["count"]) if end_num > start_num + count_num: end_num = start_num + count_num # for only give --total option it only shows "Total" cpu info if get_totalonly: end_num = -1 # find CPU[N] in output and sum the cpu_time and cgroup cpu_time sum_cputime = 0 sum_cgtime = 0 logging.debug("start_num %d, end_num %d", start_num, end_num) for i in range(start_num, end_num): logging.debug("Check CPU" + "%i" % i + " exist") sum_cputime += float(cpus_list[i - start_num + 1].split()[1]) sum_cgtime += float(cgtime[i]) if not re.search('CPU' + "%i" % i, output): test.fail("Fail to find CPU" + "%i" % i + "in " "result") # check cgroup cpu_time > sum of cpu_time if end_num >= 0: logging.debug("Check sum of cgroup cpu_time %d >= cpu_time %d", sum_cgtime, sum_cputime) if sum_cputime > sum_cgtime: test.fail("Check sum of cgroup cpu_time < sum " "of output cpu_time") # check Total cpu_time >= sum of cpu_time when no options if get_noopt: logging.debug( "Check total time %d >= sum of output cpu_time" " %d", total_time, sum_cputime) if total_time < sum_cputime: test.fail("total time < sum of output cpu_time")