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 kvm_present(self): command = "egrep -m 1 '^flags.*(vmx|svm)' /proc/cpuinfo" try: subprocess.check_output(command, shell=True) print color.INFO("<qemu>"), "KVM ON" return True except Exception as err: print color.INFO("<qemu>"), "KVM OFF" return False
def timeout(self): if VERB: print color.INFO("<timeout>"), "VM timed out" # Note: we have to stop the VM since the main thread is blocking on vm.readline self._exit_status = exit_codes["TIMEOUT"] self._exit_msg = "vmrunner timed out after " + str(self._timeout_after) + " seconds" self._hyper.stop().wait()
def make(self, params = []): print color.INFO(nametag), "Building test service with 'make' (params=" + str(params) + ")" make = ["make"] make.extend(params) res = subprocess.check_output(make) print color.SUBPROC(res) return self
def cmake(self, args = []): print color.INFO(nametag), "Building with cmake (%s)" % args # install dir: INSTDIR = os.getcwd() # create build directory try: os.makedirs("build") except OSError as err: if err.errno!=17: # Errno 17: File exists self.exit(exit_codes["BUILD_FAIL"], "could not create build directory") # go into build directory # NOTE: The test gets run from here os.chdir("build") # build with prefix = original path cmake = ["cmake", "..", "-DCMAKE_INSTALL_PREFIX:PATH=" + INSTDIR] cmake.extend(args) try: cmd(cmake) # if everything went well, build with make and install return self.make() except Exception as e: print "Excetption while building: ", e self.exit(exit_codes["BUILD_FAIL"], "building with cmake failed")
def stop(self): if hasattr(self, "_proc") and self._proc.poll() == None : print color.INFO(self._nametag),"Stopping", self._config["image"], "PID",self._proc.pid # Kill subprocess.check_call(["kill", "-SIGTERM", str(self._proc.pid)]) # Wait for termination (avoids the need to reset the terminal) self._proc.wait() return self
def wait_status(self): self.print_start() # Start and wait for the process self.output_ = self.proc_.communicate() if self.proc_.returncode == 0: print pretty.PASS_INLINE() else: print pretty.FAIL_INLINE() print pretty.INFO("Process stdout") print pretty.DATA(self.output_[0]) print pretty.INFO("Process stderr") print pretty.DATA(self.output_[1]) return self.proc_.returncode
def exit(self, status, msg): self._exit_status = status self.stop() print color.INFO(nametag),"Exit called with status", self._exit_status, "(",get_exit_code_name(self._exit_status),")" print color.INFO(nametag),"Calling on_exit" # Change back to test source os.chdir(self._root) self._on_exit() if status == 0: # Print success message and return to caller print color.SUCCESS(msg) print color.INFO(nametag),"Calling on_exit_success" return self._on_exit_success() # Print fail message and exit with appropriate code print color.EXIT_ERROR(get_exit_code_name(status), msg) sys.exit(status)
def __init__(self, config): super(qemu, self).__init__(config) self._proc = None self._stopped = False self._sudo = False self._image_name = self._config if "image" in self._config else self.name() + " vm" # Pretty printing self.info = Logger(color.INFO("<" + type(self).__name__ + ">"))
def __init__(self, config): super(qemu, self).__init__(config) self._proc = None self._stopped = False self._sudo = False # Pretty printing self._nametag = "<" + type(self).__name__ + ">" self.INFO = color.INFO(self._nametag)
def panic(self, panic_line): panic_reason = self._hyper.readline() print color.INFO(nametag), "VM signalled PANIC. Reading until EOT (", hex(ord(EOT)), ")" print color.VM(panic_reason), remaining_output = self._hyper.read_until_EOT() for line in remaining_output.split("\n"): print color.VM(line) self.exit(exit_codes["VM_PANIC"], panic_reason)
def boot(self, multiboot, kernel_args): self._nametag = "<" + type(self).__name__ + ">" print color.INFO(self._nametag), "booting", self._config["image"] # multiboot if multiboot: print color.INFO( self._nametag), "Booting with multiboot (-kernel args)" kernel_args = [ "-kernel", self._config["image"].split(".")[0], "-append", kernel_args ] else: kernel_args = [] disk_args = self.drive_arg(self._config["image"], "ide") if "drives" in self._config: for disk in self._config["drives"]: disk_args += self.drive_arg(disk["file"], disk["type"], disk["format"], disk["media"]) net_args = [] i = 0 if "net" in self._config: for net in self._config["net"]: net_args += self.net_arg(net["backend"], net["device"], "net" + str(i), net["mac"]) i += 1 mem_arg = [] if "mem" in self._config: mem_arg = ["-m", str(self._config["mem"])] command = ["qemu-system-x86_64"] if self.kvm_present(): command.append("--enable-kvm") command += kernel_args command += ["-nographic"] + disk_args + net_args + mem_arg print color.INFO(self._nametag), "command:" print color.DATA(command.__str__()) self._proc = start_process(command)
def stop(self): # Check for sudo access, needed for qemu commands if os.getuid() is not 0: print color.FAIL("Call the script with sudo access") sys.exit(1) print color.INFO(nametag), "Stopping VM..." self._hyper.stop() if hasattr(self, "_timer") and self._timer: self._timer.cancel() return self
def start_process(popen_param_list): # Start a subprocess proc = subprocess.Popen(popen_param_list, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE) # After half a second it should be started, otherwise throw time.sleep(0.5) if (proc.poll()): data, err = proc.communicate() raise Exception(color.C_FAILED+"Process exited. ERROR: " + err.__str__() + " " + data + color.C_ENDC); print color.INFO(nametag), "Started process PID ",proc.pid return proc
def __init__(self, path, clean = False, command = ['sudo', '-E', 'python', 'test.py'], name = None): self.command_ = command self.proc_ = None self.path_ = path self.output_ = None if (name == None): self.name_ = path else: self.name_ = name print pretty.INFO("Test"), "starting", self.name_ if clean: subprocess.check_output(["make","clean"]) print pretty.C_GRAY + "\t Cleaned, now start... ", pretty.C_ENDC
def integration_tests(tests): """ Function that runs the tests that are passed to it. Filters out any invalid tests before running Arguments: tests: List containing test objects to be run Returns: integer: Number of tests that failed """ # Only run the valid tests tests = [ x for x in tests if not x.skip_ and x.type_ == 'integration' ] # Print info before starting to run print pretty.HEADER("Starting " + str(len(tests)) + " integration test(s)") for test in tests: print pretty.INFO("Test"), "starting", test.name_ processes = [] fail_count = 0 global test_count test_count += len(tests) # Start running tests in parallell for test in tests: processes.append(test.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 make(self, params = []): print color.INFO(nametag), "Building with 'make' (params=" + str(params) + ")" make = ["make"] make.extend(params) cmd(make) return self
# 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 print color.HEADER("IncludeOS vmrunner loading VM configs") schema_path = package_path + "/vm.schema.json" print color.INFO(nametag), "Validating JSON according to schema ",schema_path validate_vm.load_schema(schema_path) validate_vm.has_required_stuff(".") default_spec = {"image" : "service.img"} # Provide a list of VM's with validated specs vms = [] if validate_vm.valid_vms: print color.INFO(nametag), "Loaded VM specification(s) from JSON" for spec in validate_vm.valid_vms: print color.INFO(nametag), "Found VM spec: " print color.DATA(spec.__str__()) vms.append(vm(spec))
def clean(self): print color.INFO(nametag), "Cleaning cmake build folder" subprocess.call(["rm","-rf","build"])
def wait(self): print color.INFO(self._nametag), "Waiting for process to terminate" self._proc.wait()
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): print color.INFO(nametag),"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 ") if (timeout): self._timer.cancel() self.exit(exit_codes["CALLBACK_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
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" # Provide a list of VM's with validated specs # (One default vm added at the end) vms = [] panic_signature = "\\x15\\x07\\t\*\*\*\* PANIC \*\*\*\*" nametag = "<VMRunner>" INFO = color.INFO(nametag) VERB = bool(os.environ["VERBOSE"]) if "VERBOSE" in os.environ else False class Logger: def __init__(self, tag): self.tag = tag if (VERB): self.info = self.info_verb else: self.info = self.info_silent def __call__(self, *args): self.info(args) def info_verb(self, args):
if hasattr(self, "_timer") and self._timer: self._timer.cancel() return self def wait(self): if hasattr(self, "_timer") and self._timer: self._timer.join() self._hyper.wait() return self._exit_status def poll(self): return self._hyper.poll() print color.HEADER("IncludeOS vmrunner initializing tests") print color.INFO(nametag), "Validating test service" validate_test.load_schema( os.environ.get( "INCLUDEOS_SRC", os.path.realpath(os.path.join(os.getcwd(), os.path.dirname( __file__))).split('/test')[0]) + "/test/vm.schema.json") validate_test.has_required_stuff(".") default_spec = {"image": "test.img"} # Provide a list of VM's with validated specs vms = [] if validate_test.valid_vms: print print color.INFO(nametag), "Loaded VM specification(s) from JSON"