def run_shutdown(test, params, env): """ KVM shutdown test: 1) Log into a guest 2) Send a shutdown command to the guest 3) Wait until it's down @param test: kvm test object @param params: Dictionary with the test parameters @param env: Dictionary with test environment """ vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: raise error.TestError("VM object not found in environment") if not vm.is_alive(): raise error.TestError("VM seems to be dead; Test requires a living VM") logging.info("Waiting for guest to be up...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest") logging.info("Logged in") # Send the VM's shutdown command session.sendline(vm.get_params().get("cmd_shutdown")) session.close() logging.info("Shutdown command sent; waiting for guest to go down...") if not kvm_utils.wait_for(vm.is_dead, 240, 0, 1): raise error.TestFail("Guest refuses to go down") logging.info("Guest is down")
def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2, serial=None): """ Try logging into a VM repeatedly. Stop on success or when timeout expires. @param vm: VM object. @param nic_index: Index of NIC to access in the VM. @param timeout: Time to wait before giving up. @param serial: Whether to use a serial connection instead of a remote (ssh, rss) one. @return: A shell session object. """ type = 'remote' if serial: type = 'serial' logging.info("Trying to log into guest %s using serial connection," " timeout %ds", vm.name, timeout) session = kvm_utils.wait_for(lambda: vm.serial_login(), timeout, start, step) else: logging.info("Trying to log into guest %s using remote connection," " timeout %ds", vm.name, timeout) session = kvm_utils.wait_for(lambda: vm.remote_login( nic_index=nic_index), timeout, start, step) if not session: raise error.TestFail("Could not log into guest %s using %s connection" % (vm.name, type)) logging.info("Logged into guest %s using %s connection", vm.name, type) return session
def destroy(self, gracefully=True): """ Destroy the VM. If gracefully is True, first attempt to shutdown the VM with a shell command. Then, attempt to destroy the VM via the monitor with a 'quit' command. If that fails, send SIGKILL to the qemu process. @param gracefully: Whether an attempt will be made to end the VM using a shell command before trying to end the qemu process with a 'quit' or a kill signal. """ try: # Is it already dead? if self.is_dead(): logging.debug("VM is already down") return logging.debug("Destroying VM with PID %s...", self.get_pid()) if gracefully and self.params.get("shutdown_command"): # Try to destroy with shell command logging.debug("Trying to shutdown VM with shell command...") session = self.remote_login() if session: try: # Send the shutdown command session.sendline(self.params.get("shutdown_command")) logging.debug("Shutdown command sent; waiting for VM " "to go down...") if kvm_utils.wait_for(self.is_dead, 60, 1, 1): logging.debug("VM is down, freeing mac address.") return finally: session.close() if self.monitor: # Try to destroy with a monitor command logging.debug("Trying to kill VM with monitor command...") try: self.monitor.quit() except kvm_monitor.MonitorError, e: logging.warn(e) else: # Wait for the VM to be really dead if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): logging.debug("VM is down") return # If the VM isn't dead yet... logging.debug("Cannot quit normally; sending a kill to close the " "deal...") kvm_utils.kill_process_tree(self.process.get_pid(), 9) # Wait for the VM to be really dead if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): logging.debug("VM is down") return logging.error("Process %s is a zombie!" % self.process.get_pid())
def destroy(self, gracefully=True): """ Destroy the VM. If gracefully is True, first attempt to kill the VM via SSH/Telnet with a shutdown command. Then, attempt to destroy the VM via the monitor with a 'quit' command. If that fails, send SIGKILL to the qemu process. @param gracefully: Whether an attempt will be made to end the VM using monitor command before trying to kill the qemu process or not. """ # Is it already dead? if self.is_dead(): logging.debug("VM is already down") if self.process: self.process.close() return logging.debug("Destroying VM with PID %d..." % self.process.get_pid()) if gracefully and self.params.get("cmd_shutdown"): # Try to destroy with SSH command logging.debug("Trying to shutdown VM with SSH command...") (status, output) = self.ssh(self.params.get("cmd_shutdown")) # Was the command sent successfully? if status == 0: logging.debug("Shutdown command sent; waiting for VM to go " "down...") if kvm_utils.wait_for(self.is_dead, 60, 1, 1): logging.debug("VM is down") self.process.close() return # Try to destroy with a monitor command logging.debug("Trying to kill VM with monitor command...") (status, output) = self.send_monitor_cmd("quit", block=False) # Was the command sent successfully? if status == 0: # Wait for the VM to be really dead if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): logging.debug("VM is down") self.process.close() return # If the VM isn't dead yet... logging.debug("Cannot quit normally; sending a kill to close the " "deal...") kvm_utils.safe_kill(self.process.get_pid(), 9) # Wait for the VM to be really dead if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): logging.debug("VM is down") self.process.close() return logging.error("Process %s is a zombie!" % self.process.get_pid()) self.process.close()
def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, timeout=240): """ Reboot the VM and wait for it to come back up by trying to log in until timeout expires. @param vm: VM object. @param session: A shell session object. @param method: Reboot method. Can be "shell" (send a shell reboot command) or "system_reset" (send a system_reset monitor command). @param nic_index: Index of NIC to access in the VM, when logging in after rebooting. @param timeout: Time to wait before giving up (after rebooting). @return: A new shell session object. """ if method == "shell": # Send a reboot command to the guest's shell session.sendline(vm.get_params().get("reboot_command")) logging.info("Reboot command sent. Waiting for guest to go down...") elif method == "system_reset": # Sleep for a while before sending the command time.sleep(sleep_before_reset) # Clear the event list of all QMP monitors monitors = [m for m in vm.monitors if m.protocol == "qmp"] for m in monitors: m.clear_events() # Send a system_reset monitor command vm.monitor.cmd("system_reset") logging.info("Monitor command system_reset sent. Waiting for guest to " "go down...") # Look for RESET QMP events time.sleep(1) for m in monitors: if not m.get_event("RESET"): raise error.TestFail("RESET QMP event not received after " "system_reset (monitor '%s')" % m.name) else: logging.info("RESET QMP event received") else: logging.error("Unknown reboot method: %s", method) # Wait for the session to become unresponsive and close it if not kvm_utils.wait_for(lambda: not session.is_responsive(timeout=30), 120, 0, 1): raise error.TestFail("Guest refuses to go down") session.close() # Try logging into the guest until timeout expires logging.info("Guest is down. Waiting for it to go up again, timeout %ds", timeout) session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index), timeout, 0, 2) if not session: raise error.TestFail("Could not log into guest after reboot") logging.info("Guest is up again") return session
def crash_test(vcpu): """ Trigger a crash dump through sysrq-trigger @param vcpu: vcpu which is used to trigger a crash """ session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2) session.cmd_output("rm -rf /var/crash/*") logging.info("Triggering crash on vcpu %d ...", vcpu) crash_cmd = "taskset -c %d echo c > /proc/sysrq-trigger" % vcpu session.sendline(crash_cmd) if not kvm_utils.wait_for(lambda: not session.is_responsive(), 240, 0, 1): raise error.TestFail("Could not trigger crash on vcpu %d" % vcpu) logging.info("Waiting for kernel crash dump to complete") session = kvm_test_utils.wait_for_login(vm, 0, crash_timeout, 0, 2) logging.info("Probing vmcore file...") session.cmd("ls -R /var/crash | grep vmcore") logging.info("Found vmcore.") session.cmd_output("rm -rf /var/crash/*")
def postprocess_vm(test, params, env, name): """ Postprocess a single VM object according to the instructions in params. Kill the VM if requested and get a screendump. @param test: An Autotest test object. @param params: A dict containing VM postprocessing parameters. @param env: The environment (a dict-like object). @param name: The name of the VM object. """ logging.debug("Postprocessing VM '%s'..." % name) vm = kvm_utils.env_get_vm(env, name) if vm: logging.debug("VM object found in environment") else: logging.debug("VM object does not exist in environment") return scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name) vm.send_monitor_cmd("screendump %s" % scrdump_filename) if params.get("kill_vm") == "yes": if not kvm_utils.wait_for(vm.is_dead, float(params.get("kill_vm_timeout", 0)), 0.0, 1.0, "Waiting for VM to kill itself..."): logging.debug("'kill_vm' specified; killing VM...") vm.destroy(gracefully = params.get("kill_vm_gracefully") == "yes")
def run_yum_update(test, params, env): """ Runs yum update and yum update kernel on the remote host (yum enabled hosts only). @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: message = "VM object not found in environment" logging.error(message) raise error.TestError(message) if not vm.is_alive(): message = "VM seems to be dead; Test requires a living VM" logging.error(message) raise error.TestError(message) logging.info("Logging into guest...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: message = "Could not log into guest" logging.error(message) raise error.TestFail(message) logging.info("Logged in") internal_yum_update(session, "yum update", params.get("ssh_prompt"), 600) internal_yum_update(session, "yum update kernel", params.get("ssh_prompt"), 600) session.close()
def crash_test(vcpu): """ Trigger a crash dump through sysrq-trigger @param vcpu: vcpu which is used to trigger a crash """ session = vm.wait_for_login(timeout=timeout) session.cmd_output("rm -rf /var/crash/*") logging.info("Triggering crash on vcpu %d ...", vcpu) crash_cmd = "taskset -c %d echo c > /proc/sysrq-trigger" % vcpu session.sendline(crash_cmd) if not kvm_utils.wait_for(lambda: not session.is_responsive(), 240, 0, 1): raise error.TestFail("Could not trigger crash on vcpu %d" % vcpu) logging.info("Waiting for kernel crash dump to complete") session = vm.wait_for_login(timeout=crash_timeout) logging.info("Probing vmcore file...") session.cmd("ls -R /var/crash | grep vmcore") logging.info("Found vmcore.") session.cmd_output("rm -rf /var/crash/*")
def run_shutdown(test, params, env): """ KVM shutdown test: 1) Log into a guest 2) Send a shutdown command to the guest, or issue a system_powerdown monitor command (depending on the value of shutdown_method) 3) Wait until the guest is down @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) try: if params.get("shutdown_method") == "shell": # Send a shutdown command to the guest's shell session.sendline(vm.get_params().get("shutdown_command")) logging.info("Shutdown command sent; waiting for guest to go " "down...") elif params.get("shutdown_method") == "system_powerdown": # Sleep for a while -- give the guest a chance to finish booting time.sleep(float(params.get("sleep_before_powerdown", 10))) # Send a system_powerdown monitor command vm.monitor.cmd("system_powerdown") logging.info("system_powerdown monitor command sent; waiting for " "guest to go down...") if not kvm_utils.wait_for(vm.is_dead, 240, 0, 1): raise error.TestFail("Guest refuses to go down") logging.info("Guest is down") finally: session.close()
def run_boot(test, params, env): """ KVM reboot test: 1) Log into a guest 2) Send a reboot command or a system_reset monitor command (optional) 3) Wait until the guest is up again 4) Log into the guest to verify it's up again @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) try: if params.get("reboot_method") == "shell": # Send a reboot command to the guest's shell session.sendline(vm.get_params().get("reboot_command")) logging.info("Reboot command sent; waiting for guest to go " "down...") elif params.get("reboot_method") == "system_reset": # Sleep for a while -- give the guest a chance to finish booting time.sleep(float(params.get("sleep_before_reset", 10))) # Send a system_reset monitor command vm.send_monitor_cmd("system_reset") logging.info("system_reset monitor command sent; waiting for " "guest to go down...") else: return # Wait for the session to become unresponsive if not kvm_utils.wait_for(lambda: not session.is_responsive(), 120, 0, 1): raise error.TestFail("Guest refuses to go down") finally: session.close() logging.info("Guest is down; waiting for it to go up again...") session = kvm_utils.wait_for(vm.remote_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest after reboot") session.close() logging.info("Guest is up again")
def run_boot(test, params, env): """ KVM reboot test: 1) Log into a guest 2) Send a reboot command to the guest 3) Wait until it's up. 4) Log into the guest to verify it's up again. @param test: kvm test object @param params: Dictionary with the test parameters @param env: Dictionary with test environment. """ vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: raise error.TestError("VM object not found in environment") if not vm.is_alive(): raise error.TestError("VM seems to be dead; Test requires a living VM") logging.info("Waiting for guest to be up...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest") logging.info("Logged in") if params.get("reboot") == "yes": # Send the VM's reboot command session.sendline(vm.get_params().get("cmd_reboot")) logging.info("Reboot command sent; waiting for guest to go down...") if not kvm_utils.wait_for(lambda: not session.is_responsive(), 120, 0, 1): raise error.TestFail("Guest refuses to go down") session.close() logging.info("Guest is down; waiting for it to go up again...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest after reboot") logging.info("Guest is up again") session.close()
def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, timeout=240): """ Reboot the VM and wait for it to come back up by trying to log in until timeout expires. @param vm: VM object. @param session: A shell session object. @param method: Reboot method. Can be "shell" (send a shell reboot command) or "system_reset" (send a system_reset monitor command). @param nic_index: Index of NIC to access in the VM, when logging in after rebooting. @param timeout: Time to wait before giving up (after rebooting). @return: A new shell session object. """ if method == "shell": # Send a reboot command to the guest's shell session.sendline(vm.get_params().get("reboot_command")) logging.info("Reboot command sent. Waiting for guest to go down...") elif method == "system_reset": # Sleep for a while before sending the command time.sleep(sleep_before_reset) # Clear the event list of all QMP monitors monitors = [m for m in vm.monitors if m.protocol == "qmp"] for m in monitors: m.clear_events() # Send a system_reset monitor command vm.monitor.cmd("system_reset") logging.info("Monitor command system_reset sent. Waiting for guest to " "go down...") # Look for RESET QMP events time.sleep(1) for m in monitors: if not m.get_event("RESET"): raise error.TestFail("RESET QMP event not received after " "system_reset (monitor '%s')" % m.name) else: logging.info("RESET QMP event received") else: logging.error("Unknown reboot method: %s", method) # Wait for the session to become unresponsive and close it if not kvm_utils.wait_for(lambda: not session.is_responsive(timeout=30), 120, 0, 1): raise error.TestFail("Guest refuses to go down") session.close() # Try logging into the guest until timeout expires logging.info("Guest is down. Waiting for it to go up again, timeout %ds", timeout) session = vm.wait_for_login(nic_index, timeout=timeout) logging.info("Guest is up again") return session
def run_mac_change(test, params, env): """ Change MAC address of guest. 1) Get a new mac from pool, and the old mac addr of guest. 2) Set new mac in guest and regain new IP. 3) Re-log into guest with new MAC. @param test: KVM test object. @param params: Dictionary with the test parameters. @param env: Dictionary with test environment. """ timeout = int(params.get("login_timeout", 360)) vm = kvm_test_utils.get_living_vm(env, params.get("main_vm")) session_serial = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2, serial=True) # This session will be used to assess whether the IP change worked session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2) old_mac = vm.get_mac_address(0) while True: vm.free_mac_address(0) new_mac = kvm_utils.generate_mac_address(vm.instance, 0) if old_mac != new_mac: break logging.info("The initial MAC address is %s", old_mac) interface = kvm_test_utils.get_linux_ifname(session_serial, old_mac) # Start change MAC address logging.info("Changing MAC address to %s", new_mac) change_cmd = ("ifconfig %s down && ifconfig %s hw ether %s && " "ifconfig %s up" % (interface, interface, new_mac, interface)) session_serial.cmd(change_cmd) # Verify whether MAC address was changed to the new one logging.info("Verifying the new mac address") session_serial.cmd("ifconfig | grep -i %s" % new_mac) # Restart `dhclient' to regain IP for new mac address logging.info("Restart the network to gain new IP") dhclient_cmd = "dhclient -r && dhclient %s" % interface session_serial.sendline(dhclient_cmd) # Re-log into the guest after changing mac address if kvm_utils.wait_for(session.is_responsive, 120, 20, 3): # Just warning when failed to see the session become dead, # because there is a little chance the ip does not change. logging.warn("The session is still responsive, settings may fail.") session.close() # Re-log into guest and check if session is responsive logging.info("Re-log into the guest") session = kvm_test_utils.wait_for_login(vm, timeout=int(params.get("login_timeout", 360))) if not session.is_responsive(): raise error.TestFail("The new session is not responsive.") session.close()
def read_until_output_matches(self, patterns, filter=lambda x: x, timeout=60, internal_timeout=None, print_func=None): """ Read using read_nonblocking until a match is found using match_patterns, or until timeout expires. Before attempting to search for a match, the data is filtered using the filter function provided. @brief: Read from child using read_nonblocking until a pattern matches. @param patterns: List of strings (regular expression patterns) @param filter: Function to apply to the data read from the child before attempting to match it against the patterns (should take and return a string) @param timeout: The duration (in seconds) to wait until a match is found @param internal_timeout: The timeout to pass to read_nonblocking @param print_func: A function to be used to print the data being read (should take a string parameter) @return: Tuple containing the match index and the data read so far @raise ExpectTimeoutError: Raised if timeout expires @raise ExpectProcessTerminatedError: Raised if the child process terminates while waiting for output @raise ExpectError: Raised if an unknown error occurs """ fd = self._get_fd("expect") o = "" end_time = time.time() + timeout while True: try: r, w, x = select.select([fd], [], [], max(0, end_time - time.time())) except (select.error, TypeError): break if not r: raise ExpectTimeoutError(patterns, o) # Read data from child data = self.read_nonblocking(internal_timeout) if not data: break # Print it if necessary if print_func: for line in data.splitlines(): print_func(line) # Look for patterns o += data match = self.match_patterns(filter(o), patterns) if match is not None: return match, o # Check if the child has terminated if kvm_utils.wait_for(lambda: not self.is_alive(), 5, 0, 0.1): raise ExpectProcessTerminatedError(patterns, self.get_status(), o) else: # This shouldn't happen raise ExpectError(patterns, o)
def read_until_output_matches(self, patterns, filter=lambda x: x, timeout=30.0, internal_timeout=None, print_func=None): """ Read using read_nonblocking until a match is found using match_patterns, or until timeout expires. Before attempting to search for a match, the data is filtered using the filter function provided. @brief: Read from child using read_nonblocking until a pattern matches. @param patterns: List of strings (regular expression patterns) @param filter: Function to apply to the data read from the child before attempting to match it against the patterns (should take and return a string) @param timeout: The duration (in seconds) to wait until a match is found @param internal_timeout: The timeout to pass to read_nonblocking @param print_func: A function to be used to print the data being read (should take a string parameter) @return: Tuple containing the match index and the data read so far @raise ExpectTimeoutError: Raised if timeout expires @raise ExpectProcessTerminatedError: Raised if the child process terminates while waiting for output @raise ExpectError: Raised if an unknown error occurs """ fd = self._get_fd("expect") o = "" end_time = time.time() + timeout while True: try: r, w, x = select.select([fd], [], [], max(0, end_time - time.time())) except (select.error, TypeError): break if not r: raise ExpectTimeoutError(patterns, o) # Read data from child data = self.read_nonblocking(internal_timeout) if not data: break # Print it if necessary if print_func: for line in data.splitlines(): print_func(line) # Look for patterns o += data match = self.match_patterns(filter(o), patterns) if match is not None: return match, o # Check if the child has terminated if kvm_utils.wait_for(lambda: not self.is_alive(), 5, 0, 0.1): raise ExpectProcessTerminatedError(patterns, self.get_status(), o) else: # This shouldn't happen raise ExpectError(patterns, o)
def run_mac_change(test, params, env): """ Change MAC address of guest. 1) Get a new mac from pool, and the old mac addr of guest. 2) Set new mac in guest and regain new IP. 3) Re-log into guest with new MAC. @param test: KVM test object. @param params: Dictionary with the test parameters. @param env: Dictionary with test environment. """ vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session_serial = vm.wait_for_serial_login(timeout=timeout) # This session will be used to assess whether the IP change worked session = vm.wait_for_login(timeout=timeout) old_mac = vm.get_mac_address(0) while True: vm.free_mac_address(0) new_mac = kvm_utils.generate_mac_address(vm.instance, 0) if old_mac != new_mac: break logging.info("The initial MAC address is %s", old_mac) interface = kvm_test_utils.get_linux_ifname(session_serial, old_mac) # Start change MAC address logging.info("Changing MAC address to %s", new_mac) change_cmd = ("ifconfig %s down && ifconfig %s hw ether %s && " "ifconfig %s up" % (interface, interface, new_mac, interface)) session_serial.cmd(change_cmd) # Verify whether MAC address was changed to the new one logging.info("Verifying the new mac address") session_serial.cmd("ifconfig | grep -i %s" % new_mac) # Restart `dhclient' to regain IP for new mac address logging.info("Restart the network to gain new IP") dhclient_cmd = "dhclient -r && dhclient %s" % interface session_serial.sendline(dhclient_cmd) # Re-log into the guest after changing mac address if kvm_utils.wait_for(session.is_responsive, 120, 20, 3): # Just warning when failed to see the session become dead, # because there is a little chance the ip does not change. logging.warn("The session is still responsive, settings may fail.") session.close() # Re-log into guest and check if session is responsive logging.info("Re-log into the guest") session = vm.wait_for_login(timeout=timeout) if not session.is_responsive(): raise error.TestFail("The new session is not responsive.") session.close()
def run_linux_s3(test, params, env): """ Suspend a guest Linux OS to memory. @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: raise error.TestError("VM object not found in environment") if not vm.is_alive(): raise error.TestError("VM seems to be dead; Test requires a living VM") logging.info("Waiting for guest to be up...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest") logging.info("Logged in") logging.info("Checking that VM supports S3") status = session.get_command_status("grep -q mem /sys/power/state") if status == None: logging.error("Failed to check if S3 exists") elif status != 0: raise error.TestFail("Guest does not support S3") logging.info("Waiting for a while for X to start") time.sleep(10) src_tty = session.get_command_output("fgconsole").strip() logging.info("Current virtual terminal is %s" % src_tty) if src_tty not in map(str, range(1, 10)): raise error.TestFail("Got a strange current vt (%s)" % src_tty) dst_tty = "1" if src_tty == "1": dst_tty = "2" logging.info("Putting VM into S3") command = "chvt %s && echo mem > /sys/power/state && chvt %s" % (dst_tty, src_tty) status = session.get_command_status(command, timeout=120) if status != 0: raise error.TestFail("Suspend to mem failed") logging.info("VM resumed after S3") session.close()
def transfer_file(src="guest"): """ Transfer file by scp, use tcpdump to capture packets, then check the return string. @param src: Source host of transfer file @return: Tuple (status, error msg/tcpdump result) """ session2.cmd_output("rm -rf %s" % filename) dd_cmd = ("dd if=/dev/urandom of=%s bs=1M count=%s" % (filename, params.get("filesize"))) logging.info("Creat file in source host, cmd: %s" % dd_cmd) tcpdump_cmd = "tcpdump -lep -s 0 tcp -vv port ssh" if src == "guest": session.cmd_output(dd_cmd, timeout=360) tcpdump_cmd += " and src %s" % guest_ip copy_files_fun = vm.copy_files_from else: s, o = commands.getstatusoutput(dd_cmd) tcpdump_cmd += " and dst %s" % guest_ip copy_files_fun = vm.copy_files_to if s != 0: return (False, "Fail to create file by dd, cmd: %s" % dd_cmd) # only capture the new tcp port after offload setup original_tcp_ports = re.findall("tcp.*:(\d+).*%s" % guest_ip, utils.system_output("/bin/netstat -nap")) for i in original_tcp_ports: tcpdump_cmd += " and not port %s" % i logging.debug("Listen by command: %s" % tcpdump_cmd) session2.sendline(tcpdump_cmd) if not kvm_utils.wait_for( lambda:session.cmd_status("pgrep tcpdump") == 0, 30): return (False, "Tcpdump process wasn't launched") logging.info("Start to transfer file") if not copy_files_fun(filename, filename): return (False, "Child process transfer file failed") logging.info("Transfer file completed") session.cmd("killall tcpdump") try: tcpdump_string = session2.read_up_to_prompt(timeout=60) except kvm_subprocess.ExpectError: return (False, "Fail to read tcpdump's output") if not compare_md5sum(filename): return (False, "Files' md5sum mismatched") return (True, tcpdump_string)
def wait_for_login(vm, nic_index=0, timeout=240): """ Try logging into a VM repeatedly. Stop on success or when timeout expires. @param vm: VM object. @param nic_index: Index of NIC to access in the VM. @param timeout: Time to wait before giving up. @return: A shell session object. """ logging.info("Waiting for guest '%s' to be up..." % vm.name) session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index), timeout, 0, 2) if not session: raise error.TestFail("Could not log into guest '%s'" % vm.name) logging.info("Logged in") return session
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" % device_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))
def nic_del(vm, nic_id, wait=True): """ Remove the nic from pci tree. @vm: VM object @id: the nic id @wait: Whether need to wait for the guest to unplug the device """ nic_del_cmd = "device_del %s" % nic_id vm.monitor.cmd(nic_del_cmd) if wait: logging.info("waiting for the guest to finish the unplug") if not kvm_utils.wait_for( lambda: nic_id not in vm.monitor.info("qtree"), guest_delay, 5, 1): logging.error(vm.monitor.info("qtree")) raise error.TestError("Device is not unplugged by " "guest, please check whether the " "hotplug module was loaded in guest")
def nic_del(vm, nic_id, wait=True): """ Remove the nic from pci tree. @vm: VM object @id: the nic id @wait: Whether need to wait for the guest to unplug the device """ nic_del_cmd = "device_del %s" % nic_id vm.monitor.cmd(nic_del_cmd) if wait: logging.info("waiting for the guest to finish the unplug") if not kvm_utils.wait_for(lambda: nic_id not in vm.monitor.info("qtree"), guest_delay, 5 ,1): logging.error(vm.monitor.info("qtree")) raise error.TestError("Device is not unplugged by " "guest, please check whether the " "hotplug module was loaded in guest")
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" % device_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))
def run_shutdown(test, params, env): """ KVM shutdown test: 1) Log into a guest 2) Send a shutdown command to the guest, or issue a system_powerdown monitor command (depending on the value of shutdown_method) 3) Wait until the guest is down @param test: kvm test object @param params: Dictionary with the test parameters @param env: Dictionary with test environment """ vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=timeout) try: if params.get("shutdown_method") == "shell": # Send a shutdown command to the guest's shell session.sendline(vm.get_params().get("shutdown_command")) logging.info("Shutdown command sent; waiting for guest to go " "down...") elif params.get("shutdown_method") == "system_powerdown": # Sleep for a while -- give the guest a chance to finish booting time.sleep(float(params.get("sleep_before_powerdown", 10))) # Send a system_powerdown monitor command vm.monitor.cmd("system_powerdown") logging.info("system_powerdown monitor command sent; waiting for " "guest to go down...") if not kvm_utils.wait_for(vm.is_dead, 240, 0, 1): raise error.TestFail("Guest refuses to go down") logging.info("Guest is down") finally: session.close()
def run_guest_s4(test, params, env): """ Suspend guest to disk, supports both Linux & Windows OSes. @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the 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) logging.info("Checking whether guest OS supports suspend to disk (S4)...") session.cmd(params.get("check_s4_support_cmd")) logging.info("Waiting until all guest OS services are fully started...") time.sleep(float(params.get("services_up_timeout", 30))) # Start up a program (tcpdump for linux & ping for Windows), as a flag. # If the program died after suspend, then fails this testcase. test_s4_cmd = params.get("test_s4_cmd") session.sendline(test_s4_cmd) time.sleep(5) # Get the second session to start S4 session2 = kvm_test_utils.wait_for_login(vm, timeout=timeout) # Make sure the background program is running as expected check_s4_cmd = params.get("check_s4_cmd") session2.cmd(check_s4_cmd) logging.info("Launched background command in guest: %s" % test_s4_cmd) # Suspend to disk logging.info("Starting suspend to disk now...") session2.sendline(params.get("set_s4_cmd")) # Make sure the VM goes down suspend_timeout = 240 + int(params.get("smp")) * 60 if not kvm_utils.wait_for(vm.is_dead, suspend_timeout, 2, 2): raise error.TestFail("VM refuses to go down. Suspend failed.") logging.info("VM suspended successfully. Sleeping for a while before " "resuming it.") time.sleep(10) # Start vm, and check whether the program is still running logging.info("Resuming suspended VM...") if not vm.create(): raise error.TestError("Failed to start VM after suspend to disk") # Log into the resumed VM relogin_timeout = int(params.get("relogin_timeout", 240)) logging.info("Logging into resumed VM, timeout %s", relogin_timeout) session2 = kvm_utils.wait_for(vm.remote_login, relogin_timeout, 0, 2) if not session2: raise error.TestFail("Could not log into VM after resuming from " "suspend to disk") # Check whether the test command is still alive logging.info("Checking if background command is still alive...") session2.cmd(check_s4_cmd) logging.info("VM resumed successfuly after suspend to disk") session2.cmd_output(params.get("kill_test_s4_cmd")) session.close() session2.close()
def run_guest_s4(test, params, env): """ Suspend guest to disk, supports both Linux & Windows OSes. @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ error.base_context("before S4") vm = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=timeout) error.context("checking whether guest OS supports S4", logging.info) session.cmd(params.get("check_s4_support_cmd")) error.context() logging.info("Waiting until all guest OS services are fully started...") time.sleep(float(params.get("services_up_timeout", 30))) # Start up a program (tcpdump for linux & ping for Windows), as a flag. # If the program died after suspend, then fails this testcase. test_s4_cmd = params.get("test_s4_cmd") session.sendline(test_s4_cmd) time.sleep(5) # Get the second session to start S4 session2 = vm.wait_for_login(timeout=timeout) # Make sure the background program is running as expected error.context("making sure background program is running") check_s4_cmd = params.get("check_s4_cmd") session2.cmd(check_s4_cmd) logging.info("Launched background command in guest: %s", test_s4_cmd) error.context() error.base_context() # Suspend to disk logging.info("Starting suspend to disk now...") session2.sendline(params.get("set_s4_cmd")) # Make sure the VM goes down error.base_context("after S4") suspend_timeout = 240 + int(params.get("smp")) * 60 if not kvm_utils.wait_for(vm.is_dead, suspend_timeout, 2, 2): raise error.TestFail("VM refuses to go down. Suspend failed.") logging.info("VM suspended successfully. Sleeping for a while before " "resuming it.") time.sleep(10) # Start vm, and check whether the program is still running logging.info("Resuming suspended VM...") vm.create() # Log into the resumed VM relogin_timeout = int(params.get("relogin_timeout", 240)) logging.info("Logging into resumed VM, timeout %s", relogin_timeout) session2 = vm.wait_for_login(timeout=relogin_timeout) # Check whether the test command is still alive error.context("making sure background program is still running", logging.info) session2.cmd(check_s4_cmd) error.context() logging.info("VM resumed successfuly after suspend to disk") session2.cmd_output(params.get("kill_test_s4_cmd")) session.close() session2.close()
def run_jumbo(test, params, env): """ Test the RX jumbo frame function of vnics: 1) Boot the VM. 2) Change the MTU of guest nics and host taps depending on the NIC model. 3) Add the static ARP entry for guest NIC. 4) Wait for the MTU ok. 5) Verify the path MTU using ping. 6) Ping the guest with large frames. 7) Increment size ping. 8) Flood ping the guest with large frames. 9) Verify the path MTU. 10) Recover the MTU. @param test: KVM test object. @param params: Dictionary with the test parameters. @param env: Dictionary with test environment. """ vm = env.get_vm(params["main_vm"]) vm.verify_alive() session = vm.wait_for_login(timeout=int(params.get("login_timeout", 360))) mtu = params.get("mtu", "1500") flood_time = params.get("flood_time", "300") max_icmp_pkt_size = int(mtu) - 28 ifname = vm.get_ifname(0) ip = vm.get_address(0) if ip is None: raise error.TestError("Could not get the IP address") try: # Environment preparation ethname = kvm_test_utils.get_linux_ifname(session, vm.get_mac_address(0)) logging.info("Changing the MTU of guest ...") guest_mtu_cmd = "ifconfig %s mtu %s" % (ethname , mtu) session.cmd(guest_mtu_cmd) logging.info("Chaning the MTU of host tap ...") host_mtu_cmd = "ifconfig %s mtu %s" % (ifname, mtu) utils.run(host_mtu_cmd) logging.info("Add a temporary static ARP entry ...") arp_add_cmd = "arp -s %s %s -i %s" % (ip, vm.get_mac_address(0), ifname) utils.run(arp_add_cmd) def is_mtu_ok(): s, o = kvm_test_utils.ping(ip, 1, interface=ifname, packetsize=max_icmp_pkt_size, hint="do", timeout=2) return s == 0 def verify_mtu(): logging.info("Verify the path MTU") s, o = kvm_test_utils.ping(ip, 10, interface=ifname, packetsize=max_icmp_pkt_size, hint="do", timeout=15) if s != 0 : logging.error(o) raise error.TestFail("Path MTU is not as expected") if kvm_test_utils.get_loss_ratio(o) != 0: logging.error(o) raise error.TestFail("Packet loss ratio during MTU " "verification is not zero") def flood_ping(): logging.info("Flood with large frames") kvm_test_utils.ping(ip, interface=ifname, packetsize=max_icmp_pkt_size, flood=True, timeout=float(flood_time)) def large_frame_ping(count=100): logging.info("Large frame ping") s, o = kvm_test_utils.ping(ip, count, interface=ifname, packetsize=max_icmp_pkt_size, timeout=float(count) * 2) ratio = kvm_test_utils.get_loss_ratio(o) if ratio != 0: raise error.TestFail("Loss ratio of large frame ping is %s" % ratio) def size_increase_ping(step=random.randrange(90, 110)): logging.info("Size increase ping") for size in range(0, max_icmp_pkt_size + 1, step): logging.info("Ping %s with size %s", ip, size) s, o = kvm_test_utils.ping(ip, 1, interface=ifname, packetsize=size, hint="do", timeout=1) if s != 0: s, o = kvm_test_utils.ping(ip, 10, interface=ifname, packetsize=size, adaptive=True, hint="do", timeout=20) if kvm_test_utils.get_loss_ratio(o) > int(params.get( "fail_ratio", 50)): raise error.TestFail("Ping loss ratio is greater " "than 50% for size %s" % size) logging.info("Waiting for the MTU to be OK") wait_mtu_ok = 10 if not kvm_utils.wait_for(is_mtu_ok, wait_mtu_ok, 0, 1): logging.debug(commands.getoutput("ifconfig -a")) raise error.TestError("MTU is not as expected even after %s " "seconds" % wait_mtu_ok) # Functional Test verify_mtu() large_frame_ping() size_increase_ping() # Stress test flood_ping() verify_mtu() finally: # Environment clean session.close() logging.info("Removing the temporary ARP entry") utils.run("arp -d %s -i %s" % (ip, ifname))
def wait_for_migration(): if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, "Waiting for migration to finish..."): raise error.TestFail("Timeout expired while waiting for migration " "to finish")
def run_nic_hotplug(test, params, env): """ Test hotplug of NIC devices 1) Boot up guest with one nic 2) Add a host network device through monitor cmd and check if it's added 3) Add nic device through monitor cmd and check if it's added 4) Check if new interface gets ip address 5) Disable primary link of guest 6) Ping guest new ip from host 7) Delete nic device and netdev 8) Re-enable primary link of guest @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)) guest_delay = int(params.get("guest_delay", 20)) session = kvm_test_utils.wait_for_login(vm, timeout=timeout) romfile = params.get("romfile") # Modprobe the module if specified in config file module = params.get("modprobe_module") if module: session.get_command_output("modprobe %s" % module) def netdev_add(vm): netdev_id = kvm_utils.generate_random_id() attach_cmd = ("netdev_add tap,id=%s" % netdev_id) nic_script = params.get("nic_script") if nic_script: attach_cmd += ",script=%s" % kvm_utils.get_path( vm.root_dir, nic_script) netdev_extra_params = params.get("netdev_extra_params") if netdev_extra_params: attach_cmd += ",%s" % netdev_extra_params logging.info("Adding netdev through %s", attach_cmd) vm.monitor.cmd(attach_cmd) network = vm.monitor.info("network") if netdev_id not in network: logging.error(network) raise error.TestError("Fail to add netdev: %s" % netdev_id) else: return netdev_id def netdev_del(vm, n_id): vm.monitor.cmd("netdev_del %s" % n_id) network = vm.monitor.info("network") if n_id in network: logging.error(network) raise error.TestError("Fail to remove netdev %s" % n_id) def nic_add(vm, model, netdev_id, mac, rom=None): """ Add a nic to virtual machine @vm: VM object @model: nic model @netdev_id: id of netdev @mac: Mac address of new nic @rom: Rom file """ nic_id = kvm_utils.generate_random_id() if model == "virtio": model = "virtio-net-pci" device_add_cmd = "device_add %s,netdev=%s,mac=%s,id=%s" % ( model, netdev_id, mac, nic_id) if rom: device_add_cmd += ",romfile=%s" % rom logging.info("Adding nic through %s", device_add_cmd) vm.monitor.cmd(device_add_cmd) qdev = vm.monitor.info("qtree") if not nic_id in qdev: logging.error(qdev) raise error.TestFail("Device %s was not plugged into qdev" "tree" % nic_id) else: return nic_id def nic_del(vm, nic_id, wait=True): """ Remove the nic from pci tree. @vm: VM object @id: the nic id @wait: Whether need to wait for the guest to unplug the device """ nic_del_cmd = "device_del %s" % nic_id vm.monitor.cmd(nic_del_cmd) if wait: logging.info("waiting for the guest to finish the unplug") if not kvm_utils.wait_for( lambda: nic_id not in vm.monitor.info("qtree"), guest_delay, 5, 1): logging.error(vm.monitor.info("qtree")) raise error.TestError("Device is not unplugged by " "guest, please check whether the " "hotplug module was loaded in guest") logging.info("Attach a virtio nic to vm") mac = kvm_utils.generate_mac_address(vm.instance, 1) if not mac: mac = "00:00:02:00:00:02" netdev_id = netdev_add(vm) device_id = nic_add(vm, "virtio", netdev_id, mac, romfile) if "Win" not in params.get("guest_name", ""): session.sendline("dhclient %s &" % kvm_test_utils.get_linux_ifname(session, mac)) logging.info("Shutting down the primary link") vm.monitor.cmd("set_link %s down" % vm.netdev_id[0]) try: logging.info("Waiting for new nic's ip address acquisition...") if not kvm_utils.wait_for( lambda: (vm.address_cache.get(mac) is not None), 10, 1): raise error.TestFail("Could not get ip address of new nic") ip = vm.address_cache.get(mac) if not kvm_utils.verify_ip_address_ownership(ip, mac): raise error.TestFail("Could not verify the ip address of new nic") else: logging.info("Got the ip address of new nic: %s", ip) logging.info("Ping test the new nic ...") s, o = kvm_test_utils.ping(ip, 100) if s != 0: logging.error(o) raise error.TestFail("New nic failed ping test") logging.info("Detaching a virtio nic from vm") nic_del(vm, device_id) netdev_del(vm, netdev_id) finally: vm.free_mac_address(1) logging.info("Re-enabling the primary link") vm.monitor.cmd("set_link %s up" % vm.netdev_id[0])
tcpdump_cmd += " and dst %s" % guest_ip copy_files_from = vm.copy_files_to try: utils.system(dd_cmd) except error.CmdError, e: return failure # only capture the new tcp port after offload setup original_tcp_ports = re.findall( "tcp.*:(\d+).*%s" % guest_ip, utils.system_output("/bin/netstat -nap")) for i in original_tcp_ports: tcpdump_cmd += " and not port %s" % i logging.debug("Listen using command: %s", tcpdump_cmd) session2.sendline(tcpdump_cmd) if not kvm_utils.wait_for( lambda: session.cmd_status("pgrep tcpdump") == 0, 30): return (False, "Tcpdump process wasn't launched") logging.info("Start to transfer file") try: copy_files_from(filename, filename) except kvm_utils.SCPError, e: return (False, "File transfer failed (%s)" % e) logging.info("Transfer file completed") session.cmd("killall tcpdump") try: tcpdump_string = session2.read_up_to_prompt(timeout=60) except kvm_subprocess.ExpectError: return (False, "Fail to read tcpdump's output") if not compare_md5sum(filename):
def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", mig_cancel=False, offline=False, stable_check=False, clean=False, save_path=None, dest_host='localhost', mig_port=None): """ Migrate a VM locally and re-register it in the environment. @param vm: The VM to migrate. @param env: The environment dictionary. If omitted, the migrated VM will not be registered. @param mig_timeout: timeout value for migration. @param mig_protocol: migration protocol @param mig_cancel: Test migrate_cancel or not when protocol is tcp. @param dest_host: Destination host (defaults to 'localhost'). @param mig_port: Port that will be used for migration. @return: The post-migration VM, in case of same host migration, True in case of multi-host migration. """ def mig_finished(): o = vm.monitor.info("migrate") if isinstance(o, str): return "status: active" not in o else: return o.get("status") != "active" def mig_succeeded(): o = vm.monitor.info("migrate") if isinstance(o, str): return "status: completed" in o else: return o.get("status") == "completed" def mig_failed(): o = vm.monitor.info("migrate") if isinstance(o, str): return "status: failed" in o else: return o.get("status") == "failed" def mig_cancelled(): o = vm.monitor.info("migrate") if isinstance(o, str): return ("Migration status: cancelled" in o or "Migration status: canceled" in o) else: return (o.get("status") == "cancelled" or o.get("status") == "canceled") def wait_for_migration(): if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, "Waiting for migration to finish..."): raise error.TestFail("Timeout expired while waiting for migration " "to finish") if dest_host == 'localhost': dest_vm = vm.clone() if (dest_host == 'localhost') and stable_check: # Pause the dest vm after creation dest_vm.params['extra_params'] = ( dest_vm.params.get('extra_params', '') + ' -S') if dest_host == 'localhost': dest_vm.create(migration_mode=mig_protocol, mac_source=vm) try: try: if mig_protocol == "tcp": if dest_host == 'localhost': uri = "tcp:localhost:%d" % dest_vm.migration_port else: uri = 'tcp:%s:%d' % (dest_host, mig_port) elif mig_protocol == "unix": uri = "unix:%s" % dest_vm.migration_file elif mig_protocol == "exec": uri = '"exec:nc localhost %s"' % dest_vm.migration_port if offline: vm.monitor.cmd("stop") vm.monitor.migrate(uri) if mig_cancel: time.sleep(2) vm.monitor.cmd("migrate_cancel") if not kvm_utils.wait_for( mig_cancelled, 60, 2, 2, "Waiting for migration " "cancellation"): raise error.TestFail("Failed to cancel migration") if offline: vm.monitor.cmd("cont") if dest_host == 'localhost': dest_vm.destroy(gracefully=False) return vm else: wait_for_migration() if (dest_host == 'localhost') and stable_check: save_path = None or "/tmp" save1 = os.path.join(save_path, "src") save2 = os.path.join(save_path, "dst") vm.save_to_file(save1) dest_vm.save_to_file(save2) # Fail if we see deltas md5_save1 = utils.hash_file(save1) md5_save2 = utils.hash_file(save2) if md5_save1 != md5_save2: raise error.TestFail("Mismatch of VM state before " "and after migration") if (dest_host == 'localhost') and offline: dest_vm.monitor.cmd("cont") except: if dest_host == 'localhost': dest_vm.destroy() raise finally: if (dest_host == 'localhost') and stable_check and clean: logging.debug("Cleaning the state files") if os.path.isfile(save1): os.remove(save1) if os.path.isfile(save2): os.remove(save2) # Report migration status if mig_succeeded(): logging.info("Migration finished successfully") elif mig_failed(): raise error.TestFail("Migration failed") else: raise error.TestFail("Migration ended with unknown status") if dest_host == 'localhost': if "paused" in dest_vm.monitor.info("status"): logging.debug("Destination VM is paused, resuming it...") dest_vm.monitor.cmd("cont") # Kill the source VM vm.destroy(gracefully=False) # Replace the source VM with the new cloned VM if (dest_host == 'localhost') and (env is not None): env.register_vm(vm.name, dest_vm) # Return the new cloned VM if dest_host == 'localhost': return dest_vm else: return vm
def run_stress_boot(tests, params, env): """ Boots VMs until one of them becomes unresponsive, and records the maximum number of VMs successfully started: 1) boot the first vm 2) boot the second vm cloned from the first vm, check whether it boots up and all booted vms can ssh-login 3) go on until cannot create VM anymore or cannot allocate memory for VM @param test: kvm test object @param params: Dictionary with the test parameters @param env: Dictionary with test environment. """ # boot the first vm vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: raise error.TestError("VM object not found in environment") if not vm.is_alive(): raise error.TestError("VM seems to be dead; Test requires a living VM") logging.info("Waiting for first guest to be up...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into first guest") num = 2 vms = [] sessions = [session] # boot the VMs while num <= int(params.get("max_vms")): try: vm_name = "vm" + str(num) # clone vm according to the first one vm_params = params.copy() vm_params["image_snapshot"] = "yes" vm_params["kill_vm"] = "yes" vm_params["kill_vm_gracefully"] = "no" curr_vm = vm.clone(vm_name, vm_params) kvm_utils.env_register_vm(env, vm_name, curr_vm) params["vms"] += " " + vm_name # vms.append(curr_vm) logging.info("Booting guest #%d" % num) if not curr_vm.create(): raise error.TestFail("Cannot create VM #%d" % num) curr_vm_session = kvm_utils.wait_for(curr_vm.ssh_login, 240, 0, 2) if not curr_vm_session: raise error.TestFail("Could not log into guest #%d" % num) logging.info("Guest #%d boots up successfully" % num) sessions.append(curr_vm_session) # check whether all previous ssh sessions are responsive for i, vm_session in enumerate(sessions): if vm_session.get_command_status(params.get("alive_test_cmd")): raise error.TestFail("Session #%d is not responsive" % i) num += 1 except (error.TestFail, OSError): for se in sessions: se.close() logging.info("Total number booted: %d" % (num - 1)) raise else: for se in sessions: se.close() logging.info("Total number booted: %d" % (num - 1))
def run_jumbo(test, params, env): """ Test the RX jumbo frame function of vnics: 1) Boot the VM. 2) Change the MTU of guest nics and host taps depending on the NIC model. 3) Add the static ARP entry for guest NIC. 4) Wait for the MTU ok. 5) Verify the path MTU using ping. 6) Ping the guest with large frames. 7) Increment size ping. 8) Flood ping the guest with large frames. 9) Verify the path MTU. 10) Recover the MTU. @param test: KVM test object. @param params: Dictionary with the test parameters. @param env: Dictionary with test environment. """ vm = env.get_vm(params["main_vm"]) vm.verify_alive() session = vm.wait_for_login(timeout=int(params.get("login_timeout", 360))) mtu = params.get("mtu", "1500") flood_time = params.get("flood_time", "300") max_icmp_pkt_size = int(mtu) - 28 ifname = vm.get_ifname(0) ip = vm.get_address(0) if ip is None: raise error.TestError("Could not get the IP address") try: # Environment preparation ethname = kvm_test_utils.get_linux_ifname(session, vm.get_mac_address(0)) logging.info("Changing the MTU of guest ...") guest_mtu_cmd = "ifconfig %s mtu %s" % (ethname, mtu) session.cmd(guest_mtu_cmd) logging.info("Chaning the MTU of host tap ...") host_mtu_cmd = "ifconfig %s mtu %s" % (ifname, mtu) utils.run(host_mtu_cmd) logging.info("Add a temporary static ARP entry ...") arp_add_cmd = "arp -s %s %s -i %s" % (ip, vm.get_mac_address(0), ifname) utils.run(arp_add_cmd) def is_mtu_ok(): s, o = kvm_test_utils.ping(ip, 1, interface=ifname, packetsize=max_icmp_pkt_size, hint="do", timeout=2) return s == 0 def verify_mtu(): logging.info("Verify the path MTU") s, o = kvm_test_utils.ping(ip, 10, interface=ifname, packetsize=max_icmp_pkt_size, hint="do", timeout=15) if s != 0: logging.error(o) raise error.TestFail("Path MTU is not as expected") if kvm_test_utils.get_loss_ratio(o) != 0: logging.error(o) raise error.TestFail("Packet loss ratio during MTU " "verification is not zero") def flood_ping(): logging.info("Flood with large frames") kvm_test_utils.ping(ip, interface=ifname, packetsize=max_icmp_pkt_size, flood=True, timeout=float(flood_time)) def large_frame_ping(count=100): logging.info("Large frame ping") s, o = kvm_test_utils.ping(ip, count, interface=ifname, packetsize=max_icmp_pkt_size, timeout=float(count) * 2) ratio = kvm_test_utils.get_loss_ratio(o) if ratio != 0: raise error.TestFail("Loss ratio of large frame ping is %s" % ratio) def size_increase_ping(step=random.randrange(90, 110)): logging.info("Size increase ping") for size in range(0, max_icmp_pkt_size + 1, step): logging.info("Ping %s with size %s", ip, size) s, o = kvm_test_utils.ping(ip, 1, interface=ifname, packetsize=size, hint="do", timeout=1) if s != 0: s, o = kvm_test_utils.ping(ip, 10, interface=ifname, packetsize=size, adaptive=True, hint="do", timeout=20) if kvm_test_utils.get_loss_ratio(o) > int( params.get("fail_ratio", 50)): raise error.TestFail("Ping loss ratio is greater " "than 50% for size %s" % size) logging.info("Waiting for the MTU to be OK") wait_mtu_ok = 10 if not kvm_utils.wait_for(is_mtu_ok, wait_mtu_ok, 0, 1): logging.debug(commands.getoutput("ifconfig -a")) raise error.TestError("MTU is not as expected even after %s " "seconds" % wait_mtu_ok) # Functional Test verify_mtu() large_frame_ping() size_increase_ping() # Stress test flood_ping() verify_mtu() finally: # Environment clean session.close() logging.info("Removing the temporary ARP entry") utils.run("arp -d %s -i %s" % (ip, ifname))
def run_unittest(test, params, env): """ KVM RHEL-6 style unit test: 1) Resume a stopped VM 2) Wait for VM to terminate 3) If qemu exited with code = 0, the unittest passed. Otherwise, it failed 4) Collect all logs generated @param test: kvm test object @param params: Dictionary with the test parameters @param env: Dictionary with test environment """ unittest_dir = os.path.join(test.bindir, 'unittests') if not os.path.isdir(unittest_dir): raise error.TestError("No unittest dir %s available (did you run the " "build test first?)" % unittest_dir) os.chdir(unittest_dir) unittest_list = glob.glob('*.flat') if not unittest_list: raise error.TestError("No unittest files available (did you run the " "build test first?)") logging.debug('Flat file list: %s', unittest_list) unittest_cfg = os.path.join(unittest_dir, 'unittests.cfg') parser = ConfigParser.ConfigParser() parser.read(unittest_cfg) test_list = parser.sections() if not test_list: raise error.TestError("No tests listed on config file %s" % unittest_cfg) logging.debug('Unit test list: %s', test_list) if params.get('test_list'): test_list = params.get('test_list').split() logging.info('Original test list overriden by user') logging.info('User defined unit test list: %s', test_list) nfail = 0 tests_failed = [] timeout = int(params.get('unittest_timeout', 600)) extra_params_original = params['extra_params'] for t in test_list: logging.info('Running %s', t) flat_file = None if parser.has_option(t, 'file'): flat_file = parser.get(t, 'file') if flat_file is None: nfail += 1 tests_failed.append(t) logging.error( 'Unittest config file %s has section %s but no ' 'mandatory option file', unittest_cfg, t) continue if flat_file not in unittest_list: nfail += 1 tests_failed.append(t) logging.error( 'Unittest file %s referenced in config file %s but ' 'was not find under the unittest dir', flat_file, unittest_cfg) continue smp = None if parser.has_option(t, 'smp'): smp = int(parser.get(t, 'smp')) params['smp'] = smp extra_params = None if parser.has_option(t, 'extra_params'): extra_params = parser.get(t, 'extra_params') params['extra_params'] += ' %s' % extra_params vm_name = params.get("main_vm") params['kernel'] = os.path.join(unittest_dir, flat_file) testlog_path = os.path.join(test.debugdir, "%s.log" % t) try: try: vm_name = params.get('main_vm') kvm_preprocessing.preprocess_vm(test, params, env, vm_name) vm = env.get_vm(vm_name) vm.create() vm.monitor.cmd("cont") logging.info( "Waiting for unittest %s to complete, timeout %s, " "output in %s", t, timeout, vm.get_testlog_filename()) if not kvm_utils.wait_for(vm.is_dead, timeout): raise error.TestFail("Timeout elapsed (%ss)" % timeout) # Check qemu's exit status status = vm.process.get_status() if status != 0: nfail += 1 tests_failed.append(t) logging.error("Unit test %s failed", t) except Exception, e: nfail += 1 tests_failed.append(t) logging.error('Exception happened during %s: %s', t, str(e)) finally: try: shutil.copy(vm.get_testlog_filename(), testlog_path) logging.info("Unit test log collected and available under %s", testlog_path) except (NameError, IOError): logging.error("Not possible to collect logs") # Restore the extra params so other tests can run normally params['extra_params'] = extra_params_original if nfail != 0: raise error.TestFail("Unit tests failed: %s" % " ".join(tests_failed))
if not vm: return scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name) try: if vm.monitor: vm.monitor.screendump(scrdump_filename) except kvm_monitor.MonitorError, e: logging.warn(e) if params.get("kill_vm") == "yes": kill_vm_timeout = float(params.get("kill_vm_timeout", 0)) if kill_vm_timeout: logging.debug("'kill_vm' specified; waiting for VM to shut down " "before killing it...") kvm_utils.wait_for(vm.is_dead, kill_vm_timeout, 0, 1) else: logging.debug("'kill_vm' specified; killing VM...") vm.destroy(gracefully=params.get("kill_vm_gracefully") == "yes") def process_command(test, params, env, command, command_timeout, command_noncritical): """ Pre- or post- custom commands to be executed before/after a test is run @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 command: Command to be run. @param command_timeout: Timeout for command execution.
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 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 = env.get_vm(params["main_vm"]) vm.verify_alive() timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(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") # Determine syntax of drive hotplug # __com.redhat_drive_add == qemu-kvm-0.12 on RHEL 6 if len(re.findall("\n__com.redhat_drive_add", cmd_output)) > 0: drive_cmd_type = "__com.redhat_drive_add" # drive_add == qemu-kvm-0.13 onwards elif len(re.findall("\ndrive_add", cmd_output)) > 0: drive_cmd_type = "drive_add" else: raise error.TestError("Unknow version of qemu") # Probe qemu for a list of supported devices devices_support = vm.monitor.cmd("%s ?" % cmd_type) 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 = params.object_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() device_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" % (device_id, tested_model) elif test_type == "block": image_params = params.object_params("stg") image_filename = kvm_vm.get_image_filename(image_params, test.bindir) controller_model = None if tested_model == "virtio": tested_model = "virtio-blk-pci" if tested_model == "scsi": tested_model = "scsi-disk" controller_model = "lsi53c895a" if len(re.findall(controller_model, devices_support)) == 0: raise error.TestError("scsi controller device (%s) not " "supported by qemu" % controller_model) if controller_model is not None: controller_id = "controller-" + device_id controller_add_cmd = ("device_add %s,id=%s" % (controller_model, controller_id)) vm.monitor.cmd(controller_add_cmd) if drive_cmd_type == "drive_add": driver_add_cmd = ("drive_add auto " "file=%s,if=none,id=%s,format=%s" % (image_filename, driver_id, image_format)) elif drive_cmd_type == "__com.redhat_drive_add": 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" % (device_id, tested_model, driver_id)) vm.monitor.cmd(driver_add_cmd) # Check if the device is support in qemu 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 device_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" % device_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_autotest(test, params, env): """ Run an autotest test inside a guest. @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: raise error.TestError("VM object not found in environment") if not vm.is_alive(): raise error.TestError("VM seems to be dead; Test requires a living VM") logging.info("Logging into guest...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest") logging.info("Logged in") # Collect some info test_name = params.get("test_name") test_timeout = int(params.get("test_timeout", 300)) test_control_file = params.get("test_control_file", "control") tarred_autotest_path = "/tmp/autotest.tar.bz2" tarred_test_path = "/tmp/%s.tar.bz2" % test_name # tar the contents of bindir/autotest cmd = "cd %s; tar cvjf %s autotest/*" cmd += " --exclude=autotest/tests" cmd += " --exclude=autotest/results" cmd += " --exclude=autotest/tmp" cmd += " --exclude=autotest/control" cmd += " --exclude=*.pyc" cmd += " --exclude=*.svn" cmd += " --exclude=*.git" kvm_subprocess.run_fg(cmd % (test.bindir, tarred_autotest_path), timeout=30) # tar the contents of bindir/autotest/tests/<test_name> cmd = "cd %s; tar cvjf %s %s/*" cmd += " --exclude=*.pyc" cmd += " --exclude=*.svn" cmd += " --exclude=*.git" kvm_subprocess.run_fg( cmd % (os.path.join(test.bindir, "autotest", "tests"), tarred_test_path, test_name), timeout=30 ) # Check if we need to copy autotest.tar.bz2 copy = False output = session.get_command_output("ls -l autotest.tar.bz2") if "such file" in output: copy = True else: size = int(output.split()[4]) if size != os.path.getsize(tarred_autotest_path): copy = True # Perform the copy if copy: logging.info("Copying autotest.tar.bz2 to guest" " (file is missing or has a different size)...") if not vm.scp_to_remote(tarred_autotest_path, ""): raise error.TestFail("Could not copy autotest.tar.bz2 to guest") # Check if we need to copy <test_name>.tar.bz2 copy = False output = session.get_command_output("ls -l %s.tar.bz2" % test_name) if "such file" in output: copy = True else: size = int(output.split()[4]) if size != os.path.getsize(tarred_test_path): copy = True # Perform the copy if copy: logging.info("Copying %s.tar.bz2 to guest (file is missing or has a" " different size)..." % test_name) if not vm.scp_to_remote(tarred_test_path, ""): raise error.TestFail("Could not copy %s.tar.bz2 to guest" % test_name) # Extract autotest.tar.bz2 logging.info("Extracting autotest.tar.bz2...") status = session.get_command_status("tar xvfj autotest.tar.bz2") if status != 0: raise error.TestFail("Could not extract autotest.tar.bz2") # mkdir autotest/tests session.sendline("mkdir autotest/tests") # Extract <test_name>.tar.bz2 into autotest/tests logging.info("Extracting %s.tar.bz2..." % test_name) status = session.get_command_status("tar xvfj %s.tar.bz2 -C " "autotest/tests" % test_name) if status != 0: raise error.TestFail("Could not extract %s.tar.bz2" % test_name) # Cleaning up old remaining results session.sendline("rm -rf autotest/results/*") # Copying the selected control file (located inside # test.bindir/autotest_control to the autotest dir control_file_path = os.path.join(test.bindir, "autotest_control", test_control_file) if not vm.scp_to_remote(control_file_path, "autotest/control"): raise error.TestFail("Could not copy the test control file to guest") # Run the test logging.info("Running test '%s'..." % test_name) session.sendline("cd autotest") session.sendline("rm -f control.state") session.read_up_to_prompt() session.sendline("bin/autotest control") logging.info("---------------- Test output ----------------") match = session.read_up_to_prompt(timeout=test_timeout, print_func=logging.info)[0] logging.info("---------------- End of test output ----------------") if not match: raise error.TestFail("Timeout elapsed while waiting for test to " "complete") # Get the results generated by autotest output = session.get_command_output("cat results/*/status") # Parse test results result_list = scan_results.parse_results(output) # Report test results and check for FAIL/ERROR status logging.info("Results (test, status, duration, info):") status_error = False status_fail = False if result_list == []: status_fail = True message_fail = "Test '%s' did not produce any recognizable " "results" % test_name for result in result_list: logging.info(str(result)) if result[1] == "FAIL": status_fail = True message_fail = "Test '%s' ended with FAIL " "(info: '%s')" % (result[0], result[3]) if result[1] == "ERROR": status_error = True message_error = "Test '%s' ended with ERROR " "(info: '%s')" % (result[0], result[3]) if result[1] == "ABORT": status_error = True message_error = "Test '%s' ended with ABORT " "(info: '%s')" % (result[0], result[3]) # Copy test results to the local bindir/guest_results logging.info("Copying results back from guest...") guest_results_dir = os.path.join(test.outputdir, "guest_results") if not os.path.exists(guest_results_dir): os.mkdir(guest_results_dir) if not vm.scp_from_remote("autotest/results/default/*", guest_results_dir): logging.error("Could not copy results back from guest") # Fail the test if necessary if status_fail: raise error.TestFail(message_fail) elif status_error: raise error.TestError(message_error)
def preprocess(test, params, env): """ Preprocess all VMs and images according to the instructions in params. Also, collect some host information, such as the KVM version. @param test: An Autotest test object. @param params: A dict containing all VM and image parameters. @param env: The environment (a dict-like object). """ error.context("preprocessing") # Start tcpdump if it isn't already running if "address_cache" not in env: env["address_cache"] = {} if "tcpdump" in env and not env["tcpdump"].is_alive(): env["tcpdump"].close() del env["tcpdump"] if "tcpdump" not in env and params.get("run_tcpdump", "yes") == "yes": cmd = "%s -npvi any 'dst port 68'" % kvm_utils.find_command("tcpdump") logging.debug("Starting tcpdump (%s)...", cmd) env["tcpdump"] = kvm_subprocess.Tail( command=cmd, output_func=_update_address_cache, output_params=(env["address_cache"], )) if kvm_utils.wait_for(lambda: not env["tcpdump"].is_alive(), 0.1, 0.1, 1.0): logging.warn("Could not start tcpdump") logging.warn("Status: %s" % env["tcpdump"].get_status()) logging.warn( "Output:" + kvm_utils.format_str_for_message(env["tcpdump"].get_output())) # Destroy and remove VMs that are no longer needed in the environment requested_vms = params.objects("vms") for key in env.keys(): vm = env[key] if not kvm_utils.is_vm(vm): continue if not vm.name in requested_vms: logging.debug("VM '%s' found in environment but not required for " "test; removing it..." % vm.name) vm.destroy() del env[key] # Get the KVM kernel module version and write it as a keyval logging.debug("Fetching KVM module version...") if os.path.exists("/dev/kvm"): try: kvm_version = open("/sys/module/kvm/version").read().strip() except: kvm_version = os.uname()[2] else: kvm_version = "Unknown" logging.debug("KVM module not loaded") logging.debug("KVM version: %s" % kvm_version) test.write_test_keyval({"kvm_version": kvm_version}) # Get the KVM userspace version and write it as a keyval logging.debug("Fetching KVM userspace version...") qemu_path = kvm_utils.get_path(test.bindir, params.get("qemu_binary", "qemu")) version_line = commands.getoutput("%s -help | head -n 1" % qemu_path) matches = re.findall("[Vv]ersion .*?,", version_line) if matches: kvm_userspace_version = " ".join(matches[0].split()[1:]).strip(",") else: kvm_userspace_version = "Unknown" logging.debug("Could not fetch KVM userspace version") logging.debug("KVM userspace version: %s" % kvm_userspace_version) test.write_test_keyval({"kvm_userspace_version": kvm_userspace_version}) if params.get("setup_hugepages") == "yes": h = test_setup.HugePageConfig(params) h.setup() if params.get("type") == "unattended_install": u = test_setup.UnattendedInstallConfig(test, params) u.setup() if params.get("type") == "enospc": e = test_setup.EnospcConfig(test, params) e.setup() # Execute any pre_commands if params.get("pre_command"): process_command(test, params, env, params.get("pre_command"), int(params.get("pre_command_timeout", "600")), params.get("pre_command_noncritical") == "yes") # Preprocess all VMs and images process(test, params, env, preprocess_image, preprocess_vm) # Start the screendump thread if params.get("take_regular_screendumps") == "yes": logging.debug("Starting screendump thread") global _screendump_thread, _screendump_thread_termination_event _screendump_thread_termination_event = threading.Event() _screendump_thread = threading.Thread(target=_take_screendumps, args=(test, params, env)) _screendump_thread.start()
if not vm: return scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name) try: if vm.monitor: vm.monitor.screendump(scrdump_filename) except kvm_monitor.MonitorError, e: logging.warn(e) if params.get("kill_vm") == "yes": kill_vm_timeout = float(params.get("kill_vm_timeout", 0)) if kill_vm_timeout: logging.debug("'kill_vm' specified; waiting for VM to shut down " "before killing it...") kvm_utils.wait_for(vm.is_dead, kill_vm_timeout, 0, 1) else: logging.debug("'kill_vm' specified; killing VM...") vm.destroy(gracefully = params.get("kill_vm_gracefully") == "yes") def process_command(test, params, env, command, command_timeout, command_noncritical): """ Pre- or post- custom commands to be executed before/after a test is run @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 command: Command to be run. @param command_timeout: Timeout for command execution.
def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", mig_cancel=False, offline=False, stable_check=False, clean=False, save_path=None, dest_host='localhost', mig_port=None): """ Migrate a VM locally and re-register it in the environment. @param vm: The VM to migrate. @param env: The environment dictionary. If omitted, the migrated VM will not be registered. @param mig_timeout: timeout value for migration. @param mig_protocol: migration protocol @param mig_cancel: Test migrate_cancel or not when protocol is tcp. @param dest_host: Destination host (defaults to 'localhost'). @param mig_port: Port that will be used for migration. @return: The post-migration VM, in case of same host migration, True in case of multi-host migration. """ def mig_finished(): o = vm.monitor.info("migrate") if isinstance(o, str): return "status: active" not in o else: return o.get("status") != "active" def mig_succeeded(): o = vm.monitor.info("migrate") if isinstance(o, str): return "status: completed" in o else: return o.get("status") == "completed" def mig_failed(): o = vm.monitor.info("migrate") if isinstance(o, str): return "status: failed" in o else: return o.get("status") == "failed" def mig_cancelled(): o = vm.monitor.info("migrate") if isinstance(o, str): return ("Migration status: cancelled" in o or "Migration status: canceled" in o) else: return (o.get("status") == "cancelled" or o.get("status") == "canceled") def wait_for_migration(): if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, "Waiting for migration to finish..."): raise error.TestFail("Timeout expired while waiting for migration " "to finish") if dest_host == 'localhost': dest_vm = vm.clone() if (dest_host == 'localhost') and stable_check: # Pause the dest vm after creation dest_vm.params['extra_params'] = (dest_vm.params.get('extra_params','') + ' -S') if dest_host == 'localhost': dest_vm.create(migration_mode=mig_protocol, mac_source=vm) try: try: if mig_protocol == "tcp": if dest_host == 'localhost': uri = "tcp:localhost:%d" % dest_vm.migration_port else: uri = 'tcp:%s:%d' % (dest_host, mig_port) elif mig_protocol == "unix": uri = "unix:%s" % dest_vm.migration_file elif mig_protocol == "exec": uri = '"exec:nc localhost %s"' % dest_vm.migration_port if offline: vm.monitor.cmd("stop") vm.monitor.migrate(uri) if mig_cancel: time.sleep(2) vm.monitor.cmd("migrate_cancel") if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, "Waiting for migration " "cancellation"): raise error.TestFail("Failed to cancel migration") if offline: vm.monitor.cmd("cont") if dest_host == 'localhost': dest_vm.destroy(gracefully=False) return vm else: wait_for_migration() if (dest_host == 'localhost') and stable_check: save_path = None or "/tmp" save1 = os.path.join(save_path, "src") save2 = os.path.join(save_path, "dst") vm.save_to_file(save1) dest_vm.save_to_file(save2) # Fail if we see deltas md5_save1 = utils.hash_file(save1) md5_save2 = utils.hash_file(save2) if md5_save1 != md5_save2: raise error.TestFail("Mismatch of VM state before " "and after migration") if (dest_host == 'localhost') and offline: dest_vm.monitor.cmd("cont") except: if dest_host == 'localhost': dest_vm.destroy() raise finally: if (dest_host == 'localhost') and stable_check and clean: logging.debug("Cleaning the state files") if os.path.isfile(save1): os.remove(save1) if os.path.isfile(save2): os.remove(save2) # Report migration status if mig_succeeded(): logging.info("Migration finished successfully") elif mig_failed(): raise error.TestFail("Migration failed") else: raise error.TestFail("Migration ended with unknown status") if dest_host == 'localhost': if "paused" in dest_vm.monitor.info("status"): logging.debug("Destination VM is paused, resuming it...") dest_vm.monitor.cmd("cont") # Kill the source VM vm.destroy(gracefully=False) # Replace the source VM with the new cloned VM if (dest_host == 'localhost') and (env is not None): env.register_vm(vm.name, dest_vm) # Return the new cloned VM if dest_host == 'localhost': return dest_vm else: return vm
def run_timedrift(test, params, env): """ Time drift test (mainly for Windows guests): 1) Log into a guest. 2) Take a time reading from the guest and host. 3) Run load on the guest and host. 4) Take a second time reading. 5) Stop the load and rest for a while. 6) Take a third time reading. 7) If the drift immediately after load is higher than a user- specified value (in %), fail. If the drift after the rest period is higher than a user-specified value, fail. @param test: KVM test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ vm = kvm_utils.env_get_vm(env, params.get("main_vm")) if not vm: raise error.TestError("VM object not found in environment") if not vm.is_alive(): raise error.TestError("VM seems to be dead; Test requires a living VM") logging.info("Waiting for guest to be up...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest") logging.info("Logged in") # Collect test parameters: # Command to run to get the current time time_command = params.get("time_command") # Filter which should match a string to be passed to time.strptime() time_filter_re = params.get("time_filter_re") # Time format for time.strptime() time_format = params.get("time_format") guest_load_command = params.get("guest_load_command") guest_load_stop_command = params.get("guest_load_stop_command") host_load_command = params.get("host_load_command") guest_load_instances = int(params.get("guest_load_instances", "1")) host_load_instances = int(params.get("host_load_instances", "0")) # CPU affinity mask for taskset cpu_mask = params.get("cpu_mask", "0xFF") load_duration = float(params.get("load_duration", "30")) rest_duration = float(params.get("rest_duration", "10")) drift_threshold = float(params.get("drift_threshold", "200")) drift_threshold_after_rest = float(params.get("drift_threshold_after_rest", "200")) guest_load_sessions = [] host_load_sessions = [] # Remember the VM's previous CPU affinity prev_cpu_mask = commands.getoutput("taskset -p %s" % vm.get_pid()) prev_cpu_mask = prev_cpu_mask.split()[-1] # Set the VM's CPU affinity commands.getoutput("taskset -p %s %s" % (cpu_mask, vm.get_pid())) try: # Get time before load host_time_0 = time.time() session.sendline(time_command) (match, s) = session.read_up_to_prompt() s = re.findall(time_filter_re, s)[0] guest_time_0 = time.mktime(time.strptime(s, time_format)) # Run some load on the guest logging.info("Starting load on guest...") for i in range(guest_load_instances): load_session = vm.ssh_login() if not load_session: raise error.TestFail("Could not log into guest") load_session.set_output_prefix("(guest load %d) " % i) load_session.set_output_func(logging.debug) load_session.sendline(guest_load_command) guest_load_sessions.append(load_session) # Run some load on the host logging.info("Starting load on host...") for i in range(host_load_instances): host_load_sessions.append( kvm_subprocess.run_bg( host_load_command, output_func=logging.debug, output_prefix="(host load %d) " % i, timeout=0.5 ) ) # Set the CPU affinity of the shell running the load process pid = host_load_sessions[-1].get_shell_pid() commands.getoutput("taskset -p %s %s" % (cpu_mask, pid)) # Try setting the CPU affinity of the load process itself pid = host_load_sessions[-1].get_pid() if pid: commands.getoutput("taskset -p %s %s" % (cpu_mask, pid)) # Sleep for a while (during load) logging.info("Sleeping for %s seconds..." % load_duration) time.sleep(load_duration) # Get time delta after load host_time_1 = time.time() session.sendline(time_command) (match, s) = session.read_up_to_prompt() s = re.findall(time_filter_re, s)[0] guest_time_1 = time.mktime(time.strptime(s, time_format)) # Report results host_delta = host_time_1 - host_time_0 guest_delta = guest_time_1 - guest_time_0 drift = 100.0 * (host_delta - guest_delta) / host_delta logging.info("Host duration: %.2f" % host_delta) logging.info("Guest duration: %.2f" % guest_delta) logging.info("Drift: %.2f%%" % drift) finally: logging.info("Cleaning up...") # Restore the VM's CPU affinity commands.getoutput("taskset -p %s %s" % (prev_cpu_mask, vm.get_pid())) # Stop the guest load if guest_load_stop_command: session.get_command_output(guest_load_stop_command) # Close all load shell sessions for load_session in guest_load_sessions: load_session.close() for load_session in host_load_sessions: load_session.close() # Sleep again (rest) logging.info("Sleeping for %s seconds..." % rest_duration) time.sleep(rest_duration) # Get time after rest host_time_2 = time.time() session.sendline(time_command) (match, s) = session.read_up_to_prompt() s = re.findall(time_filter_re, s)[0] guest_time_2 = time.mktime(time.strptime(s, time_format)) # Report results host_delta_total = host_time_2 - host_time_0 guest_delta_total = guest_time_2 - guest_time_0 drift_total = 100.0 * (host_delta_total - guest_delta_total) / host_delta logging.info("Total host duration including rest: %.2f" % host_delta_total) logging.info("Total guest duration including rest: %.2f" % guest_delta_total) logging.info("Total drift after rest: %.2f%%" % drift_total) # Fail the test if necessary if drift > drift_threshold: raise error.TestFail("Time drift too large: %.2f%%" % drift) if drift > drift_threshold_after_rest: raise error.TestFail("Time drift too large after rest period: %.2f%%" % drift_total) session.close()
def preprocess(test, params, env): """ Preprocess all VMs and images according to the instructions in params. Also, collect some host information, such as the KVM version. @param test: An Autotest test object. @param params: A dict containing all VM and image parameters. @param env: The environment (a dict-like object). """ error.context("preprocessing") # Start tcpdump if it isn't already running if "address_cache" not in env: env["address_cache"] = {} if "tcpdump" in env and not env["tcpdump"].is_alive(): env["tcpdump"].close() del env["tcpdump"] if "tcpdump" not in env and params.get("run_tcpdump", "yes") == "yes": cmd = "%s -npvi any 'dst port 68'" % kvm_utils.find_command("tcpdump") logging.debug("Starting tcpdump (%s)...", cmd) env["tcpdump"] = kvm_subprocess.Tail( command=cmd, output_func=_update_address_cache, output_params=(env["address_cache"],)) if kvm_utils.wait_for(lambda: not env["tcpdump"].is_alive(), 0.1, 0.1, 1.0): logging.warn("Could not start tcpdump") logging.warn("Status: %s" % env["tcpdump"].get_status()) logging.warn("Output:" + kvm_utils.format_str_for_message( env["tcpdump"].get_output())) # Destroy and remove VMs that are no longer needed in the environment requested_vms = params.objects("vms") for key in env.keys(): vm = env[key] if not kvm_utils.is_vm(vm): continue if not vm.name in requested_vms: logging.debug("VM '%s' found in environment but not required for " "test; removing it..." % vm.name) vm.destroy() del env[key] # Get the KVM kernel module version and write it as a keyval logging.debug("Fetching KVM module version...") if os.path.exists("/dev/kvm"): try: kvm_version = open("/sys/module/kvm/version").read().strip() except: kvm_version = os.uname()[2] else: kvm_version = "Unknown" logging.debug("KVM module not loaded") logging.debug("KVM version: %s" % kvm_version) test.write_test_keyval({"kvm_version": kvm_version}) # Get the KVM userspace version and write it as a keyval logging.debug("Fetching KVM userspace version...") qemu_path = kvm_utils.get_path(test.bindir, params.get("qemu_binary", "qemu")) version_line = commands.getoutput("%s -help | head -n 1" % qemu_path) matches = re.findall("[Vv]ersion .*?,", version_line) if matches: kvm_userspace_version = " ".join(matches[0].split()[1:]).strip(",") else: kvm_userspace_version = "Unknown" logging.debug("Could not fetch KVM userspace version") logging.debug("KVM userspace version: %s" % kvm_userspace_version) test.write_test_keyval({"kvm_userspace_version": kvm_userspace_version}) if params.get("setup_hugepages") == "yes": h = test_setup.HugePageConfig(params) h.setup() if params.get("type") == "unattended_install": u = test_setup.UnattendedInstallConfig(test, params) u.setup() if params.get("type") == "enospc": e = test_setup.EnospcConfig(test, params) e.setup() # Execute any pre_commands if params.get("pre_command"): process_command(test, params, env, params.get("pre_command"), int(params.get("pre_command_timeout", "600")), params.get("pre_command_noncritical") == "yes") # Preprocess all VMs and images process(test, params, env, preprocess_image, preprocess_vm) # Start the screendump thread if params.get("take_regular_screendumps") == "yes": logging.debug("Starting screendump thread") global _screendump_thread, _screendump_thread_termination_event _screendump_thread_termination_event = threading.Event() _screendump_thread = threading.Thread(target=_take_screendumps, args=(test, params, env)) _screendump_thread.start()
def run_migration(test, params, env): """ KVM migration test: 1) Get two live VMs. One will be the 'source', the other will be the 'destination'. 2) Verify if the source VM supports migration. If it does, proceed with the test 3) Send a migration command to the source vm and wait until it's finished. 4) Kill off the source vm 3) Log into the destination vm after the migration is finished. 4) Compare the output of a reference command executed on the source with the output of the same command on the destination machine @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ src_vm_name = params.get("migration_src") vm = kvm_utils.env_get_vm(env, src_vm_name) if not vm: raise error.TestError("VM '%s' not found in environment" % src_vm_name) if not vm.is_alive(): raise error.TestError("VM '%s' seems to be dead; Test requires a" " living VM" % src_vm_name) dest_vm_name = params.get("migration_dst") dest_vm = kvm_utils.env_get_vm(env, dest_vm_name) if not dest_vm: raise error.TestError("VM '%s' not found in environment" % dest_vm_name) if not dest_vm.is_alive(): raise error.TestError("VM '%s' seems to be dead; Test requires a" " living VM" % dest_vm_name) pre_scrdump_filename = os.path.join(test.debugdir, "migration_pre.ppm") post_scrdump_filename = os.path.join(test.debugdir, "migration_post.ppm") # See if migration is supported s, o = vm.send_monitor_cmd("help info") if not "info migrate" in o: raise error.TestError("Migration is not supported") # Log into guest and get the output of migration_test_command logging.info("Waiting for guest to be up...") session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) if not session: raise error.TestFail("Could not log into guest") logging.info("Logged in") reference_output = session.get_command_output(params.get("migration_test_" "command")) session.close() # Define the migration command cmd = "migrate -d tcp:localhost:%d" % dest_vm.migration_port logging.debug("Migration command: %s" % cmd) # Migrate s, o = vm.send_monitor_cmd(cmd) if s: logging.error("Migration command failed (command: %r, output: %r)" % (cmd, o)) raise error.TestFail("Migration command failed") # Define some helper functions def mig_finished(): s, o = vm.send_monitor_cmd("info migrate") if s: return False if "Migration status: active" in o: return False return True def mig_succeeded(): s, o = vm.send_monitor_cmd("info migrate") if s == 0 and "Migration status: completed" in o: return True return False def mig_failed(): s, o = vm.send_monitor_cmd("info migrate") if s == 0 and "Migration status: failed" in o: return True return False # Wait for migration to finish if not kvm_utils.wait_for(mig_finished, 90, 2, 2, "Waiting for migration to finish..."): raise error.TestFail("Timeout elapsed while waiting for migration to" "finish") # Report migration status if mig_succeeded(): logging.info("Migration finished successfully") else: if mig_failed(): message = "Migration failed" else: message = "Migration ended with unknown status" raise error.TestFail(message) # Get 'post' screendump dest_vm.send_monitor_cmd("screendump %s" % post_scrdump_filename) # Get 'pre' screendump vm.send_monitor_cmd("screendump %s" % pre_scrdump_filename) # Kill the source VM vm.send_monitor_cmd("quit", block=False) # Hack: it seems that the first attempt to communicate with the SSH port # following migration always fails (or succeeds after a very long time). # So just connect to the port once so the following call to ssh_login # succeeds. dest_vm.is_sshd_running(timeout=0.0) # Log into guest and get the output of migration_test_command logging.info("Logging into guest after migration...") session = dest_vm.ssh_login() if not session: raise error.TestFail("Could not log into guest after migration") logging.info("Logged in after migration") output = session.get_command_output(params.get("migration_test_command")) session.close() # Compare output to reference output if output != reference_output: logging.info("Command output before migration differs from command" " output after migration") logging.info("Command: %s" % params.get("migration_test_command")) logging.info("Output before:" + kvm_utils.format_str_for_message(reference_output)) logging.info("Output after:" + kvm_utils.format_str_for_message(output)) raise error.TestFail("Command produced different output before and" " after migration")
def preprocess(test, params, env): """ Preprocess all VMs and images according to the instructions in params. Also, collect some host information, such as the KVM version. @param test: An Autotest test object. @param params: A dict containing all VM and image parameters. @param env: The environment (a dict-like object). """ # Start tcpdump if it isn't already running if not env.has_key("address_cache"): env["address_cache"] = {} if env.has_key("tcpdump") and not env["tcpdump"].is_alive(): env["tcpdump"].close() del env["tcpdump"] if not env.has_key("tcpdump"): command = "/usr/sbin/tcpdump -npvi any 'dst port 68'" logging.debug("Starting tcpdump (%s)...", command) env["tcpdump"] = kvm_subprocess.kvm_tail( command=command, output_func=_update_address_cache, output_params=(env["address_cache"],)) if kvm_utils.wait_for(lambda: not env["tcpdump"].is_alive(), 0.1, 0.1, 1.0): logging.warn("Could not start tcpdump") logging.warn("Status: %s" % env["tcpdump"].get_status()) logging.warn("Output:" + kvm_utils.format_str_for_message( env["tcpdump"].get_output())) # Destroy and remove VMs that are no longer needed in the environment requested_vms = kvm_utils.get_sub_dict_names(params, "vms") for key in env.keys(): vm = env[key] if not kvm_utils.is_vm(vm): continue if not vm.name in requested_vms: logging.debug("VM '%s' found in environment but not required for" " test; removing it..." % vm.name) vm.destroy() del env[key] # Execute any pre_commands if params.get("pre_command"): process_command(test, params, env, params.get("pre_command"), int(params.get("pre_command_timeout", "600")), params.get("pre_command_noncritical") == "yes") # Preprocess all VMs and images process(test, params, env, preprocess_image, preprocess_vm) # Get the KVM kernel module version and write it as a keyval logging.debug("Fetching KVM module version...") if os.path.exists("/dev/kvm"): kvm_version = os.uname()[2] try: file = open("/sys/module/kvm/version", "r") kvm_version = file.read().strip() file.close() except: pass else: kvm_version = "Unknown" logging.debug("KVM module not loaded") logging.debug("KVM version: %s" % kvm_version) test.write_test_keyval({"kvm_version": kvm_version}) # Get the KVM userspace version and write it as a keyval logging.debug("Fetching KVM userspace version...") qemu_path = kvm_utils.get_path(test.bindir, params.get("qemu_binary", "qemu")) version_line = commands.getoutput("%s -help | head -n 1" % qemu_path) exp = re.compile("[Vv]ersion .*?,") match = exp.search(version_line) if match: kvm_userspace_version = " ".join(match.group().split()[1:]).strip(",") else: kvm_userspace_version = "Unknown" logging.debug("Could not fetch KVM userspace version") logging.debug("KVM userspace version: %s" % kvm_userspace_version) test.write_test_keyval({"kvm_userspace_version": kvm_userspace_version})