def get_address(self, index=0): """ Return the address of a NIC of the guest, in host space. If port redirection is used, return 'localhost' (the NIC has no IP address of its own). Otherwise return the NIC's IP address. @param index: Index of the NIC whose address is requested. """ nics = kvm_utils.get_sub_dict_names(self.params, "nics") nic_name = nics[index] nic_params = kvm_utils.get_sub_dict(self.params, nic_name) if nic_params.get("nic_mode") == "tap": mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params) if not mac: logging.debug("MAC address unavailable") return None if not ip or nic_params.get("always_use_tcpdump") == "yes": # Get the IP address from the cache ip = self.address_cache.get(mac) if not ip: logging.debug("Could not find IP address for MAC address: " "%s" % mac) return None # Make sure the IP address is assigned to this guest nic_dicts = [kvm_utils.get_sub_dict(self.params, nic) for nic in nics] macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0] for dict in nic_dicts] if not kvm_utils.verify_ip_address_ownership(ip, macs): logging.debug("Could not verify MAC-IP address mapping: " "%s ---> %s" % (mac, ip)) return None return ip else: return "localhost"
def chk_fmt_model(device, fmt_model, info_cmd, str): f_fail = 0 devices = kvm_utils.get_sub_dict_names(params, device) for chk_device in devices: expected = kvm_utils.get_sub_dict(params, chk_device).get(fmt_model) if not expected: expected = "rtl8139" try: o = vm.monitor.info(info_cmd) except kvm_monitor.MonitorError, e: f_fail += 1 logging.error(e) logging.error("info/query monitor command failed (%s)", info_cmd) device_found = re.findall(str, o) logging.debug("Found devices: %s" % device_found) found = False for fm in device_found: if expected in fm: found = True if not found: f_fail += 1 logging.error("%s model mismatch:") logging.error(" Assigned to VM: %s" % expected) logging.error(" Reported by OS: %s" % device_found)
def get_address(self, index=0): """ Return the address of a NIC of the guest, in host space. If port redirection is used, return 'localhost' (the NIC has no IP address of its own). Otherwise return the NIC's IP address. @param index: Index of the NIC whose address is requested. """ nics = kvm_utils.get_sub_dict_names(self.params, "nics") nic_name = nics[index] nic_params = kvm_utils.get_sub_dict(self.params, nic_name) if nic_params.get("nic_mode") == "tap": mac = self.get_mac_address(index) if not mac: logging.debug("MAC address unavailable") return None mac = mac.lower() # Get the IP address from the cache ip = self.address_cache.get(mac) if not ip: logging.debug("Could not find IP address for MAC address: %s" % mac) return None # Make sure the IP address is assigned to this guest macs = [self.get_mac_address(i) for i in range(len(nics))] if not kvm_utils.verify_ip_address_ownership(ip, macs): logging.debug("Could not verify MAC-IP address mapping: " "%s ---> %s" % (mac, ip)) return None return ip else: return "localhost"
def postprocess_on_error(test, params, env): """ Perform postprocessing operations required only if the test failed. @param test: An Autotest test object. @param params: A dict containing all VM and image parameters. @param env: The environment (a dict-like object). """ params.update(kvm_utils.get_sub_dict(params, "on_error"))
def get_ifname(self, nic_index=0): """ Return the ifname of a tap device associated with a NIC. @param nic_index: Index of the NIC """ nics = kvm_utils.get_sub_dict_names(self.params, "nics") nic_name = nics[nic_index] nic_params = kvm_utils.get_sub_dict(self.params, nic_name) if nic_params.get("nic_ifname"): return nic_params.get("nic_ifname") else: return "t%d-%s" % (nic_index, self.instance[-11:])
def process(test, params, env, image_func, vm_func): """ Pre- or post-process VMs and images according to the instructions in params. Call image_func for each image listed in params and vm_func for each VM. @param test: An Autotest test object. @param params: A dict containing all VM and image parameters. @param env: The environment (a dict-like object). @param image_func: A function to call for each image. @param vm_func: A function to call for each VM. """ # Get list of VMs specified for this test vm_names = kvm_utils.get_sub_dict_names(params, "vms") for vm_name in vm_names: vm_params = kvm_utils.get_sub_dict(params, vm_name) # Get list of images specified for this VM image_names = kvm_utils.get_sub_dict_names(vm_params, "images") for image_name in image_names: image_params = kvm_utils.get_sub_dict(vm_params, image_name) # Call image_func for each image image_func(test, image_params) # Call vm_func for each vm vm_func(test, vm_params, env, vm_name)
def get_port(self, port, nic_index=0): """ Return the port in host space corresponding to port in guest space. @param port: Port number in host space. @param nic_index: Index of the NIC. @return: If port redirection is used, return the host port redirected to guest port port. Otherwise return port. """ nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index] nic_params = kvm_utils.get_sub_dict(self.params, nic_name) if nic_params.get("nic_mode") == "tap": return port else: if not self.redirs.has_key(port): logging.warn("Warning: guest port %s requested but not " "redirected" % port) return self.redirs.get(port)
def create(self, name=None, params=None, root_dir=None, for_migration=False, timeout=5.0): """ Start the VM by running a qemu command. All parameters are optional. The following applies to all parameters but for_migration: If a parameter is not supplied, the corresponding value stored in the class attributes is used, and if it is supplied, it is stored for later use. @param name: The name of the object @param params: A dict containing VM params @param root_dir: Base directory for relative filenames @param for_migration: If True, start the VM with the -incoming option """ self.destroy() if name != None: self.name = name if params != None: self.params = params if root_dir != None: self.root_dir = root_dir name = self.name params = self.params root_dir = self.root_dir # Verify the md5sum of the ISO image iso = params.get("cdrom") if iso: iso = kvm_utils.get_path(root_dir, iso) if not os.path.exists(iso): logging.error("ISO file not found: %s" % iso) return False compare = False if params.get("md5sum_1m"): logging.debug("Comparing expected MD5 sum with MD5 sum of " "first MB of ISO file...") actual_md5sum = kvm_utils.md5sum_file(iso, 1048576) expected_md5sum = params.get("md5sum_1m") compare = True elif params.get("md5sum"): logging.debug("Comparing expected MD5 sum with MD5 sum of ISO " "file...") actual_md5sum = kvm_utils.md5sum_file(iso) expected_md5sum = params.get("md5sum") compare = True if compare: if actual_md5sum == expected_md5sum: logging.debug("MD5 sums match") else: logging.error("Actual MD5 sum differs from expected one") return False # Make sure the following code is not executed by more than one thread # at the same time lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") fcntl.lockf(lockfile, fcntl.LOCK_EX) try: # Handle port redirections redir_names = kvm_utils.get_sub_dict_names(params, "redirs") host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) self.redirs = {} for i in range(len(redir_names)): redir_params = kvm_utils.get_sub_dict(params, redir_names[i]) guest_port = int(redir_params.get("guest_port")) self.redirs[guest_port] = host_ports[i] # Find available VNC port, if needed if params.get("display") == "vnc": self.vnc_port = kvm_utils.find_free_port(5900, 6000) # Find random UUID if specified 'uuid = random' in config file if params.get("uuid") == "random": f = open("/proc/sys/kernel/random/uuid") self.uuid = f.read().strip() f.close() # Make qemu command qemu_command = self.make_qemu_command() # Is this VM supposed to accept incoming migrations? if for_migration: # Find available migration port self.migration_port = kvm_utils.find_free_port(5200, 6000) # Add -incoming option to the qemu command qemu_command += " -incoming tcp:0:%d" % self.migration_port logging.debug("Running qemu command:\n%s", qemu_command) self.process = kvm_subprocess.run_bg(qemu_command, None, logging.debug, "(qemu) ") if not self.process.is_alive(): logging.error("VM could not be created; " "qemu command failed:\n%s" % qemu_command) logging.error("Status: %s" % self.process.get_status()) logging.error("Output:" + kvm_utils.format_str_for_message(self.process.get_output())) self.destroy() return False if not kvm_utils.wait_for(self.is_alive, timeout, 0, 1): logging.error("VM is not alive for some reason; " "qemu command:\n%s" % qemu_command) self.destroy() return False # Get the output so far, to see if we have any problems with # hugepage setup. output = self.process.get_output() if "alloc_mem_area" in output: logging.error("Could not allocate hugepage memory; " "qemu command:\n%s" % qemu_command) logging.error("Output:" + kvm_utils.format_str_for_message(self.process.get_output())) return False logging.debug("VM appears to be alive with PID %d", self.process.get_pid()) return True finally: fcntl.lockf(lockfile, fcntl.LOCK_UN) lockfile.close()
def make_qemu_command(self, name=None, params=None, root_dir=None): """ Generate a qemu command line. All parameters are optional. If a parameter is not supplied, the corresponding value stored in the class attributes is used. @param name: The name of the object @param params: A dict containing VM params @param root_dir: Base directory for relative filenames @note: The params dict should contain: mem -- memory size in MBs cdrom -- ISO filename to use with the qemu -cdrom parameter extra_params -- a string to append to the qemu command shell_port -- port of the remote shell daemon on the guest (SSH, Telnet or the home-made Remote Shell Server) shell_client -- client program to use for connecting to the remote shell daemon on the guest (ssh, telnet or nc) x11_display -- if specified, the DISPLAY environment variable will be be set to this value for the qemu process (useful for SDL rendering) images -- a list of image object names, separated by spaces nics -- a list of NIC object names, separated by spaces For each image in images: drive_format -- string to pass as 'if' parameter for this image (e.g. ide, scsi) image_snapshot -- if yes, pass 'snapshot=on' to qemu for this image image_boot -- if yes, pass 'boot=on' to qemu for this image In addition, all parameters required by get_image_filename. For each NIC in nics: nic_model -- string to pass as 'model' parameter for this NIC (e.g. e1000) """ if name == None: name = self.name if params == None: params = self.params if root_dir == None: root_dir = self.root_dir # Start constructing the qemu command qemu_cmd = "" # Set the X11 display parameter if requested if params.get("x11_display"): qemu_cmd += "DISPLAY=%s " % params.get("x11_display") # Add the qemu binary qemu_cmd += kvm_utils.get_path(root_dir, params.get("qemu_binary", "qemu")) # Add the VM's name qemu_cmd += " -name '%s'" % name # Add the monitor socket parameter qemu_cmd += " -monitor unix:%s,server,nowait" % self.monitor_file_name for image_name in kvm_utils.get_sub_dict_names(params, "images"): image_params = kvm_utils.get_sub_dict(params, image_name) if image_params.get("boot_drive") == "no": continue qemu_cmd += " -drive file=%s" % get_image_filename(image_params, root_dir) if image_params.get("drive_format"): qemu_cmd += ",if=%s" % image_params.get("drive_format") if image_params.get("drive_cache"): qemu_cmd += ",cache=%s" % image_params.get("drive_cache") if image_params.get("drive_serial"): qemu_cmd += ",serial=%s" % image_params.get("drive_serial") if image_params.get("image_snapshot") == "yes": qemu_cmd += ",snapshot=on" if image_params.get("image_boot") == "yes": qemu_cmd += ",boot=on" vlan = 0 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"): nic_params = kvm_utils.get_sub_dict(params, nic_name) # Handle the '-net nic' part qemu_cmd += " -net nic,vlan=%d" % vlan if nic_params.get("nic_model"): qemu_cmd += ",model=%s" % nic_params.get("nic_model") if nic_params.has_key("address_index"): mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params) if mac: qemu_cmd += ",macaddr=%s" % mac # Handle the '-net tap' or '-net user' part mode = nic_params.get("nic_mode", "user") qemu_cmd += " -net %s,vlan=%d" % (mode, vlan) if mode == "tap": if nic_params.get("nic_ifname"): qemu_cmd += ",ifname=%s" % nic_params.get("nic_ifname") script_path = nic_params.get("nic_script") if script_path: script_path = kvm_utils.get_path(root_dir, script_path) qemu_cmd += ",script=%s" % script_path script_path = nic_params.get("nic_downscript") if script_path: script_path = kvm_utils.get_path(root_dir, script_path) qemu_cmd += ",downscript=%s" % script_path # Proceed to next NIC vlan += 1 mem = params.get("mem") if mem: qemu_cmd += " -m %s" % mem iso = params.get("cdrom") if iso: iso = kvm_utils.get_path(root_dir, iso) qemu_cmd += " -cdrom %s" % iso extra_params = params.get("extra_params") if extra_params: qemu_cmd += " %s" % extra_params for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"): redir_params = kvm_utils.get_sub_dict(params, redir_name) guest_port = int(redir_params.get("guest_port")) host_port = self.redirs.get(guest_port) qemu_cmd += " -redir tcp:%s::%s" % (host_port, guest_port) if params.get("display") == "vnc": qemu_cmd += " -vnc :%d" % (self.vnc_port - 5900) elif params.get("display") == "sdl": qemu_cmd += " -sdl" elif params.get("display") == "nographic": qemu_cmd += " -nographic" if params.get("uuid") == "random": qemu_cmd += " -uuid %s" % self.uuid elif params.get("uuid"): qemu_cmd += " -uuid %s" % params.get("uuid") return qemu_cmd
def run_pci_hotplug(test, params, env): """ Test hotplug of PCI devices. (Elements between [] are configurable test parameters) 1) PCI add a deivce (NIC / block) 2) Compare output of monitor command 'info pci'. 3) Compare output of guest command [reference_cmd]. 4) Verify whether pci_model is shown in [pci_find_cmd]. 5) Check whether the newly added PCI device works fine. 6) PCI delete the device, verify whether could remove the PCI device. @param test: KVM test object. @param params: Dictionary with the test parameters. @param env: Dictionary with test environment. """ vm = kvm_test_utils.get_living_vm(env, params.get("main_vm")) timeout = int(params.get("login_timeout", 360)) session = kvm_test_utils.wait_for_login(vm, timeout=timeout) # Modprobe the module if specified in config file module = params.get("modprobe_module") if module: session.cmd("modprobe %s" % module) # Get output of command 'info pci' as reference info_pci_ref = vm.monitor.info("pci") # Get output of command as reference reference = session.cmd_output(params.get("reference_cmd")) tested_model = params.get("pci_model") test_type = params.get("pci_type") image_format = params.get("image_format_stg") # Probe qemu to verify what is the supported syntax for PCI hotplug cmd_output = vm.monitor.cmd("?") if len(re.findall("\ndevice_add", cmd_output)) > 0: cmd_type = "device_add" elif len(re.findall("\npci_add", cmd_output)) > 0: cmd_type = "pci_add" else: raise error.TestError("Unknow version of qemu") if cmd_type == "pci_add": if test_type == "nic": pci_add_cmd = "pci_add pci_addr=auto nic model=%s" % tested_model elif test_type == "block": image_params = kvm_utils.get_sub_dict(params, "stg") image_filename = kvm_vm.get_image_filename(image_params, test.bindir) pci_add_cmd = ("pci_add pci_addr=auto storage file=%s,if=%s" % (image_filename, tested_model)) # Execute pci_add (should be replaced by a proper monitor method call) add_output = vm.monitor.cmd(pci_add_cmd) if not "OK domain" in add_output: raise error.TestFail("Add PCI device failed. " "Monitor command is: %s, Output: %r" % (pci_add_cmd, add_output)) after_add = vm.monitor.info("pci") elif cmd_type == "device_add": driver_id = test_type + "-" + kvm_utils.generate_random_id() id = test_type + "-" + kvm_utils.generate_random_id() if test_type == "nic": if tested_model == "virtio": tested_model = "virtio-net-pci" pci_add_cmd = "device_add id=%s,driver=%s" % (id, tested_model) elif test_type == "block": image_params = kvm_utils.get_sub_dict(params, "stg") image_filename = kvm_vm.get_image_filename(image_params, test.bindir) if tested_model == "virtio": tested_model = "virtio-blk-pci" if tested_model == "scsi": tested_model = "scsi-disk" driver_add_cmd = (" __com.redhat_drive_add " "file=%s,format=%s,id=%s" % (image_filename, image_format, driver_id)) pci_add_cmd = ("device_add id=%s,driver=%s,drive=%s" % (id, tested_model, driver_id)) driver_output = vm.monitor.cmd(driver_add_cmd) # Check if the device is support in qemu devices_support = vm.monitor.cmd("%s ?" % cmd_type) if len(re.findall(tested_model, devices_support)) > 0: add_output = vm.monitor.cmd(pci_add_cmd) else: raise error.TestError("%s doesn't support device: %s" % (cmd_type, tested_model)) after_add = vm.monitor.info("pci") if not id in after_add: raise error.TestFail("Add device failed. Monitor command is: %s" ". Output: %r" % (pci_add_cmd, add_output)) # Define a helper function to delete the device def pci_del(ignore_failure=False): if cmd_type == "pci_add": result_domain, bus, slot, function = add_output.split(',') domain = int(result_domain.split()[2]) bus = int(bus.split()[1]) slot = int(slot.split()[1]) pci_addr = "%x:%x:%x" % (domain, bus, slot) cmd = "pci_del pci_addr=%s" % pci_addr elif cmd_type == "device_add": cmd = "device_del %s" % id # This should be replaced by a proper monitor method call vm.monitor.cmd(cmd) def device_removed(): after_del = vm.monitor.info("pci") return after_del != after_add if (not kvm_utils.wait_for(device_removed, 10, 0, 1) and not ignore_failure): raise error.TestFail("Failed to hot remove PCI device: %s. " "Monitor command: %s" % (tested_model, cmd)) try: # Compare the output of 'info pci' if after_add == info_pci_ref: raise error.TestFail("No new PCI device shown after executing " "monitor command: 'info pci'") # Define a helper function to compare the output def new_shown(): o = session.cmd_output(params.get("reference_cmd")) return o != reference secs = int(params.get("wait_secs_for_hook_up")) if not kvm_utils.wait_for(new_shown, 30, secs, 3): raise error.TestFail("No new device shown in output of command " "executed inside the guest: %s" % params.get("reference_cmd")) # Define a helper function to catch PCI device string def find_pci(): o = session.cmd_output(params.get("find_pci_cmd")) return params.get("match_string") in o if not kvm_utils.wait_for(find_pci, 30, 3, 3): raise error.TestFail("PCI %s %s device not found in guest. " "Command was: %s" % (tested_model, test_type, params.get("find_pci_cmd"))) # Test the newly added device try: session.cmd(params.get("pci_test_cmd")) except kvm_subprocess.ShellError, e: raise error.TestFail("Check for %s device failed after PCI " "hotplug. Output: %r" % (test_type, e.output)) session.close()
def run_whql_submission(test, params, env): """ WHQL submission test: 1) Log into the guest (the client machine) and into a DTM server machine 2) Copy the automation program binary (dsso_test_binary) to the server machine 3) Run the automation program 4) Pass the program all relevant parameters (e.g. device_data) 5) Wait for the program to terminate 6) Parse and report job results (logs and HTML reports are placed in test.bindir) @param test: kvm test object @param params: Dictionary with the test parameters @param env: Dictionary with test environment. """ vm = kvm_test_utils.get_living_vm(env, params.get("main_vm")) session = kvm_test_utils.wait_for_login(vm, 0, 240) # Collect parameters server_address = params.get("server_address") server_shell_port = int(params.get("server_shell_port")) server_file_transfer_port = int(params.get("server_file_transfer_port")) server_studio_path = params.get("server_studio_path", "%programfiles%\\ " "Microsoft Driver Test Manager\\Studio") dsso_test_binary = params.get("dsso_test_binary", "deps/whql_submission_15.exe") dsso_test_binary = kvm_utils.get_path(test.bindir, dsso_test_binary) test_device = params.get("test_device") job_filter = params.get("job_filter", ".*") test_timeout = float(params.get("test_timeout", 600)) wtt_services = params.get("wtt_services") # Restart WTT service(s) on the client logging.info("Restarting WTT services on client") for svc in wtt_services.split(): kvm_test_utils.stop_windows_service(session, svc) for svc in wtt_services.split(): kvm_test_utils.start_windows_service(session, svc) # Copy dsso_test_binary to the server rss_file_transfer.upload( server_address, server_file_transfer_port, dsso_test_binary, server_studio_path, timeout=60 ) # Open a shell session with the server server_session = kvm_utils.remote_login( "nc", server_address, server_shell_port, "", "", session.prompt, session.linesep ) server_session.set_status_test_command(session.status_test_command) # Get the computer names of the server and client cmd = "echo %computername%" server_name = server_session.cmd_output(cmd).strip() client_name = session.cmd_output(cmd).strip() session.close() # Run the automation program on the server server_session.cmd("cd %s" % server_studio_path) cmd = "%s %s %s %s %s %s" % ( os.path.basename(dsso_test_binary), server_name, client_name, "%s_pool" % client_name, "%s_submission" % client_name, test_timeout, ) server_session.sendline(cmd) # Helper function: wait for a given prompt and raise an exception if an # error occurs def find_prompt(prompt): m, o = server_session.read_until_last_line_matches( [prompt, server_session.prompt], print_func=logging.info, timeout=600 ) if m != 0: errors = re.findall("^Error:.*$", o, re.I | re.M) if errors: raise error.TestError(errors[0]) else: raise error.TestError("Error running automation program: " "could not find '%s' prompt" % prompt) # Tell the automation program which device to test find_prompt("Device to test:") server_session.sendline(test_device) # Tell the automation program which jobs to run find_prompt("Jobs to run:") server_session.sendline(job_filter) # Give the automation program all the device data supplied by the user find_prompt("DeviceData name:") for dd in kvm_utils.get_sub_dict_names(params, "device_data"): dd_params = kvm_utils.get_sub_dict(params, dd) if dd_params.get("dd_name") and dd_params.get("dd_data"): server_session.sendline(dd_params.get("dd_name")) server_session.sendline(dd_params.get("dd_data")) server_session.sendline() # Give the automation program all the descriptor information supplied by # the user find_prompt("Descriptor path:") for desc in kvm_utils.get_sub_dict_names(params, "descriptors"): desc_params = kvm_utils.get_sub_dict(params, desc) if desc_params.get("desc_path"): server_session.sendline(desc_params.get("desc_path")) server_session.sendline() # Wait for the automation program to terminate try: o = server_session.read_up_to_prompt(print_func=logging.info, timeout=test_timeout + 300) # (test_timeout + 300 is used here because the automation program is # supposed to terminate cleanly on its own when test_timeout expires) done = True except kvm_subprocess.ExpectError, e: o = e.output done = False
def create(self, name=None, params=None, root_dir=None, timeout=5.0, migration_mode=None, mac_source=None): """ Start the VM by running a qemu command. All parameters are optional. If name, params or root_dir are not supplied, the respective values stored as class attributes are used. @param name: The name of the object @param params: A dict containing VM params @param root_dir: Base directory for relative filenames @param migration_mode: If supplied, start VM for incoming migration using this protocol (either 'tcp', 'unix' or 'exec') @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."' (e.g. 'gzip -c -d filename') if migration_mode is 'exec' @param mac_source: A VM object from which to copy MAC addresses. If not specified, new addresses will be generated. """ self.destroy() if name is not None: self.name = name if params is not None: self.params = params if root_dir is not None: self.root_dir = root_dir name = self.name params = self.params root_dir = self.root_dir # Verify the md5sum of the ISO image iso = params.get("cdrom") if iso: iso = kvm_utils.get_path(root_dir, iso) if not os.path.exists(iso): logging.error("ISO file not found: %s" % iso) return False compare = False if params.get("md5sum_1m"): logging.debug("Comparing expected MD5 sum with MD5 sum of " "first MB of ISO file...") actual_hash = utils.hash_file(iso, 1048576, method="md5") expected_hash = params.get("md5sum_1m") compare = True elif params.get("md5sum"): logging.debug("Comparing expected MD5 sum with MD5 sum of ISO " "file...") actual_hash = utils.hash_file(iso, method="md5") expected_hash = params.get("md5sum") compare = True elif params.get("sha1sum"): logging.debug("Comparing expected SHA1 sum with SHA1 sum of " "ISO file...") actual_hash = utils.hash_file(iso, method="sha1") expected_hash = params.get("sha1sum") compare = True if compare: if actual_hash == expected_hash: logging.debug("Hashes match") else: logging.error("Actual hash differs from expected one") return False # Make sure the following code is not executed by more than one thread # at the same time lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") fcntl.lockf(lockfile, fcntl.LOCK_EX) try: # Handle port redirections redir_names = kvm_utils.get_sub_dict_names(params, "redirs") host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) self.redirs = {} for i in range(len(redir_names)): redir_params = kvm_utils.get_sub_dict(params, redir_names[i]) guest_port = int(redir_params.get("guest_port")) self.redirs[guest_port] = host_ports[i] # Generate netdev IDs for all NICs self.netdev_id = [] for nic in kvm_utils.get_sub_dict_names(params, "nics"): self.netdev_id.append(kvm_utils.generate_random_id()) # Find available VNC port, if needed if params.get("display") == "vnc": self.vnc_port = kvm_utils.find_free_port(5900, 6100) # Find random UUID if specified 'uuid = random' in config file if params.get("uuid") == "random": f = open("/proc/sys/kernel/random/uuid") self.uuid = f.read().strip() f.close() # Generate or copy MAC addresses for all NICs num_nics = len(kvm_utils.get_sub_dict_names(params, "nics")) for vlan in range(num_nics): nic_name = kvm_utils.get_sub_dict_names(params, "nics")[vlan] nic_params = kvm_utils.get_sub_dict(params, nic_name) if nic_params.get("nic_mac", None): mac = nic_params.get("nic_mac") kvm_utils.set_mac_address(self.instance, vlan, mac) else: mac = mac_source and mac_source.get_mac_address(vlan) if mac: kvm_utils.set_mac_address(self.instance, vlan, mac) else: kvm_utils.generate_mac_address(self.instance, vlan) # Assign a PCI assignable device self.pci_assignable = None pa_type = params.get("pci_assignable") if pa_type in ["vf", "pf", "mixed"]: pa_devices_requested = params.get("devices_requested") # Virtual Functions (VF) assignable devices if pa_type == "vf": self.pci_assignable = kvm_utils.PciAssignable( type=pa_type, driver=params.get("driver"), driver_option=params.get("driver_option"), devices_requested=pa_devices_requested) # Physical NIC (PF) assignable devices elif pa_type == "pf": self.pci_assignable = kvm_utils.PciAssignable( type=pa_type, names=params.get("device_names"), devices_requested=pa_devices_requested) # Working with both VF and PF elif pa_type == "mixed": self.pci_assignable = kvm_utils.PciAssignable( type=pa_type, driver=params.get("driver"), driver_option=params.get("driver_option"), names=params.get("device_names"), devices_requested=pa_devices_requested) self.pa_pci_ids = self.pci_assignable.request_devs() if self.pa_pci_ids: logging.debug("Successfuly assigned devices: %s", self.pa_pci_ids) else: logging.error("No PCI assignable devices were assigned " "and 'pci_assignable' is defined to %s " "on your config file. Aborting VM creation.", pa_type) return False elif pa_type and pa_type != "no": logging.warn("Unsupported pci_assignable type: %s", pa_type) # Make qemu command qemu_command = self.make_qemu_command() # Add migration parameters if required if migration_mode == "tcp": self.migration_port = kvm_utils.find_free_port(5200, 6000) qemu_command += " -incoming tcp:0:%d" % self.migration_port elif migration_mode == "unix": self.migration_file = "/tmp/migration-unix-%s" % self.instance qemu_command += " -incoming unix:%s" % self.migration_file elif migration_mode == "exec": self.migration_port = kvm_utils.find_free_port(5200, 6000) qemu_command += (' -incoming "exec:nc -l %s"' % self.migration_port) logging.debug("Running qemu command:\n%s", qemu_command) self.process = kvm_subprocess.run_bg(qemu_command, None, logging.debug, "(qemu) ") # Make sure the process was started successfully if not self.process.is_alive(): logging.error("VM could not be created; " "qemu command failed:\n%s" % qemu_command) logging.error("Status: %s" % self.process.get_status()) logging.error("Output:" + kvm_utils.format_str_for_message( self.process.get_output())) self.destroy() return False # Establish monitor connections self.monitors = [] for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): monitor_params = kvm_utils.get_sub_dict(params, monitor_name) # Wait for monitor connection to succeed end_time = time.time() + timeout while time.time() < end_time: try: if monitor_params.get("monitor_type") == "qmp": # Add a QMP monitor monitor = kvm_monitor.QMPMonitor( monitor_name, self.get_monitor_filename(monitor_name)) else: # Add a "human" monitor monitor = kvm_monitor.HumanMonitor( monitor_name, self.get_monitor_filename(monitor_name)) except kvm_monitor.MonitorError, e: logging.warn(e) else: if monitor.is_responsive(): break time.sleep(1) else: logging.error("Could not connect to monitor '%s'" % monitor_name) self.destroy() return False # Add this monitor to the list self.monitors += [monitor] # Get the output so far, to see if we have any problems with # KVM modules or with hugepage setup. output = self.process.get_output() if re.search("Could not initialize KVM", output, re.IGNORECASE): logging.error("Could not initialize KVM; " "qemu command:\n%s" % qemu_command) logging.error("Output:" + kvm_utils.format_str_for_message( self.process.get_output())) self.destroy() return False if "alloc_mem_area" in output: logging.error("Could not allocate hugepage memory; " "qemu command:\n%s" % qemu_command) logging.error("Output:" + kvm_utils.format_str_for_message( self.process.get_output())) self.destroy() return False logging.debug("VM appears to be alive with PID %s", self.get_pid()) # Establish a session with the serial console -- requires a version # of netcat that supports -U self.serial_console = kvm_subprocess.ShellSession( "nc -U %s" % self.get_serial_console_filename(), auto_close=False, output_func=kvm_utils.log_line, output_params=("serial-%s.log" % name,)) return True
def make_qemu_command(self, name=None, params=None, root_dir=None): """ Generate a qemu command line. All parameters are optional. If a parameter is not supplied, the corresponding value stored in the class attributes is used. @param name: The name of the object @param params: A dict containing VM params @param root_dir: Base directory for relative filenames @note: The params dict should contain: mem -- memory size in MBs cdrom -- ISO filename to use with the qemu -cdrom parameter extra_params -- a string to append to the qemu command shell_port -- port of the remote shell daemon on the guest (SSH, Telnet or the home-made Remote Shell Server) shell_client -- client program to use for connecting to the remote shell daemon on the guest (ssh, telnet or nc) x11_display -- if specified, the DISPLAY environment variable will be be set to this value for the qemu process (useful for SDL rendering) images -- a list of image object names, separated by spaces nics -- a list of NIC object names, separated by spaces For each image in images: drive_format -- string to pass as 'if' parameter for this image (e.g. ide, scsi) image_snapshot -- if yes, pass 'snapshot=on' to qemu for this image image_boot -- if yes, pass 'boot=on' to qemu for this image In addition, all parameters required by get_image_filename. For each NIC in nics: nic_model -- string to pass as 'model' parameter for this NIC (e.g. e1000) """ # Helper function for command line option wrappers def has_option(help, option): return bool(re.search(r"^-%s(\s|$)" % option, help, re.MULTILINE)) # Wrappers for all supported qemu command line parameters. # This is meant to allow support for multiple qemu versions. # Each of these functions receives the output of 'qemu -help' as a # parameter, and should add the requested command line option # accordingly. def add_name(help, name): return " -name '%s'" % name def add_human_monitor(help, filename): return " -monitor unix:'%s',server,nowait" % filename def add_qmp_monitor(help, filename): return " -qmp unix:'%s',server,nowait" % filename def add_serial(help, filename): return " -serial unix:'%s',server,nowait" % filename def add_mem(help, mem): return " -m %s" % mem def add_smp(help, smp): return " -smp %s" % smp def add_cdrom(help, filename, index=None): if has_option(help, "drive"): cmd = " -drive file='%s',media=cdrom" % filename if index is not None: cmd += ",index=%s" % index return cmd else: return " -cdrom '%s'" % filename def add_drive(help, filename, index=None, format=None, cache=None, werror=None, serial=None, snapshot=False, boot=False): cmd = " -drive file='%s'" % filename if index is not None: cmd += ",index=%s" % index if format: cmd += ",if=%s" % format if cache: cmd += ",cache=%s" % cache if werror: cmd += ",werror=%s" % werror if serial: cmd += ",serial='%s'" % serial if snapshot: cmd += ",snapshot=on" if boot: cmd += ",boot=on" return cmd def add_nic(help, vlan, model=None, mac=None, netdev_id=None, nic_extra_params=None): if has_option(help, "netdev"): netdev_vlan_str = ",netdev=%s" % netdev_id else: netdev_vlan_str = ",vlan=%d" % vlan if has_option(help, "device"): if not model: model = "rtl8139" elif model == "virtio": model = "virtio-net-pci" cmd = " -device %s" % model + netdev_vlan_str if mac: cmd += ",mac='%s'" % mac if nic_extra_params: cmd += ",%s" % nic_extra_params else: cmd = " -net nic" + netdev_vlan_str if model: cmd += ",model=%s" % model if mac: cmd += ",macaddr='%s'" % mac return cmd def add_net(help, vlan, mode, ifname=None, script=None, downscript=None, tftp=None, bootfile=None, hostfwd=[], netdev_id=None, netdev_extra_params=None): if has_option(help, "netdev"): cmd = " -netdev %s,id=%s" % (mode, netdev_id) if netdev_extra_params: cmd += ",%s" % netdev_extra_params else: cmd = " -net %s,vlan=%d" % (mode, vlan) if mode == "tap": if ifname: cmd += ",ifname='%s'" % ifname if script: cmd += ",script='%s'" % script cmd += ",downscript='%s'" % (downscript or "no") elif mode == "user": if tftp and "[,tftp=" in help: cmd += ",tftp='%s'" % tftp if bootfile and "[,bootfile=" in help: cmd += ",bootfile='%s'" % bootfile if "[,hostfwd=" in help: for host_port, guest_port in hostfwd: cmd += ",hostfwd=tcp::%s-:%s" % (host_port, guest_port) return cmd def add_floppy(help, filename): return " -fda '%s'" % filename def add_tftp(help, filename): # If the new syntax is supported, don't add -tftp if "[,tftp=" in help: return "" else: return " -tftp '%s'" % filename def add_bootp(help, filename): # If the new syntax is supported, don't add -bootp if "[,bootfile=" in help: return "" else: return " -bootp '%s'" % filename def add_tcp_redir(help, host_port, guest_port): # If the new syntax is supported, don't add -redir if "[,hostfwd=" in help: return "" else: return " -redir tcp:%s::%s" % (host_port, guest_port) def add_vnc(help, vnc_port): return " -vnc :%d" % (vnc_port - 5900) def add_sdl(help): if has_option(help, "sdl"): return " -sdl" else: return "" def add_nographic(help): return " -nographic" def add_uuid(help, uuid): return " -uuid '%s'" % uuid def add_pcidevice(help, host): return " -pcidevice host='%s'" % host def add_kernel(help, filename): return " -kernel '%s'" % filename def add_initrd(help, filename): return " -initrd '%s'" % filename def add_kernel_cmdline(help, cmdline): return " -append %s" % cmdline def add_testdev(help, filename): return (" -chardev file,id=testlog,path=%s" " -device testdev,chardev=testlog" % filename) def add_no_hpet(help): if has_option(help, "no-hpet"): return " -no-hpet" else: return "" # End of command line option wrappers if name is None: name = self.name if params is None: params = self.params if root_dir is None: root_dir = self.root_dir qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary", "qemu")) # Get the output of 'qemu -help' (log a message in case this call never # returns or causes some other kind of trouble) logging.debug("Getting output of 'qemu -help'") help = commands.getoutput("%s -help" % qemu_binary) # Start constructing the qemu command qemu_cmd = "" # Set the X11 display parameter if requested if params.get("x11_display"): qemu_cmd += "DISPLAY=%s " % params.get("x11_display") # Add the qemu binary qemu_cmd += qemu_binary # Add the VM's name qemu_cmd += add_name(help, name) # Add monitors for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): monitor_params = kvm_utils.get_sub_dict(params, monitor_name) monitor_filename = self.get_monitor_filename(monitor_name) if monitor_params.get("monitor_type") == "qmp": qemu_cmd += add_qmp_monitor(help, monitor_filename) else: qemu_cmd += add_human_monitor(help, monitor_filename) # Add serial console redirection qemu_cmd += add_serial(help, self.get_serial_console_filename()) for image_name in kvm_utils.get_sub_dict_names(params, "images"): image_params = kvm_utils.get_sub_dict(params, image_name) if image_params.get("boot_drive") == "no": continue qemu_cmd += add_drive(help, get_image_filename(image_params, root_dir), image_params.get("drive_index"), image_params.get("drive_format"), image_params.get("drive_cache"), image_params.get("drive_werror"), image_params.get("drive_serial"), image_params.get("image_snapshot") == "yes", image_params.get("image_boot") == "yes") redirs = [] for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"): redir_params = kvm_utils.get_sub_dict(params, redir_name) guest_port = int(redir_params.get("guest_port")) host_port = self.redirs.get(guest_port) redirs += [(host_port, guest_port)] vlan = 0 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"): nic_params = kvm_utils.get_sub_dict(params, nic_name) # Handle the '-net nic' part mac = self.get_mac_address(vlan) qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, self.netdev_id[vlan], nic_params.get("nic_extra_params")) # Handle the '-net tap' or '-net user' part script = nic_params.get("nic_script") downscript = nic_params.get("nic_downscript") tftp = nic_params.get("tftp") if script: script = kvm_utils.get_path(root_dir, script) if downscript: downscript = kvm_utils.get_path(root_dir, downscript) if tftp: tftp = kvm_utils.get_path(root_dir, tftp) qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), self.get_ifname(vlan), script, downscript, tftp, nic_params.get("bootp"), redirs, self.netdev_id[vlan], nic_params.get("netdev_extra_params")) # Proceed to next NIC vlan += 1 mem = params.get("mem") if mem: qemu_cmd += add_mem(help, mem) smp = params.get("smp") if smp: qemu_cmd += add_smp(help, smp) cdroms = kvm_utils.get_sub_dict_names(params, "cdroms") for cdrom in cdroms: cdrom_params = kvm_utils.get_sub_dict(params, cdrom) iso = cdrom_params.get("cdrom") if iso: qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso), cdrom_params.get("drive_index")) # We may want to add {floppy_otps} parameter for -fda # {fat:floppy:}/path/. However vvfat is not usually recommended. floppy = params.get("floppy") if floppy: floppy = kvm_utils.get_path(root_dir, floppy) qemu_cmd += add_floppy(help, floppy) tftp = params.get("tftp") if tftp: tftp = kvm_utils.get_path(root_dir, tftp) qemu_cmd += add_tftp(help, tftp) bootp = params.get("bootp") if bootp: qemu_cmd += add_bootp(help, bootp) kernel = params.get("kernel") if kernel: kernel = kvm_utils.get_path(root_dir, kernel) qemu_cmd += add_kernel(help, kernel) kernel_cmdline = params.get("kernel_cmdline") if kernel_cmdline: qemu_cmd += add_kernel_cmdline(help, kernel_cmdline) initrd = params.get("initrd") if initrd: initrd = kvm_utils.get_path(root_dir, initrd) qemu_cmd += add_initrd(help, initrd) for host_port, guest_port in redirs: qemu_cmd += add_tcp_redir(help, host_port, guest_port) if params.get("display") == "vnc": qemu_cmd += add_vnc(help, self.vnc_port) elif params.get("display") == "sdl": qemu_cmd += add_sdl(help) elif params.get("display") == "nographic": qemu_cmd += add_nographic(help) if params.get("uuid") == "random": qemu_cmd += add_uuid(help, self.uuid) elif params.get("uuid"): qemu_cmd += add_uuid(help, params.get("uuid")) if params.get("testdev") == "yes": qemu_cmd += add_testdev(help, self.get_testlog_filename()) if params.get("disable_hpet") == "yes": qemu_cmd += add_no_hpet(help) # If the PCI assignment step went OK, add each one of the PCI assigned # devices to the qemu command line. if self.pci_assignable: for pci_id in self.pa_pci_ids: qemu_cmd += add_pcidevice(help, pci_id) extra_params = params.get("extra_params") if extra_params: qemu_cmd += " %s" % extra_params return qemu_cmd
def make_qemu_command(self, name=None, params=None, qemu_path=None, image_dir=None, iso_dir=None): """ Generate a qemu command line. All parameters are optional. If a parameter is not supplied, the corresponding value stored in the class attributes is used. @param name: The name of the object @param params: A dict containing VM params @param qemu_path: The path of the qemu binary @param image_dir: The directory where images reside @param iso_dir: The directory where ISOs reside @note: The params dict should contain: mem -- memory size in MBs cdrom -- ISO filename to use with the qemu -cdrom parameter (iso_dir is pre-pended to the ISO filename) extra_params -- a string to append to the qemu command ssh_port -- should be 22 for SSH, 23 for Telnet x11_display -- if specified, the DISPLAY environment variable will be be set to this value for the qemu process (useful for SDL rendering) images -- a list of image object names, separated by spaces nics -- a list of NIC object names, separated by spaces For each image in images: drive_format -- string to pass as 'if' parameter for this image (e.g. ide, scsi) image_snapshot -- if yes, pass 'snapshot=on' to qemu for this image image_boot -- if yes, pass 'boot=on' to qemu for this image In addition, all parameters required by get_image_filename. For each NIC in nics: nic_model -- string to pass as 'model' parameter for this NIC (e.g. e1000) """ if name == None: name = self.name if params == None: params = self.params if qemu_path == None: qemu_path = self.qemu_path if image_dir == None: image_dir = self.image_dir if iso_dir == None: iso_dir = self.iso_dir # Start constructing the qemu command qemu_cmd = "" # Set the X11 display parameter if requested if params.get("x11_display"): qemu_cmd += "DISPLAY=%s " % params.get("x11_display") # Add the qemu binary qemu_cmd += qemu_path # Add the VM's name qemu_cmd += " -name '%s'" % name # Add the monitor socket parameter qemu_cmd += " -monitor unix:%s,server,nowait" % self.monitor_file_name for image_name in kvm_utils.get_sub_dict_names(params, "images"): image_params = kvm_utils.get_sub_dict(params, image_name) qemu_cmd += " -drive file=%s" % get_image_filename(image_params, image_dir) if image_params.get("drive_format"): qemu_cmd += ",if=%s" % image_params.get("drive_format") if image_params.get("drive_cache"): qemu_cmd += ",cache=%s" % image_params.get("drive_cache") if image_params.get("drive_serial"): qemu_cmd += ",serial=%s" % image_params.get("drive_serial") if image_params.get("image_snapshot") == "yes": qemu_cmd += ",snapshot=on" if image_params.get("image_boot") == "yes": qemu_cmd += ",boot=on" vlan = 0 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"): nic_params = kvm_utils.get_sub_dict(params, nic_name) qemu_cmd += " -net nic,vlan=%d" % vlan if nic_params.get("nic_model"): qemu_cmd += ",model=%s" % nic_params.get("nic_model") qemu_cmd += " -net user,vlan=%d" % vlan vlan += 1 mem = params.get("mem") if mem: qemu_cmd += " -m %s" % mem iso = params.get("cdrom") if iso: iso = os.path.join(iso_dir, iso) qemu_cmd += " -cdrom %s" % iso extra_params = params.get("extra_params") if extra_params: qemu_cmd += " %s" % extra_params for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"): redir_params = kvm_utils.get_sub_dict(params, redir_name) guest_port = int(redir_params.get("guest_port")) host_port = self.get_port(guest_port) qemu_cmd += " -redir tcp:%s::%s" % (host_port, guest_port) if params.get("display") == "vnc": qemu_cmd += " -vnc :%d" % (self.vnc_port - 5900) elif params.get("display") == "sdl": qemu_cmd += " -sdl" elif params.get("display") == "nographic": qemu_cmd += " -nographic" if params.get("uuid") == "random": qemu_cmd += " -uuid %s" % self.uuid elif params.get("uuid"): qemu_cmd += " -uuid %s" % params.get("uuid") return qemu_cmd