physical_resources_check.py 14.2 KB
Newer Older
L
Lucas Meneghel Rodrigues 已提交
1 2 3 4
import re
import string
import logging
import random
5
from autotest.client.shared import error, utils
6
from virttest import qemu_monitor, storage, utils_misc, env_process, data_dir
7
from virttest import qemu_qtree
8 9


10
def run(test, params, env):
11 12 13 14 15 16 17 18 19
    """
    Check physical resources assigned to KVM virtual machines:
    1) Log into the guest
    2) Verify whether cpu counts ,memory size, nics' model,
       count and drives' format & count, drive_serial, UUID
       reported by the guest OS matches what has been assigned
       to the VM (qemu command line)
    3) Verify all MAC addresses for guest NICs

L
Lucas Meneghel Rodrigues 已提交
20 21 22
    :param test: QEMU test object.
    :param params: Dictionary with the test parameters.
    :param env: Dictionary with test environment.
23 24 25 26 27 28 29
    """
    # Define a function for checking number of hard drivers & NICs
    def check_num(devices, info_cmd, check_str):
        f_fail = []
        expected_num = params.objects(devices).__len__()
        o = ""
        try:
30
            o = vm.monitor.human_monitor_cmd("info %s " % info_cmd)
31
        except qemu_monitor.MonitorError, e:
32
            fail_log = str(e) + "\n"
33 34 35 36 37 38
            fail_log += "info/query monitor command failed (%s)" % info_cmd
            f_fail.append(fail_log)
            logging.error(fail_log)

        actual_num = string.count(o, check_str)
        if expected_num != actual_num:
L
Lucas Meneghel Rodrigues 已提交
39
            fail_log = "%s number mismatch:\n" % str(devices)
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
            fail_log += "    Assigned to VM: %d\n" % expected_num
            fail_log += "    Reported by OS: %d" % actual_num
            f_fail.append(fail_log)
            logging.error(fail_log)
        return expected_num, f_fail

    # Define a function for checking hard drives & NICs' model
    def chk_fmt_model(device, fmt_model, info_cmd, regexp):
        f_fail = []
        devices = params.objects(device)
        for chk_device in devices:
            expected = params.object_params(chk_device).get(fmt_model)
            if not expected:
                expected = "rtl8139"
            o = ""
            try:
56
                o = vm.monitor.human_monitor_cmd("info %s" % info_cmd)
57
            except qemu_monitor.MonitorError, e:
58
                fail_log = str(e) + "\n"
59 60 61 62 63 64 65 66 67 68 69 70
                fail_log += "info/query monitor command failed (%s)" % info_cmd
                f_fail.append(fail_log)
                logging.error(fail_log)

            device_found = re.findall(regexp, o)
            logging.debug("Found devices: %s", device_found)
            found = False
            for fm in device_found:
                if expected in fm:
                    found = True

            if not found:
L
Lucas Meneghel Rodrigues 已提交
71
                fail_log = "%s model mismatch:\n" % str(device)
72 73 74 75 76 77 78 79 80 81 82
                fail_log += "    Assigned to VM: %s\n" % expected
                fail_log += "    Reported by OS: %s" % device_found
                f_fail.append(fail_log)
                logging.error(fail_log)
        return f_fail

    # Define a function to verify UUID & Serial number
    def verify_device(expect, name, verify_cmd):
        f_fail = []
        if verify_cmd:
            actual = session.cmd_output(verify_cmd)
83
            if not re.findall(expect, actual, re.I):
L
Lucas Meneghel Rodrigues 已提交
84
                fail_log = "%s mismatch:\n" % name
85 86 87 88 89 90
                fail_log += "    Assigned to VM: %s\n" % string.upper(expect)
                fail_log += "    Reported by OS: %s" % actual
                f_fail.append(fail_log)
                logging.error(fail_log)
        return f_fail

91
    def get_cpu_number(chk_type, chk_timeout):
