def stop(self, label): """Stops a virtual machine. @param label: virtual machine name. @raise DetectorMachineError: if unable to stop. """ log.debug("Stopping vm %s" % label) if self._status(label) in [self.POWEROFF, self.ABORTED]: raise DetectorMachineError("Trying to stop an already stopped " "vm %s" % label) try: proc = subprocess.Popen([self.options.virtualbox.path, "controlvm", label, "poweroff"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Sometimes VBoxManage stucks when stopping vm so we needed # to add a timeout and kill it after that. stop_me = 0 while proc.poll() is None: if stop_me < int(self.options_globals.timeouts.vm_state): time.sleep(1) stop_me += 1 else: log.debug("Stopping vm %s timeouted. Killing" % label) proc.terminate() if proc.returncode != 0 and \ stop_me < int(self.options_globals.timeouts.vm_state): log.debug("VBoxManage exited with error " "powering off the machine") except OSError as e: raise DetectorMachineError("VBoxManage failed powering off the " "machine: %s" % e) self._wait_status(label, [self.POWEROFF, self.ABORTED, self.SAVED])
def stop(self, label): """Stops a virtual machine. Kill them all. @param label: virtual machine name. @raise DetectorMachineError: if unable to stop virtual machine. """ log.debug("Stopping machine %s", label) if self._status(label) == self.POWEROFF: raise DetectorMachineError("Trying to stop an already stopped " "machine {0}".format(label)) # Force virtual machine shutdown. conn = self._connect() try: if not self.vms[label].isActive(): log.debug( "Trying to stop an already stopped machine %s. " "Skip", label) else: self.vms[label].destroy() # Machete's way! except libvirt.libvirtError as e: raise DetectorMachineError("Error stopping virtual machine " "{0}: {1}".format(label, e)) finally: self._disconnect(conn) # Check state. self._wait_status(label, self.POWEROFF)
def start(self, label, task): """Start a virtual machine. @param label: virtual machine name. @param task: task object. @raise DetectorMachineError: if unable to start. """ log.debug("Starting vm %s" % label) if self._status(label) == self.RUNNING: raise DetectorMachineError("Trying to start an already " "started vm %s" % label) machine = self.db.view_machine_by_label(label) virtualbox_args = [self.options.virtualbox.path, "snapshot", label] if machine.snapshot: log.debug("Using snapshot {0} for virtual machine " "{1}".format(machine.snapshot, label)) virtualbox_args.extend(["restore", machine.snapshot]) else: log.debug("Using current snapshot for virtual machine " "{0}".format(label)) virtualbox_args.extend(["restorecurrent"]) try: if subprocess.call(virtualbox_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE): raise DetectorMachineError("VBoxManage exited with error " "restoring the machine's snapshot") except OSError as e: raise DetectorMachineError("VBoxManage failed restoring the " "machine: %s" % e) self._wait_status(label, self.SAVED) try: proc = subprocess.Popen([self.options.virtualbox.path, "startvm", label, "--type", self.options.virtualbox.mode], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = proc.communicate() if err: raise OSError(err) except OSError as e: raise DetectorMachineError("VBoxManage failed starting the machine " "in %s mode: %s" % (self.options.virtualbox.mode.upper(), e)) self._wait_status(label, self.RUNNING) # Handle network dumping through the internal VirtualBox functionality. if "nictrace" in machine.options: self.dump_pcap(label, task)
def start(self, label, task): """Starts a virtual machine. @param label: virtual machine name. @param task: task object. @raise DetectorMachineError: if unable to start virtual machine. """ log.debug("Starting machine %s", label) if self._status(label) != self.POWEROFF: msg = "Trying to start a virtual machine that has not " \ "been turned off {0}".format(label) raise DetectorMachineError(msg) conn = self._connect() vm_info = self.db.view_machine_by_label(label) snapshot_list = self.vms[label].snapshotListNames(flags=0) # If a snapshot is configured try to use it. if vm_info.snapshot and vm_info.snapshot in snapshot_list: # Revert to desired snapshot, if it exists. log.debug("Using snapshot {0} for virtual machine " "{1}".format(vm_info.snapshot, label)) try: vm = self.vms[label] snapshot = vm.snapshotLookupByName(vm_info.snapshot, flags=0) self.vms[label].revertToSnapshot(snapshot, flags=0) except libvirt.libvirtError: msg = "Unable to restore snapshot {0} on " \ "virtual machine {1}".format(vm_info.snapshot, label) raise DetectorMachineError(msg) finally: self._disconnect(conn) elif self._get_snapshot(label): snapshot = self._get_snapshot(label) log.debug("Using snapshot {0} for virtual machine " "{1}".format(snapshot.getName(), label)) try: self.vms[label].revertToSnapshot(snapshot, flags=0) except libvirt.libvirtError: raise DetectorMachineError("Unable to restore snapshot on " "virtual machine {0}".format(label)) finally: self._disconnect(conn) else: self._disconnect(conn) raise DetectorMachineError("No snapshot found for virtual machine " "{0}".format(label)) # Check state. self._wait_status(label, self.RUNNING)
def _connect(self): """Connects to libvirt subsystem. @raise DetectorMachineError: when unable to connect to libvirt. """ # Check if a connection string is available. if not self.dsn: raise DetectorMachineError("You must provide a proper " "connection string") try: return libvirt.open(self.dsn) except libvirt.libvirtError: raise DetectorMachineError("Cannot connect to libvirt")
def _list(self): """Lists virtual machines installed. @return: virtual machine names list. """ try: proc = subprocess.Popen([self.options.virtualbox.path, "list", "vms"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, _ = proc.communicate() except OSError as e: raise DetectorMachineError("VBoxManage error listing " "installed machines: %s" % e) machines = [] for line in output.split("\n"): try: label = line.split('"')[1] if label == "<inaccessible>": log.warning("Found an inaccessible virtual machine, " "please check its state.") else: machines.append(label) except IndexError: continue return machines
def _wait_status(self, label, state): """Waits for a vm status. @param label: virtual machine name. @param state: virtual machine status, accepts multiple states as list. @raise DetectorMachineError: if default waiting timeout expire. """ # This block was originally suggested by Loic Jaquemet. waitme = 0 try: current = self._status(label) except NameError: return if isinstance(state, str): state = [state] while current not in state: log.debug( "Waiting %i detectorseconds for machine %s to switch " "to status %s", waitme, label, state) if waitme > int(self.options_globals.timeouts.vm_state): raise DetectorMachineError("Timeout hit while for machine {0} " "to change status".format(label)) time.sleep(1) waitme += 1 current = self._status(label)
def _disconnect(self, conn): """Disconnects to libvirt subsystem. @raise DetectorMachineError: if cannot disconnect from libvirt. """ try: conn.close() except libvirt.libvirtError: raise DetectorMachineError("Cannot disconnect from libvirt")
def _status(self, label): """Gets current status of a vm. @param label: virtual machine name. @return: status string. """ log.debug("Getting status for %s", label) # Stetes mapping of python-libvirt. # virDomainState # VIR_DOMAIN_NOSTATE = 0 # VIR_DOMAIN_RUNNING = 1 # VIR_DOMAIN_BLOCKED = 2 # VIR_DOMAIN_PAUSED = 3 # VIR_DOMAIN_SHUTDOWN = 4 # VIR_DOMAIN_SHUTOFF = 5 # VIR_DOMAIN_CRASHED = 6 # VIR_DOMAIN_PMSUSPENDED = 7 conn = self._connect() try: state = self.vms[label].state(flags=0) except libvirt.libvirtError as e: raise DetectorMachineError("Error getting status for virtual " "machine {0}: {1}".format(label, e)) finally: self._disconnect(conn) if state: if state[0] == 1: status = self.RUNNING elif state[0] == 3: status = self.PAUSED elif state[0] == 4 or state[0] == 5: status = self.POWEROFF else: status = self.ERROR # Report back status. if status: self.set_status(label, status) return status else: raise DetectorMachineError("Unable to get status for " "{0}".format(label))
def _list(self): """List available virtual machines. @raise DetectorMachineError: if unable to list virtual machines. """ conn = self._connect() try: names = conn.listDefinedDomains() except libvirt.libvirtError: raise DetectorMachineError("Cannot list domains") finally: self._disconnect(conn) return names
def dump_memory(self, label, path): """Takes a memory dump. @param path: path to where to store the memory dump. """ try: proc = subprocess.Popen([self.options.virtualbox.path, "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = proc.communicate() if proc.returncode != 0: # It's quite common for virtualbox crap utility to exit with: # VBoxManage: error: Details: code E_ACCESSDENIED (0x80070005) # So we just log to debug this. log.debug("VBoxManage returns error checking status for " "machine %s: %s", label, err) except OSError as e: raise DetectorMachineError("VBoxManage failed return it's version: %s" % (e)) if output[:1] == str(5): # VirtualBox version 5.x dumpcmd = "dumpvmcore" else: # VirtualBox version 4.x dumpcmd = "dumpguestcore" try: subprocess.call([self.options.virtualbox.path, "debugvm", label, dumpcmd, "--filename", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) log.info("Successfully generated memory dump for virtual machine " "with label %s to path %s", label, path) except OSError as e: raise DetectorMachineError("VBoxManage failed to take a memory " "dump of the machine with label %s: %s" % (label, e))
def dump_memory(self, label, path): """Takes a memory dump. @param path: path to where to store the memory dump. """ log.debug("Dumping memory for machine %s", label) conn = self._connect() try: self.vms[label].coreDump(path, flags=libvirt.VIR_DUMP_MEMORY_ONLY) except libvirt.libvirtError as e: raise DetectorMachineError("Error dumping memory virtual machine " "{0}: {1}".format(label, e)) finally: self._disconnect(conn)
def _lookup(self, label): """Search for a virtual machine. @param conn: libvirt connection handle. @param label: virtual machine name. @raise DetectorMachineError: if virtual machine is not found. """ conn = self._connect() try: vm = conn.lookupByName(label) except libvirt.libvirtError: raise DetectorMachineError("Cannot find machine " "{0}".format(label)) finally: self._disconnect(conn) return vm
def _initialize_check(self): """Runs all checks when a machine manager is initialized. @raise DetectorMachineError: if libvirt version is not supported. """ # Version checks. if not self._version_check(): raise DetectorMachineError("Libvirt version is not supported, " "please get an updated version") # Preload VMs self.vms = self._fetch_machines() # Base checks. Also attempts to shutdown any machines which are # currently still active. super(LibVirtMachinery, self)._initialize_check()
def _status(self, label): """Gets current status of a vm. @param label: virtual machine name. @return: status string. """ log.debug("Getting status for %s" % label) status = None try: proc = subprocess.Popen([self.options.virtualbox.path, "showvminfo", label, "--machinereadable"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = proc.communicate() if proc.returncode != 0: # It's quite common for virtualbox crap utility to exit with: # VBoxManage: error: Details: code E_ACCESSDENIED (0x80070005) # So we just log to debug this. log.debug("VBoxManage returns error checking status for " "machine %s: %s", label, err) status = self.ERROR except OSError as e: log.warning("VBoxManage failed to check status for machine %s: %s", label, e) status = self.ERROR if not status: for line in output.split("\n"): state = re.match(r'VMState="(\w+)"', line, re.M | re.I) if state: status = state.group(1) log.debug("Machine %s status %s" % (label, status)) status = status.lower() # Report back status. if status: self.set_status(label, status) return status else: raise DetectorMachineError("Unable to get status for %s" % label)
def _get_snapshot(self, label): """Get current snapshot for virtual machine @param label: virtual machine name @return None or current snapshot @raise DetectorMachineError: if cannot find current snapshot or when there are too many snapshots available """ def _extract_creation_time(node): """Extracts creation time from a KVM vm config file. @param node: config file node @return: extracted creation time """ xml = ET.fromstring(node.getXMLDesc(flags=0)) return xml.findtext("./creationTime") snapshot = None conn = self._connect() try: vm = self.vms[label] # Try to get the currrent snapshot, otherwise fallback on the latest # from config file. if vm.hasCurrentSnapshot(flags=0): snapshot = vm.snapshotCurrent(flags=0) else: log.debug("No current snapshot, using latest snapshot") # No current snapshot, try to get the last one from config file. snapshot = sorted(vm.listAllSnapshots(flags=0), key=_extract_creation_time, reverse=True)[0] except libvirt.libvirtError: raise DetectorMachineError("Unable to get snapshot for " "virtual machine {0}".format(label)) finally: self._disconnect(conn) return snapshot