def boot(self, timeout = None, multiboot = True, kernel_args = "booted with vmrunner"): # Check for sudo access, needed for tap network devices and the KVM module if os.getuid() is not 0: print color.FAIL("Call the script with sudo access") sys.exit(1) # Start the timeout thread if (timeout): self._timer = threading.Timer(timeout, self._on_timeout) self._timer.start() # Boot via hypervisor try: self._hyper.boot(multiboot, kernel_args + "/" + self._hyper.name()) except Exception as err: print color.WARNING("Exception raised while booting ") if (timeout): self._timer.cancel() raise err # Start analyzing output while self._hyper.poll() == None and not self._exit_status: line = self._hyper.readline() print color.SUBPROC("<VM> "+line.rstrip()) # Look for event-triggers for pattern, func in self._on_output.iteritems(): if re.search(pattern, line): try: res = func() except Exception as err: print color.WARNING("Exception raised in event callback: ") print_exception() res = False self.stop().wait() #NOTE: It can be 'None' without problem if res == False: self._exit_status = exit_codes["OUTSIDE_FAIL"] self.exit(self._exit_status, color.FAIL(nametag + " VM-external test failed")) # Now we either have an exit status from timer thread, or an exit status # from the subprocess, or the VM was powered off by the external test. # If the process didn't exit we need to stop it. if (self.poll() == None): self.stop() self.wait() if self._exit_status: print color.WARNING(nametag + "Found non-zero exit status but process didn't end. ") print color.FAIL(nametag + "Tests failed or program error") print color.INFO(nametag),"Done running VM. Exit status: ", self._exit_status sys.exit(self._exit_status) else: print color.SUCCESS(nametag + " VM exited with 0 exit status.") print color.INFO(nametag), "Subprocess finished. Exiting with ", self._hyper.poll() sys.exit(self._hyper.poll()) raise Exception("Unexpected termination")
def handler(signum, frame): print color.WARNING("Process interrupted - stopping vms") for vm in vms: try: vm.exit(exit_codes["ABORT"], "Process terminated by user") except Exception as e: print color.WARNING("Forced shutdown caused exception: "), e raise e
def integration_tests(): """ Loops over all valid tests as defined by ./validate_all.py. Runs them one by one and gives an update of the statuses at the end. """ global test_count valid = valid_tests() if not valid: print pretty.WARNING("Integration tests skipped") return 0 test_count += len(valid) print pretty.HEADER("Starting " + str(len(valid)) + " integration test(s)") processes = [] fail_count = 0 for path in valid: processes.append(Test(path, clean = args.clean).start()) # Collect test results print pretty.HEADER("Collecting integration test results") for p in processes: fail_count += 1 if p.wait_status() else 0 # Exit early if any tests failed if fail_count and args.fail: print pretty.FAIL(str(fail_count) + "integration tests failed") sys.exit(fail_count) return fail_count
def load_configs(config_path="."): global vms # Clear out the default unconfigured vm if (not vms[0]._config): vms = [] print color.HEADER("IncludeOS vmrunner loading VM configs") schema_path = package_path + "/vm.schema.json" print INFO, "Validating JSON according to schema ", schema_path validate_vm.load_schema(schema_path) validate_vm.load_configs(config_path) if validate_vm.valid_vms: print INFO, "Loaded VM specification(s) from JSON" for spec in validate_vm.valid_vms: print INFO, "Found VM spec: " print color.DATA(spec.__str__()) vms.append(vm(spec)) else: print color.WARNING( nametag), "No VM specification JSON found, trying default config" vms.append(vm(default_config)) return vms
def start_process(self, cmdlist): if cmdlist[0] == "sudo": # and have_sudo(): print color.WARNING("Running with sudo") self._sudo = True # Start a subprocess self._proc = subprocess.Popen(cmdlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) return self._proc
def unit_tests(): """Perform unit tests""" global test_count test_count += 1 if ("unit" in args.skip): print pretty.WARNING("Unit tests skipped") return 0 print pretty.HEADER("Building and running unit tests") build_status = Test(".", name="Unit test build", command=["make"], clean = args.clean).start().wait_status() unit_status = Test(".", name="Unit tests", command = ["./test.lest"]).start().wait_status() if (build_status or unit_status) and args.fail: print pretty.FAIL("Unit tests failed") sys.exit(max(build_status, unit_status)) return max(build_status, unit_status)
def trigger_event(self, line): # Find any callback triggered by this line for pattern, func in self._on_output.iteritems(): if re.search(pattern, line): try: # Call it res = func(line) except Exception as err: print color.WARNING("Exception raised in event callback: ") print_exception() res = False self.stop() # NOTE: Result can be 'None' without problem if res == False: self._exit_status = exit_codes["CALLBACK_FAILED"] self.exit(self._exit_status, " Event-triggered test failed")
def valid_tests(verb=False): tests = [] dirs = os.walk('.').next()[1] for directory in dirs: subdirs = os.walk(directory).next()[1] if "integration" in subdirs: subdirs = os.walk(directory + "/integration").next()[1] if subdirs: for d in subdirs: path = directory + "/integration/" + d if validate_test.validate_path(path, verb): tests.append(path) else: print color.WARNING("Validator: " + path + " failed validation") return tests
def examples_working(): global test_count if ("examples" in args.skip): print pretty.WARNING("Examples test skipped") return 0 examples_dir = '../examples' dirs = os.walk(examples_dir).next()[1] print pretty.HEADER("Building " + str(len(dirs)) + " examples") test_count += len(dirs) fail_count = 0 for directory in dirs: example = examples_dir + "/" + directory print "Building Example ", example build = Test(example, command = ["make"], name = directory + " build").start().wait_status() run = 0 #TODO: Make a 'test' folder for each example, containing test.py, vm.json etc. fail_count += 1 if build or run else 0 return fail_count
def stress_test(): """Perform stresstest""" global test_count test_count += 1 if ("stress" in args.skip): print pretty.WARNING("Stress test skipped") return 0 if (not validate_test.validate_path("stress")): raise Exception("Stress test failed validation") print pretty.HEADER("Starting stress test") stress = Test("stress", clean = args.clean).start() if (stress and args.fail): print pretty.FAIL("Stress test failed") sys.exit(stress) return 1 if stress.wait_status() else 0
def misc_working(): global test_count if ("misc" in args.skip): print pretty.WARNING("Misc test skipped") return 0 misc_dir = 'misc' dirs = os.walk(misc_dir).next()[1] dirs.sort() print pretty.HEADER("Building " + str(len(dirs)) + " misc") test_count += len(dirs) fail_count = 0 for directory in dirs: misc = misc_dir + "/" + directory print "Building misc ", misc build = Test(misc, command=['./test.sh'], name=directory).start().wait_status() run = 0 #TODO: Make a 'test' folder for each miscellanous test, containing test.py, vm.json etc. fail_count += 1 if build or run else 0 return fail_count
import re import linecache import traceback import validate_vm import signal import psutil from shutil import copyfile from prettify import color INCLUDEOS_HOME = None if "INCLUDEOS_PREFIX" not in os.environ: def_home = "/usr/local" print color.WARNING( "WARNING:" ), "Environment variable INCLUDEOS_PREFIX is not set. Trying default", def_home if not os.path.isdir(def_home): raise Exception("Couldn't find INCLUDEOS_PREFIX") INCLUDEOS_HOME = def_home else: INCLUDEOS_HOME = os.environ['INCLUDEOS_PREFIX'] package_path = os.path.dirname(os.path.realpath(__file__)) default_config = INCLUDEOS_HOME + "/includeos/vmrunner/vm.default.json" default_json = "./vm.json" chainloader = INCLUDEOS_HOME + "/includeos/chainloader"
def boot(self, timeout=60, multiboot=True, kernel_args="booted with vmrunner", image_name=None): # This might be a reboot self._exit_status = None self._timeout_after = timeout # Start the timeout thread if (timeout): info("setting timeout to", timeout, "seconds") self._timer = threading.Timer(timeout, self._on_timeout) self._timer.start() # Boot via hypervisor try: self._hyper.boot(multiboot, kernel_args, image_name) except Exception as err: print color.WARNING("Exception raised while booting: ") print_exception() if (timeout): self._timer.cancel() self.exit(exit_codes["BOOT_FAILED"], str(err)) # Start analyzing output while self._hyper.poll() == None and not self._exit_status: try: line = self._hyper.readline() except Exception as e: print color.WARNING( "Exception thrown while waiting for vm output") break if line: # Special case for end-of-transmission if line == EOT: if not self._exit_status: self._exit_status = exit_codes["VM_EOT"] break if line.startswith( " [ Kernel ] service exited with status"): self._exit_status = int(line.split(" ")[-1].rstrip()) self._exit_msg = "Service exited" break else: print color.VM(line.rstrip()) else: pass # TODO: Add event-trigger for EOF? for pattern, func in self._on_output.iteritems(): if re.search(pattern, line): try: res = func(line) except Exception as err: print color.WARNING( "Exception raised in event callback: ") print_exception() res = False self.stop() # NOTE: It can be 'None' without problem if res == False: self._exit_status = exit_codes["CALLBACK_FAILED"] self.exit(self._exit_status, " Event-triggered test failed") # If the VM process didn't exit by now we need to stop it. if (self.poll() == None): self.stop() # We might have an exit status, e.g. set by a callback noticing something wrong with VM output if self._exit_status: self.exit(self._exit_status, self._exit_msg) # Process might have ended prematurely elif self.poll(): self.exit(self._hyper.poll(), self._hyper.get_error_messages()) # If everything went well we can return return self
def handler(signum, frame): print color.WARNING("Process interrupted") thread.interrupt_main() thread.exit()
import time import re import linecache import traceback import validate_test import signal from prettify import color INCLUDEOS_HOME = None nametag = "<VMRunner>" if "INCLUDEOS_HOME" not in os.environ: print color.WARNING( "WARNING:" ), "Environment varialble INCLUDEOS_HOME is not set. Trying default" def_home = os.environ["HOME"] + "/IncludeOS_install" if not os.path.isdir(def_home): raise Exception("Couldn't find INCLUDEOS_HOME") INCLUDEOS_HOME = def_home else: INCLUDEOS_HOME = os.environ['INCLUDEOS_HOME'] # Exit codes used by this program exit_codes = { "SUCCESS": 0, "PROGRAM_FAILURE": 1, "TIMEOUT": 66, "VM_FAIL": 67, "OUTSIDE_FAIL": 68
def boot(self, timeout=60, multiboot=True, debug=False, kernel_args="booted with vmrunner", image_name=None): info("VM boot, timeout: ", timeout, "multiboot: ", multiboot, "Kernel_args: ", kernel_args, "image_name: ", image_name) # This might be a reboot self._exit_status = None self._exit_complete = False self._timeout_after = timeout # Start the timeout thread if (timeout): info("setting timeout to", timeout, "seconds") self._timer = threading.Timer(timeout, self._on_timeout) self._timer.start() # Boot via hypervisor try: self._hyper.boot(multiboot, debug, kernel_args, image_name) except Exception as err: print color.WARNING("Exception raised while booting: ") print_exception() if (timeout): self._timer.cancel() self.exit(exit_codes["BOOT_FAILED"], str(err)) # Start analyzing output while self._exit_status == None and self.poll() == None: try: line = self._hyper.readline() except Exception as e: print color.WARNING( "Exception thrown while waiting for vm output") break if line and self.find_exit_status(line) == None: print color.VM(line.rstrip()) self.trigger_event(line) # Empty line - should only happen if process exited else: pass # VM Done info("Event loop done. Exit status:", self._exit_status, "poll:", self.poll()) # If the VM process didn't exit by now we need to stop it. if (self.poll() == None): self.stop() # Process may have ended without EOT / exit message being read yet # possibly normal vm shutdown if self.poll() != None: info("No poll - getting final output") try: data, err = self._hyper.get_final_output() # Print stderr if exit status wasnt 0 if err and self.poll() != 0: print color.WARNING("Stderr: \n" + err) # Parse the last output from vm lines = data.split("\n") for line in lines: print color.VM(line) self.find_exit_status(line) # Note: keep going. Might find panic after service exit except Exception as e: pass # We should now have an exit status, either from a callback or VM EOT / exit msg. if self._exit_status != None: info("VM has exit status. Exiting.") self.exit(self._exit_status, self._exit_msg) else: self.exit(self._hyper.poll(), "process exited") # If everything went well we can return return self
def boot(self, multiboot, debug=False, kernel_args="", image_name=None): self._stopped = False info("Booting with multiboot:", multiboot, "kernel_args: ", kernel_args, "image_name:", image_name) # Resolve if kvm is present self._kvm_present = self.kvm_present() # Use provided image name if set, otherwise try to find it in json-config if not image_name: if not "image" in self._config: raise Exception( "No image name provided, neither as param or in config file" ) image_name = self._config["image"] self._image_name = image_name disk_args = [] debug_args = [] if debug: debug_args = ["-s"] # multiboot - e.g. boot with '-kernel' and no bootloader if multiboot: # TODO: Remove .img-extension from vm.json in tests to avoid this hack if (image_name.endswith(".img")): image_name = image_name.split(".")[0] if not kernel_args: kernel_args = "\"\"" info("File magic: ", file_type(image_name)) if is_Elf64(image_name): info("Found 64-bit ELF, need chainloader") print "Looking for chainloader: " print "Found", chainloader, "Type: ", file_type(chainloader) if not is_Elf32(chainloader): print color.WARNING( "Chainloader doesn't seem to be a 32-bit ELF executable" ) kernel_args = [ "-kernel", chainloader, "-append", kernel_args, "-initrd", image_name + " " + kernel_args ] elif is_Elf32(image_name): info("Found 32-bit elf, trying direct boot") kernel_args = ["-kernel", image_name, "-append", kernel_args] else: print color.WARNING( "Provided kernel is neither 64-bit or 32-bit ELF executable." ) kernel_args = ["-kernel", image_name, "-append", kernel_args] info("Booting", image_name, "directly without bootloader (multiboot / -kernel args)") else: kernel_args = [] image_in_config = False # If the provided image name is also defined in vm.json, use vm.json if "drives" in self._config: for disk in self._config["drives"]: if disk["file"] == image_name: image_in_config = True if not image_in_config: info("Provided image", image_name, "not found in config. Appending.") self._config["drives"].insert( 0, { "file": image_name, "type": "ide", "format": "raw", "media": "disk" }) else: self._config["drives"] = [{ "file": image_name, "type": "ide", "format": "raw", "media": "disk" }] info("Booting", image_name, "with a bootable disk image") if "drives" in self._config: for disk in self._config["drives"]: disk_args += self.drive_arg(disk["file"], disk["type"], disk["format"], disk["media"]) mod_args = [] if "modules" in self._config: mod_args += self.mod_args(self._config["modules"]) if "bios" in self._config: kernel_args.extend(["-bios", self._config["bios"]]) if "uuid" in self._config: kernel_args.extend(["--uuid", str(self._config["uuid"])]) if "smp" in self._config: kernel_args.extend(["-smp", str(self._config["smp"])]) if "cpu" in self._config: cpu = self._config["cpu"] cpu_str = cpu["model"] if "features" in cpu: cpu_str += ",+" + ",+".join(cpu["features"]) kernel_args.extend(["-cpu", cpu_str]) net_args = [] i = 0 if "net" in self._config: for net in self._config["net"]: mac = net["mac"] if "mac" in net else None bridge = net["bridge"] if "bridge" in net else None scripts = net["scripts"] if "scripts" in net else None net_args += self.net_arg(net["backend"], net["device"], "net" + str(i), mac, bridge, scripts) i += 1 mem_arg = [] if "mem" in self._config: mem_arg = ["-m", str(self._config["mem"])] vga_arg = ["-nographic"] if "vga" in self._config: vga_arg = ["-vga", str(self._config["vga"])] trace_arg = [] if "trace" in self._config: trace_arg = ["-trace", "events=" + str(self._config["trace"])] pci_arg = [] if "vfio" in self._config: pci_arg = ["-device", "vfio-pci,host=" + self._config["vfio"]] # custom qemu binary/location qemu_binary = "qemu-system-x86_64" if "qemu" in self._config: qemu_binary = self._config["qemu"] # TODO: sudo is only required for tap networking and kvm. Check for those. command = ["sudo", qemu_binary] if self._kvm_present: command.extend(["--enable-kvm"]) command += kernel_args command += disk_args + debug_args + net_args + mem_arg + mod_args command += vga_arg + trace_arg + pci_arg #command_str = " ".join(command) #command_str.encode('ascii','ignore') #command = command_str.split(" ") info("Command:", " ".join(command)) try: self.start_process(command) self.info("Started process PID ", self._proc.pid) except Exception as e: print self.INFO, "Starting subprocess threw exception:", e raise e
def print_skipped(name, reason): print pretty.WARNING("* Skipping " + name) print " Reason: {0:40}".format(reason)
def print_skipped(tests): for test in tests: if test.skip_: print pretty.WARNING("* Skipping " + test.name_) print " Reason: {0:40}".format(test.skip_reason_)
import re import linecache import traceback import validate_vm import signal import psutil from prettify import color INCLUDEOS_HOME = None nametag = "<VMRunner>" if "INCLUDEOS_PREFIX" not in os.environ: def_home = "/usr/local" print color.WARNING("WARNING:"), "Environment varialble INCLUDEOS_PREFIX is not set. Trying default", def_home if not os.path.isdir(def_home): raise Exception("Couldn't find INCLUDEOS_PREFIX") INCLUDEOS_HOME= def_home else: INCLUDEOS_HOME = os.environ['INCLUDEOS_PREFIX'] package_path = os.path.dirname(os.path.realpath(__file__)) # The end-of-transmission character EOT = chr(4) # Exit codes used by this program exit_codes = {"SUCCESS" : 0, "PROGRAM_FAILURE" : 1, "TIMEOUT" : 66, "VM_PANIC" : 67,