92
        """
93
        Get cpu sockets/cores/threads number.
94

L
Lucas Meneghel Rodrigues 已提交
95 96
        :param chk_type: Should be one of 'sockets', 'cores', 'threads'.
        :param chk_timeout: timeout of running chk_cmd.
97

98
        :return: Actual number of guest cpu number.
99
        """
100
        chk_str = params["mem_chk_re_str"]
101
        chk_cmd = params.get("cpu_%s_chk_cmd" % chk_type)
102

103 104 105
        if chk_cmd is None:
            fail_log = "Unknown cpu number checking type: '%s'" % chk_type
            logging.error(fail_log)
106
            return -1
107 108 109 110 111 112

        s, output = session.cmd_status_output(chk_cmd, timeout=chk_timeout)
        num = re.findall(chk_str, output)
        if s != 0 or not num:
            fail_log = "Failed to get guest %s number, " % chk_type
            fail_log += "guest output: '%s'" % output
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
            logging.error(fail_log)
            return -2

        logging.info("CPU %s number: %d",
                     string.capitalize(chk_type), int(num[-1]))
        return int(num[-1])

    def check_cpu_number(chk_type, actual_n, expected_n):
        """
        Checking cpu sockets/cores/threads number.

        :param chk_type: Should be one of 'sockets', 'cores', 'threads'.
        :param actual_n: Actual number of guest cpu number.
        :param expected_n: Expected number of guest cpu number.

        :return: a list that contains fail report.
        """
        f_fail = []

        if actual_n == -1:
            fail_log = "Unknown cpu number checking type: '%s'" % chk_type
            logging.error(fail_log)
135
            f_fail.append(fail_log)
136 137 138 139
            return f_fail

        if actual_n == -2:
            fail_log = "Failed to get guest %s number." % chk_type
140
            logging.error(fail_log)
141
            f_fail.append(fail_log)
142 143
            return f_fail

144 145
        logging.info("CPU %s number check", string.capitalize(chk_type))

146 147 148 149 150 151 152 153 154 155 156 157
        if actual_n != expected_n:
            fail_log = "%s output mismatch:\n" % string.capitalize(chk_type)
            fail_log += "    Assigned to VM: '%s'\n" % expected_n
            fail_log += "    Reported by OS: '%s'" % actual_n
            f_fail.append(fail_log)
            logging.error(fail_log)
            return f_fail

        logging.debug("%s check pass. Expected: '%s', Actual: '%s'",
                      string.capitalize(chk_type), expected_n, actual_n)
        return f_fail

158 159 160
    def verify_machine_type():
        f_fail = []
        cmd = params.get("check_machine_type_cmd")
161
        fail_log = ""
162 163 164 165

        if cmd is None:
            return f_fail

166 167
        status, actual_mtype = session.cmd_status_output(cmd)
        if status != 0:
168 169
            raise error.TestError("Failed to get machine type from vm")

170 171 172 173 174 175 176 177 178 179 180
        machine_type_cmd = "%s -M ?" % utils_misc.get_qemu_binary(params)
        machine_types = utils.system_output(machine_type_cmd,
                                            ignore_status=True)
        machine_types = machine_types.split(':')[-1]
        machine_type_map = {}
        for machine_type in machine_types.splitlines():
            if not machine_type:
                continue
            type_pair = re.findall("([\w\.-]+)\s+([^(]+).*", machine_type)
            if len(type_pair) == 1 and len(type_pair[0]) == 2:
                machine_type_map[type_pair[0][0]] = type_pair[0][1]
181
            else:
182 183 184 185 186 187 188
                logging.warn("Unexpect output from qemu-kvm -M "
                             "?: '%s'" % machine_type)
        try:
            expect_mtype = machine_type_map[params['machine_type']].strip()
        except KeyError:
            logging.warn("Can not find machine type '%s' from qemu-kvm -M ?"
                         " output. Skip this test." % params['machine_type'])
189
            return f_fail
190 191 192 193

        if expect_mtype not in actual_mtype:
            fail_log += "    Assigned to VM: '%s' \n" % expect_mtype
            fail_log += "    Reported by OS: '%s'" % actual_mtype
194 195
            f_fail.append(fail_log)
            logging.error(fail_log)
