def __init__(self, name, params, root_dir, address_cache): """ Initialize the object and set a few attributes. @param name: The name of the object @param params: A dict containing VM params (see method make_qemu_command for a full description) @param root_dir: Base directory for relative filenames @param address_cache: A dict that maps MAC addresses to IP addresses """ self.process = None self.serial_console = None self.redirs = {} self.vnc_port = 5900 self.monitors = [] self.pci_assignable = None self.netdev_id = [] self.uuid = None self.name = name self.params = params self.root_dir = root_dir self.address_cache = address_cache # Find a unique identifier for this VM while True: self.instance = (time.strftime("%Y%m%d-%H%M%S-") + kvm_utils.generate_random_string(4)) if not glob.glob("/tmp/*%s" % self.instance): break
def __init__(self, name, params, root_dir, address_cache): """ Initialize the object and set a few attributes. @param name: The name of the object @param params: A dict containing VM params (see method make_qemu_command for a full description) @param root_dir: Base directory for relative filenames @param address_cache: A dict that maps MAC addresses to IP addresses """ self.process = None self.redirs = {} self.vnc_port = 5900 self.uuid = None self.name = name self.params = params self.root_dir = root_dir self.address_cache = address_cache # Find available monitor filename while True: # The monitor filename should be unique self.instance = time.strftime("%Y%m%d-%H%M%S-") + kvm_utils.generate_random_string(4) self.monitor_file_name = os.path.join("/tmp", "monitor-" + self.instance) if not os.path.exists(self.monitor_file_name): break
def _take_screendumps(test, params, env): global _screendump_thread_termination_event temp_dir = test.debugdir if params.get("screendump_temp_dir"): temp_dir = kvm_utils.get_path(test.bindir, params.get("screendump_temp_dir")) try: os.makedirs(temp_dir) except OSError: pass temp_filename = os.path.join(temp_dir, "scrdump-%s.ppm" % kvm_utils.generate_random_string(6)) delay = float(params.get("screendump_delay", 5)) quality = int(params.get("screendump_quality", 30)) cache = {} while True: for vm in env.get_all_vms(): if not vm.is_alive(): continue try: vm.monitor.screendump(temp_filename) except kvm_monitor.MonitorError, e: logging.warn(e) continue if not os.path.exists(temp_filename): logging.warn("VM '%s' failed to produce a screendump", vm.name) continue if not ppm_utils.image_verify_ppm_file(temp_filename): logging.warn("VM '%s' produced an invalid screendump", vm.name) os.unlink(temp_filename) continue screendump_dir = os.path.join(test.debugdir, "screendumps_%s" % vm.name) try: os.makedirs(screendump_dir) except OSError: pass screendump_filename = os.path.join(screendump_dir, "%s_%s.jpg" % (vm.name, time.strftime("%Y-%m-%d_%H-%M-%S"))) hash = utils.hash_file(temp_filename) if hash in cache: try: os.link(cache[hash], screendump_filename) except OSError: pass else: try: image = PIL.Image.open(temp_filename) image.save(screendump_filename, format="JPEG", quality=quality) cache[hash] = screendump_filename except NameError: pass os.unlink(temp_filename) if _screendump_thread_termination_event.isSet(): _screendump_thread_termination_event = None break _screendump_thread_termination_event.wait(delay)
def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600): """ Transfer files from the guest. @param local_path: Guest path @param remote_path: Host path @param nic_index: The index of the NIC to connect to. @param timeout: Time (seconds) before giving up on doing the remote copy. """ username = self.params.get("username", "") password = self.params.get("password", "") client = self.params.get("file_transfer_client") address = self.get_address(nic_index) port = self.get_port(int(self.params.get("file_transfer_port"))) if not address or not port: logging.debug("IP address or port unavailable") return None if client == "scp": log_filename = ("scp-%s-%s.log" % (self.name, kvm_utils.generate_random_string(4))) return kvm_utils.scp_from_remote(address, port, username, password, remote_path, local_path, log_filename, timeout) elif client == "rss": c = rss_file_transfer.FileDownloadClient(address, port) c.download(remote_path, local_path, timeout) c.close() return True
def remote_login(self, nic_index=0, timeout=10): """ Log into the guest via SSH/Telnet/Netcat. If timeout expires while waiting for output from the guest (e.g. a password prompt or a shell prompt) -- fail. @param nic_index: The index of the NIC to connect to. @param timeout: Time (seconds) before giving up logging into the guest. @return: ShellSession object on success and None on failure. """ username = self.params.get("username", "") password = self.params.get("password", "") prompt = self.params.get("shell_prompt", "[\#\$]") linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) client = self.params.get("shell_client") address = self.get_address(nic_index) port = self.get_port(int(self.params.get("shell_port"))) log_filename = ("session-%s-%s.log" % (self.name, kvm_utils.generate_random_string(4))) if not address or not port: logging.debug("IP address or port unavailable") return None session = kvm_utils.remote_login(client, address, port, username, password, prompt, linesep, log_filename, timeout) if session: session.set_status_test_command(self.params.get("status_test_" "command", "")) return session
def __init__(self, name, params, qemu_path, image_dir, iso_dir): """ Initialize the object and set a few attributes. @param name: The name of the object @param params: A dict containing VM params (see method make_qemu_command for a full description) @param qemu_path: The path of the qemu binary @param image_dir: The directory where images reside @param iso_dir: The directory where ISOs reside """ self.process = None self.redirs = {} self.vnc_port = 5900 self.uuid = None self.name = name self.params = params self.qemu_path = qemu_path self.image_dir = image_dir self.iso_dir = iso_dir # Find available monitor filename while True: # The monitor filename should be unique self.instance = (time.strftime("%Y%m%d-%H%M%S-") + kvm_utils.generate_random_string(4)) self.monitor_file_name = os.path.join("/tmp", "monitor-" + self.instance) if not os.path.exists(self.monitor_file_name): break
def cmd(self, cmd, args=None, timeout=20): """ Send a QMP monitor command and return the response. Note: an id is automatically assigned to the command and the response is checked for the presence of the same id. @param cmd: Command to send @param args: A dict containing command arguments, or None @param timeout: Time duration to wait for response @return: The response received @raise MonitorLockError: Raised if the lock cannot be acquired @raise MonitorSocketError: Raised if a socket error occurs @raise MonitorProtocolError: Raised if no response is received @raise QMPCmdError: Raised if the response is an error message (the exception's args are (cmd, args, data) where data is the error data) """ if not self._acquire_lock(20): raise MonitorLockError("Could not acquire exclusive lock to send " "QMP command '%s'" % cmd) try: # Read any data that might be available self._read_objects() # Send command id = kvm_utils.generate_random_string(8) self._send(json.dumps(self._build_cmd(cmd, args, id)) + "\n") # Read response r = self._get_response(id, timeout) if r is None: raise MonitorProtocolError("Received no response to QMP " "command '%s', or received a " "response with an incorrect id" % cmd) if "return" in r: return r["return"] if "error" in r: raise QMPCmdError(cmd, args, r["error"]) finally: self._lock.release()
def run_file_transfer(test, params, env): """ Test ethrnet device function by ethtool 1) Boot up a VM. 2) Create a large file by dd on host. 3) Copy this file from host to guest. 4) Copy this file from guest to host. 5) Check if file transfers ended good. @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() login_timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=login_timeout) dir_name = test.tmpdir transfer_timeout = int(params.get("transfer_timeout")) transfer_type = params.get("transfer_type") tmp_dir = params.get("tmp_dir", "/tmp/") clean_cmd = params.get("clean_cmd", "rm -f") filesize = int(params.get("filesize", 4000)) count = int(filesize / 10) if count == 0: count = 1 host_path = os.path.join(dir_name, "tmp-%s" % kvm_utils.generate_random_string(8)) host_path2 = host_path + ".2" cmd = "dd if=/dev/zero of=%s bs=10M count=%d" % (host_path, count) guest_path = tmp_dir + "file_transfer-%s" % kvm_utils.generate_random_string(8) try: logging.info("Creating %dMB file on host", filesize) utils.run(cmd) if transfer_type == "remote": logging.info("Transfering file host -> guest, timeout: %ss", transfer_timeout) t_begin = time.time() vm.copy_files_to(host_path, guest_path, timeout=transfer_timeout) t_end = time.time() throughput = filesize / (t_end - t_begin) logging.info("File transfer host -> guest succeed, " "estimated throughput: %.2fMB/s", throughput) logging.info("Transfering file guest -> host, timeout: %ss", transfer_timeout) t_begin = time.time() vm.copy_files_from(guest_path, host_path2, timeout=transfer_timeout) t_end = time.time() throughput = filesize / (t_end - t_begin) logging.info("File transfer guest -> host succeed, " "estimated throughput: %.2fMB/s", throughput) else: raise error.TestError("Unknown test file transfer mode %s" % transfer_type) if utils.hash_file(host_path, method="md5") != utils.hash_file(host_path2, method="md5"): raise error.TestFail("File changed after transfer host -> guest " "and guest -> host") finally: logging.info("Cleaning temp file on guest") session.cmd("rm -rf %s" % guest_path) logging.info("Cleaning temp files on host") try: os.remove(host_path) os.remove(host_path2) except OSError: pass session.close()
def run_whql_client_install(test, params, env): """ WHQL DTM client installation: 1) Log into the guest (the client machine) and into a DTM server machine 2) Stop the DTM client service (wttsvc) on the client machine 3) Delete the client machine from the server's data store 4) Rename the client machine (give it a randomly generated name) 5) Move the client machine into the server's workgroup 6) Reboot the client machine 7) Install the DTM client software 8) Setup auto logon for the user created by the installation (normally DTMLLUAdminUser) 9) Reboot again @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))) # Collect test params server_address = params.get("server_address") server_shell_port = int(params.get("server_shell_port")) server_file_transfer_port = int(params.get("server_file_transfer_port")) server_studio_path = params.get("server_studio_path", "%programfiles%\\ " "Microsoft Driver Test Manager\\Studio") server_username = params.get("server_username") server_password = params.get("server_password") client_username = params.get("client_username") client_password = params.get("client_password") dsso_delete_machine_binary = params.get("dsso_delete_machine_binary", "deps/whql_delete_machine_15.exe") dsso_delete_machine_binary = kvm_utils.get_path(test.bindir, dsso_delete_machine_binary) install_timeout = float(params.get("install_timeout", 600)) install_cmd = params.get("install_cmd") wtt_services = params.get("wtt_services") # Stop WTT service(s) on client for svc in wtt_services.split(): kvm_test_utils.stop_windows_service(session, svc) # Copy dsso_delete_machine_binary to server rss_file_transfer.upload(server_address, server_file_transfer_port, dsso_delete_machine_binary, server_studio_path, timeout=60) # Open a shell session with server server_session = kvm_utils.remote_login("nc", server_address, server_shell_port, "", "", session.prompt, session.linesep) server_session.set_status_test_command(session.status_test_command) # Get server and client information cmd = "echo %computername%" server_name = server_session.cmd_output(cmd).strip() client_name = session.cmd_output(cmd).strip() cmd = "wmic computersystem get domain" server_workgroup = server_session.cmd_output(cmd).strip() server_workgroup = server_workgroup.splitlines()[-1] regkey = r"HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" cmd = "reg query %s /v Domain" % regkey o = server_session.cmd_output(cmd).strip().splitlines()[-1] try: server_dns_suffix = o.split(None, 2)[2] except IndexError: server_dns_suffix = "" # Delete the client machine from the server's data store (if it's there) server_session.cmd("cd %s" % server_studio_path) cmd = "%s %s %s" % (os.path.basename(dsso_delete_machine_binary), server_name, client_name) server_session.cmd(cmd, print_func=logging.info) server_session.close() # Rename the client machine client_name = "autotest_%s" % kvm_utils.generate_random_string(4) logging.info("Renaming client machine to '%s'", client_name) cmd = ('wmic computersystem where name="%%computername%%" rename name="%s"' % client_name) session.cmd(cmd, timeout=600) # Join the server's workgroup logging.info("Joining workgroup '%s'", server_workgroup) cmd = ('wmic computersystem where name="%%computername%%" call ' 'joindomainorworkgroup name="%s"' % server_workgroup) session.cmd(cmd, timeout=600) # Set the client machine's DNS suffix logging.info("Setting DNS suffix to '%s'", server_dns_suffix) cmd = 'reg add %s /v Domain /d "%s" /f' % (regkey, server_dns_suffix) session.cmd(cmd, timeout=300) # Reboot session = vm.reboot(session) # Access shared resources on the server machine logging.info("Attempting to access remote share on server") cmd = r"net use \\%s /user:%s %s" % (server_name, server_username, server_password) end_time = time.time() + 120 while time.time() < end_time: try: session.cmd(cmd) break except: pass time.sleep(5) else: raise error.TestError("Could not access server share from client " "machine") # Install logging.info("Installing DTM client (timeout=%ds)", install_timeout) install_cmd = r"cmd /c \\%s\%s" % (server_name, install_cmd.lstrip("\\")) session.cmd(install_cmd, timeout=install_timeout) # Setup auto logon logging.info("Setting up auto logon for user '%s'", client_username) cmd = ('reg add ' '"HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\winlogon" ' '/v "%s" /d "%s" /t REG_SZ /f') session.cmd(cmd % ("AutoAdminLogon", "1")) session.cmd(cmd % ("DefaultUserName", client_username)) session.cmd(cmd % ("DefaultPassword", client_password)) # Reboot one more time session = vm.reboot(session) session.close()
def run_migration_with_file_transfer(test, params, env): """ KVM migration test: 1) Get a live VM and clone it. 2) Verify that the source VM supports migration. If it does, proceed with the test. 3) Transfer file from host to guest. 4) Repeatedly migrate VM and wait until transfer's finished. 5) Transfer file from guest back to host. 6) Repeatedly migrate VM and wait until transfer's finished. @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ vm = env.get_vm(params["main_vm"]) vm.verify_alive() login_timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=login_timeout) mig_timeout = float(params.get("mig_timeout", "3600")) mig_protocol = params.get("migration_protocol", "tcp") mig_cancel_delay = int(params.get("mig_cancel") == "yes") * 2 host_path = "/tmp/file-%s" % kvm_utils.generate_random_string(6) host_path_returned = "%s-returned" % host_path guest_path = params.get("guest_path", "/tmp/file") file_size = params.get("file_size", "500") transfer_timeout = int(params.get("transfer_timeout", "240")) try: utils.run("dd if=/dev/urandom of=%s bs=1M count=%s" % (host_path, file_size)) def run_and_migrate(bg): bg.start() try: while bg.isAlive(): logging.info( "File transfer not ended, starting a round of " "migration...") vm.migrate(mig_timeout, mig_protocol, mig_cancel_delay) except: # If something bad happened in the main thread, ignore # exceptions raised in the background thread bg.join(suppress_exception=True) raise else: bg.join() error.context("transferring file to guest while migrating", logging.info) bg = kvm_utils.Thread(vm.copy_files_to, (host_path, guest_path), dict(verbose=True, timeout=transfer_timeout)) run_and_migrate(bg) error.context("transferring file back to host while migrating", logging.info) bg = kvm_utils.Thread(vm.copy_files_from, (guest_path, host_path_returned), dict(verbose=True, timeout=transfer_timeout)) run_and_migrate(bg) # Make sure the returned file is identical to the original one error.context("comparing hashes", logging.info) orig_hash = client_utils.hash_file(host_path) returned_hash = client_utils.hash_file(host_path_returned) if orig_hash != returned_hash: raise error.TestFail("Returned file hash (%s) differs from " "original one (%s)" % (returned_hash, orig_hash)) error.context() finally: session.close() if os.path.isfile(host_path): os.remove(host_path) if os.path.isfile(host_path_returned): os.remove(host_path_returned)
def run_migration_with_file_transfer(test, params, env): """ KVM migration test: 1) Get a live VM and clone it. 2) Verify that the source VM supports migration. If it does, proceed with the test. 3) Transfer file from host to guest. 4) Repeatedly migrate VM and wait until transfer's finished. 5) Transfer file from guest back to host. 6) Repeatedly migrate VM and wait until transfer's finished. @param test: kvm test object. @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ vm = env.get_vm(params["main_vm"]) vm.verify_alive() login_timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=login_timeout) mig_timeout = float(params.get("mig_timeout", "3600")) mig_protocol = params.get("migration_protocol", "tcp") mig_cancel_delay = int(params.get("mig_cancel") == "yes") * 2 host_path = "/tmp/file-%s" % kvm_utils.generate_random_string(6) host_path_returned = "%s-returned" % host_path guest_path = params.get("guest_path", "/tmp/file") file_size = params.get("file_size", "500") transfer_timeout = int(params.get("transfer_timeout", "240")) try: utils.run("dd if=/dev/urandom of=%s bs=1M count=%s" % (host_path, file_size)) def run_and_migrate(bg): bg.start() try: while bg.isAlive(): logging.info("File transfer not ended, starting a round of " "migration...") vm.migrate(mig_timeout, mig_protocol, mig_cancel_delay) except: # If something bad happened in the main thread, ignore # exceptions raised in the background thread bg.join(suppress_exception=True) raise else: bg.join() error.context("transferring file to guest while migrating", logging.info) bg = kvm_utils.Thread(vm.copy_files_to, (host_path, guest_path), dict(verbose=True, timeout=transfer_timeout)) run_and_migrate(bg) error.context("transferring file back to host while migrating", logging.info) bg = kvm_utils.Thread(vm.copy_files_from, (guest_path, host_path_returned), dict(verbose=True, timeout=transfer_timeout)) run_and_migrate(bg) # Make sure the returned file is identical to the original one error.context("comparing hashes", logging.info) orig_hash = client_utils.hash_file(host_path) returned_hash = client_utils.hash_file(host_path_returned) if orig_hash != returned_hash: raise error.TestFail("Returned file hash (%s) differs from " "original one (%s)" % (returned_hash, orig_hash)) error.context() finally: session.close() if os.path.isfile(host_path): os.remove(host_path) if os.path.isfile(host_path_returned): os.remove(host_path_returned)
def __init__(self, command=None, id=None, auto_close=False, echo=False, linesep="\n"): """ Initialize the class and run command as a child process. @param command: Command to run, or None if accessing an already running server. @param id: ID of an already running server, if accessing a running server, or None if starting a new one. @param auto_close: If True, close() the instance automatically when its reference count drops to zero (default False). @param echo: Boolean indicating whether echo should be initially enabled for the pseudo terminal running the subprocess. This parameter has an effect only when starting a new server. @param linesep: Line separator to be appended to strings sent to the child process by sendline(). """ self.id = id or kvm_utils.generate_random_string(8) # Define filenames for communication with server base_dir = "/tmp/kvm_spawn" try: os.makedirs(base_dir) except: pass (self.shell_pid_filename, self.status_filename, self.output_filename, self.inpipe_filename, self.lock_server_running_filename, self.lock_client_starting_filename) = _get_filenames(base_dir, self.id) # Remember some attributes self.auto_close = auto_close self.echo = echo self.linesep = linesep # Make sure the 'readers' and 'close_hooks' attributes exist if not hasattr(self, "readers"): self.readers = [] if not hasattr(self, "close_hooks"): self.close_hooks = [] # Define the reader filenames self.reader_filenames = dict( (reader, _get_reader_filename(base_dir, self.id, reader)) for reader in self.readers) # Let the server know a client intends to open some pipes; # if the executed command terminates quickly, the server will wait for # the client to release the lock before exiting lock_client_starting = _lock(self.lock_client_starting_filename) # Start the server (which runs the command) if command: sub = subprocess.Popen("%s %s" % (sys.executable, __file__), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Send parameters to the server sub.stdin.write("%s\n" % self.id) sub.stdin.write("%s\n" % echo) sub.stdin.write("%s\n" % ",".join(self.readers)) sub.stdin.write("%s\n" % command) # Wait for the server to complete its initialization while not "Server %s ready" % self.id in sub.stdout.readline(): pass # Open the reading pipes self.reader_fds = {} try: assert(_locked(self.lock_server_running_filename)) for reader, filename in self.reader_filenames.items(): self.reader_fds[reader] = os.open(filename, os.O_RDONLY) except: pass # Allow the server to continue _unlock(lock_client_starting)
def run_file_transfer(test, params, env): """ Test ethrnet device function by ethtool 1) Boot up a VM. 2) Create a large file by dd on host. 3) Copy this file from host to guest. 4) Copy this file from guest to host. 5) Check if file transfers ended good. @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() login_timeout = int(params.get("login_timeout", 360)) session = vm.wait_for_login(timeout=login_timeout) dir_name = test.tmpdir transfer_timeout = int(params.get("transfer_timeout")) transfer_type = params.get("transfer_type") tmp_dir = params.get("tmp_dir", "/tmp/") clean_cmd = params.get("clean_cmd", "rm -f") filesize = int(params.get("filesize", 4000)) count = int(filesize / 10) if count == 0: count = 1 host_path = os.path.join(dir_name, "tmp-%s" % kvm_utils.generate_random_string(8)) host_path2 = host_path + ".2" cmd = "dd if=/dev/zero of=%s bs=10M count=%d" % (host_path, count) guest_path = (tmp_dir + "file_transfer-%s" % kvm_utils.generate_random_string(8)) try: logging.info("Creating %dMB file on host", filesize) utils.run(cmd) if transfer_type == "remote": logging.info("Transfering file host -> guest, timeout: %ss", transfer_timeout) t_begin = time.time() vm.copy_files_to(host_path, guest_path, timeout=transfer_timeout) t_end = time.time() throughput = filesize / (t_end - t_begin) logging.info( "File transfer host -> guest succeed, " "estimated throughput: %.2fMB/s", throughput) logging.info("Transfering file guest -> host, timeout: %ss", transfer_timeout) t_begin = time.time() vm.copy_files_from(guest_path, host_path2, timeout=transfer_timeout) t_end = time.time() throughput = filesize / (t_end - t_begin) logging.info( "File transfer guest -> host succeed, " "estimated throughput: %.2fMB/s", throughput) else: raise error.TestError("Unknown test file transfer mode %s" % transfer_type) if (utils.hash_file(host_path, method="md5") != utils.hash_file( host_path2, method="md5")): raise error.TestFail("File changed after transfer host -> guest " "and guest -> host") finally: logging.info('Cleaning temp file on guest') session.cmd("rm -rf %s" % guest_path) logging.info('Cleaning temp files on host') try: os.remove(host_path) os.remove(host_path2) except OSError: pass session.close()
def _take_screendumps(test, params, env): global _screendump_thread_termination_event temp_dir = test.debugdir if params.get("screendump_temp_dir"): temp_dir = kvm_utils.get_path(test.bindir, params.get("screendump_temp_dir")) try: os.makedirs(temp_dir) except OSError: pass temp_filename = os.path.join( temp_dir, "scrdump-%s.ppm" % kvm_utils.generate_random_string(6)) delay = float(params.get("screendump_delay", 5)) quality = int(params.get("screendump_quality", 30)) cache = {} while True: for vm in env.get_all_vms(): if not vm.is_alive(): continue try: vm.monitor.screendump(temp_filename) except kvm_monitor.MonitorError, e: logging.warn(e) continue if not os.path.exists(temp_filename): logging.warn("VM '%s' failed to produce a screendump", vm.name) continue if not ppm_utils.image_verify_ppm_file(temp_filename): logging.warn("VM '%s' produced an invalid screendump", vm.name) os.unlink(temp_filename) continue screendump_dir = os.path.join(test.debugdir, "screendumps_%s" % vm.name) try: os.makedirs(screendump_dir) except OSError: pass screendump_filename = os.path.join( screendump_dir, "%s_%s.jpg" % (vm.name, time.strftime("%Y-%m-%d_%H-%M-%S"))) hash = utils.hash_file(temp_filename) if hash in cache: try: os.link(cache[hash], screendump_filename) except OSError: pass else: try: image = PIL.Image.open(temp_filename) image.save(screendump_filename, format="JPEG", quality=quality) cache[hash] = screendump_filename except NameError: pass os.unlink(temp_filename) if _screendump_thread_termination_event.isSet(): _screendump_thread_termination_event = None break _screendump_thread_termination_event.wait(delay)