Screendumps are taken regularly and converted to JPEG format. They are stored in .../debug/screendumps_<VMname>/. Requires python-imaging. - Enabled by 'take_regular_screendumps = yes' (naming suggestions welcome). - Delay between screendumps is controlled by 'screendump_delay' (default 5). - Compression quality is controlled by 'screendump_quality' (default 30). - It's probably a good idea to dump them to /dev/shm before converting them in order to minimize disk use. This can be enabled by 'screendump_temp_dir = /dev/shm' (enabled by default.) - Screendumps are removed unless 'keep_screendumps'['_on_error'] is 'yes'. The recommended setting when submitting jobs from autoserv is 'keep_screendumps_on_error = yes', which means screendumps are kept only if the test fails. Keeping all screendumps may use up all of the server's storage space. This patch sets reasonable defaults in tests_base.cfg.sample. (It also makes sure post_command is executed last in the postprocessing procedure -- otherwise post_command failure can prevent other postprocessing steps (like removing the screendump dirs) from taking place.) Changes from v2: - When encountering a screendump that has already been stored, create a hard link to the existing one instead of a new JPG file. - By default, use /dev/shm as temporary storage for screendumps. - Use yyyy-mm-dd_hh-mm-ss timestamps in screendump filenames (instead of yyyymmdd-hhmmss). Changes from v1: Print debug messages when starting or terminating the screendump thread. Signed-off-by: Michael Goldish <mgoldish@xxxxxxxxxx> --- client/tests/kvm/kvm_preprocessing.py | 98 ++++++++++++++++++++++++++++++-- client/tests/kvm/tests_base.cfg.sample | 13 +++- 2 files changed, 102 insertions(+), 9 deletions(-) diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py index a92282a..b3fef9d 100644 --- a/client/tests/kvm/kvm_preprocessing.py +++ b/client/tests/kvm/kvm_preprocessing.py @@ -1,4 +1,4 @@ -import sys, os, time, commands, re, logging, signal, glob +import sys, os, time, commands, re, logging, signal, glob, threading, shutil from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error import kvm_vm, kvm_utils, kvm_subprocess, ppm_utils @@ -11,6 +11,10 @@ except ImportError: 'distro.') +_screendump_thread = None +_screendump_thread_termination_event = None + + def preprocess_image(test, params): """ Preprocess a single QEMU image according to the instructions in params. @@ -254,6 +258,15 @@ def preprocess(test, params, env): # 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 postprocess(test, params, env): """ @@ -263,8 +276,16 @@ def postprocess(test, params, env): @param params: Dict containing all VM and image parameters. @param env: The environment (a dict-like object). """ + # Postprocess all VMs and images process(test, params, env, postprocess_image, postprocess_vm) + # Terminate the screendump thread + global _screendump_thread, _screendump_thread_termination_event + if _screendump_thread: + logging.debug("Terminating screendump thread...") + _screendump_thread_termination_event.set() + _screendump_thread.join(10) + # Warn about corrupt PPM files for f in glob.glob(os.path.join(test.debugdir, "*.ppm")): if not ppm_utils.image_verify_ppm_file(f): @@ -290,11 +311,13 @@ def postprocess(test, params, env): for f in glob.glob(os.path.join(test.debugdir, '*.ppm')): os.unlink(f) - # Execute any post_commands - if params.get("post_command"): - process_command(test, params, env, params.get("post_command"), - int(params.get("post_command_timeout", "600")), - params.get("post_command_noncritical") == "yes") + # Should we keep the screendump dirs? + if params.get("keep_screendumps") != "yes": + logging.debug("'keep_screendumps' not specified; removing screendump " + "dirs...") + for d in glob.glob(os.path.join(test.debugdir, "screendumps_*")): + if os.path.isdir(d) and not os.path.islink(d): + shutil.rmtree(d, ignore_errors=True) # Kill all unresponsive VMs if params.get("kill_unresponsive_vms") == "yes": @@ -318,6 +341,12 @@ def postprocess(test, params, env): env["tcpdump"].close() del env["tcpdump"] + # Execute any post_commands + if params.get("post_command"): + process_command(test, params, env, params.get("post_command"), + int(params.get("post_command_timeout", "600")), + params.get("post_command_noncritical") == "yes") + def postprocess_on_error(test, params, env): """ @@ -343,3 +372,60 @@ def _update_address_cache(address_cache, line): mac_address, address_cache.get("last_seen")) address_cache[mac_address] = address_cache.get("last_seen") del address_cache["last_seen"] + + +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 kvm_utils.env_get_all_vms(env): + if vm.is_dead(): + continue + vm.send_monitor_cmd("screendump %s" % temp_filename) + 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(): + break + _screendump_thread_termination_event.wait(delay) diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample index b13aec4..2a36687 100644 --- a/client/tests/kvm/tests_base.cfg.sample +++ b/client/tests/kvm/tests_base.cfg.sample @@ -10,13 +10,20 @@ main_vm = vm1 # Some preprocessor/postprocessor params start_vm = yes -convert_ppm_files_to_png_on_error = yes -#keep_ppm_files = yes -#keep_ppm_files_on_error = yes kill_vm = no kill_vm_gracefully = yes kill_unresponsive_vms = yes +# Screendump specific stuff +convert_ppm_files_to_png_on_error = yes +#keep_ppm_files = yes +#keep_ppm_files_on_error = yes +take_regular_screendumps = yes +keep_screendumps_on_error = yes +screendump_delay = 5 +screendump_quality = 30 +screendump_temp_dir = /dev/shm + # Some default VM params qemu_binary = qemu qemu_img_binary = qemu-img -- 1.5.4.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html