196 197
        else:
            logging.info("MachineType check pass. Expected: %s, Actual: %s" %
198
                         (expect_mtype, actual_mtype))
199
        return f_fail
200

201 202 203 204 205 206
    if params.get("catch_serial_cmd") is not None:
        length = int(params.get("length", "20"))
        id_leng = random.randint(0, length)
        drive_serial = ""
        convert_str = "!\"#$%&\'()*+./:;<=>?@[\\]^`{|}~"
        drive_serial = utils_misc.generate_random_string(id_leng,
207 208
                                                         ignore_str=",",
                                                         convert_str=convert_str)
209 210 211 212 213 214 215 216 217 218 219

        params["drive_serial"] = drive_serial
        params["start_vm"] = "yes"

        vm = params["main_vm"]
        vm_params = params.object_params(vm)
        env_process.preprocess_vm(test, vm_params, env, vm)
        vm = env.get_vm(vm)
    else:
        vm = env.get_vm(params["main_vm"])

220 221 222 223
    vm.verify_alive()
    timeout = int(params.get("login_timeout", 360))
    chk_timeout = int(params.get("chk_timeout", 240))
    session = vm.wait_for_login(timeout=timeout)
224
    qtree = qemu_qtree.QtreeContainer()
225 226 227 228
    try:
        qtree.parse_info_qtree(vm.monitor.info('qtree'))
    except AttributeError:  # monitor doesn't support info qtree
        qtree = None
229 230 231 232 233 234 235 236 237

    logging.info("Starting physical resources check test")
    logging.info("Values assigned to VM are the values we expect "
                 "to see reported by the Operating System")
    # Define a failure counter, as we want to check all physical
    # resources to know which checks passed and which ones failed
    n_fail = []

    # We will check HDs with the image name
238
    image_name = storage.get_image_filename(params, data_dir.get_data_dir())
239 240 241 242

    # Check cpu count
    logging.info("CPU count check")
    actual_cpu_nr = vm.get_cpu_count()
243 244 245 246 247
    cpu_cores_num = get_cpu_number("cores", chk_timeout)
    cpu_lp_num = get_cpu_number("logical_processors", chk_timeout)
    cpu_threads_num = get_cpu_number("threads", chk_timeout)
    cpu_sockets_num = get_cpu_number("sockets", chk_timeout)

L
Lukáš Doktor 已提交
248 249
    if ((params.get("os_type") == 'windows') and cpu_cores_num > 0 and
            cpu_lp_num > 0 and cpu_sockets_num > 0):
250 251 252
        actual_cpu_nr = cpu_lp_num * cpu_sockets_num
        cpu_threads_num = cpu_lp_num / cpu_cores_num

253
    if vm.cpuinfo.smp != actual_cpu_nr:
L
Lucas Meneghel Rodrigues 已提交
254
        fail_log = "CPU count mismatch:\n"
255 256 257 258 259
        fail_log += "    Assigned to VM: %s \n" % vm.cpuinfo.smp
        fail_log += "    Reported by OS: %s" % actual_cpu_nr
        n_fail.append(fail_log)
        logging.error(fail_log)

260
    n_fail.extend(check_cpu_number("cores", cpu_cores_num, vm.cpuinfo.cores))
261

262 263
    n_fail.extend(check_cpu_number("threads",
                                   cpu_threads_num, vm.cpuinfo.threads))
264

265 266
    n_fail.extend(check_cpu_number("sockets",
                                   cpu_sockets_num, vm.cpuinfo.sockets))
267 268 269 270 271 272 273

    # Check the cpu vendor_id
    expected_vendor_id = params.get("cpu_model_vendor")
    cpu_vendor_id_chk_cmd = params.get("cpu_vendor_id_chk_cmd")
    if expected_vendor_id and cpu_vendor_id_chk_cmd:
        output = session.cmd_output(cpu_vendor_id_chk_cmd)

274
        if expected_vendor_id not in output:
275 276 277 278 279 280 281 282
            fail_log = "CPU vendor id check failed.\n"
            fail_log += "    Assigned to VM: '%s'\n" % expected_vendor_id
            fail_log += "    Reported by OS: '%s'" % output
            n_fail.append(fail_log)
            logging.error(fail_log)

    # Check memory size
    logging.info("Memory size check")
283
    expected_mem = int(params["mem"])
284 285
    actual_mem = vm.get_memory_size()
    if actual_mem != expected_mem:
L
Lucas Meneghel Rodrigues 已提交
286
        fail_log = "Memory size mismatch:\n"
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
        fail_log += "    Assigned to VM: %s\n" % expected_mem
        fail_log += "    Reported by OS: %s\n" % actual_mem
        n_fail.append(fail_log)
        logging.error(fail_log)

    logging.info("Hard drive count check")
    _, f_fail = check_num("images", "block", image_name)
    n_fail.extend(f_fail)

    logging.info("NIC count check")
    _, f_fail = check_num("nics", "network", "model=")
    n_fail.extend(f_fail)

    logging.info("NICs model check")
    f_fail = chk_fmt_model("nics", "nic_model", "network", "model=(.*),")
    n_fail.extend(f_fail)

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
    if qtree is not None:
        logging.info("Images params check")
        logging.debug("Found devices: %s", params.objects('images'))
        qdisks = qemu_qtree.QtreeDisksContainer(qtree.get_nodes())
        _ = sum(qdisks.parse_info_block(
            vm.monitor.info_block()))
        _ += qdisks.generate_params()
        _ += qdisks.check_disk_params(params)
        if _:
            _ = ("Images check failed with %s errors, check the log for "
                 "details" % _)
            logging.error(_)
            n_fail.append(_)
    else:
        logging.info("Images check param skipped (qemu monitor doesn't "
                     "support 'info qtree')")
320 321 322 323

    logging.info("Network card MAC check")
    o = ""
    try:
324
        o = vm.monitor.human_monitor_cmd("info network")
325
    except qemu_monitor.MonitorError, e:
326
        fail_log = str(e) + "\n"
327 328 329 330 331 332 333 334 335
        fail_log += "info/query monitor command failed (network)"
        n_fail.append(fail_log)
        logging.error(fail_log)
    found_mac_addresses = re.findall("macaddr=(\S+)", o)
    logging.debug("Found MAC adresses: %s", found_mac_addresses)

    num_nics = len(params.objects("nics"))
    for nic_index in range(num_nics):
        mac = vm.get_mac_address(nic_index)
336
        if string.lower(mac) not in found_mac_addresses:
L
Lucas Meneghel Rodrigues 已提交
337
            fail_log = "MAC address mismatch:\n"
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
            fail_log += "    Assigned to VM (not found): %s" % mac
            n_fail.append(fail_log)
            logging.error(fail_log)

    logging.info("UUID check")
    if vm.get_uuid():
        f_fail = verify_device(vm.get_uuid(), "UUID",
                               params.get("catch_uuid_cmd"))
        n_fail.extend(f_fail)

    logging.info("Hard Disk serial number check")
    catch_serial_cmd = params.get("catch_serial_cmd")
    f_fail = verify_device(params.get("drive_serial"), "Serial",
                           catch_serial_cmd)
    n_fail.extend(f_fail)

    # only check if the MS Windows VirtIO driver is digital signed.
    chk_cmd = params.get("vio_driver_chk_cmd")
    if chk_cmd:
        logging.info("Virtio Driver Check")
        chk_output = session.cmd_output(chk_cmd, timeout=chk_timeout)
        if "FALSE" in chk_output:
            fail_log = "VirtIO driver is not digitally signed!"
            fail_log += "    VirtIO driver check output: '%s'" % chk_output
            n_fail.append(fail_log)
            logging.error(fail_log)

365 366 367 368
    logging.info("Machine Type Check")
    f_fail = verify_machine_type()
    n_fail.extend(f_fail)

369 370 371 372 373 374 375
    if n_fail:
        session.close()
        raise error.TestFail("Physical resources check test "
                             "reported %s failures:\n%s" %
                             (len(n_fail), "\n".join(n_fail)))

    session.close()