def stop(self, label): """Stops a virtual machine. Kill them all. @param label: virtual machine name. @raise CuckooMachineError: if unable to stop virtual machine. """ log.debug("Stopping machine %s", label) if self._status(label) == self.POWEROFF: raise CuckooMachineError( "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 CuckooMachineError( "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): """Start a virtual machine. @param label: virtual machine identifier: path to vmx file and current snapshot name. @raise CuckooMachineError: if unable to start. """ host, snapshot = self._parse_label(label) # Preventive check if self._is_running(host): raise CuckooMachineError("Machine %s is already running" % host) self._revert(host, snapshot) time.sleep(3) log.debug("Starting vm %s" % host) try: proc = subprocess.Popen([ self.options.vmware.path, "start", host, self.options.vmware.mode ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if self.options.vmware.mode.lower() == "gui": output, error = proc.communicate() if output: raise CuckooMachineError("Unable to start machine %s: %s" % (host, output)) except OSError as e: raise CuckooMachineError( "Unable to start machine %s in %s mode: %s" % (host, self.options.vmware.mode.upper(), e))
def stop(self, label): """Stops a virtual machine. @param label: virtual machine name. @raise CuckooMachineError: if unable to stop. """ log.debug("Stopping vm %s" % label) if self._status(label) in [self.POWEROFF, self.ABORTED]: raise CuckooMachineError("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 CuckooMachineError("VBoxManage failed powering off the machine: %s" % e) self._wait_status(label, [self.POWEROFF, self.ABORTED, self.SAVED])
def start(self, label): """Start a virtual machine. @param label: virtual machine name. @raise CuckooMachineError: if unable to start. """ log.debug("Starting vm %s" % label) if self._status(label) == self.RUNNING: raise CuckooMachineError("Trying to start an already started vm %s" % label) try: if subprocess.call([self.options.virtualbox.path, "snapshot", label, "restorecurrent"], stdout=subprocess.PIPE, stderr=subprocess.PIPE): raise CuckooMachineError("VBoxManage exited with error restoring the machine's snapshot") except OSError as e: raise CuckooMachineError("VBoxManage failed restoring the machine: %s" % e) self._wait_status(label, self.SAVED) try: subprocess.call([self.options.virtualbox.path, "startvm", label, "--type", self.options.virtualbox.mode], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as e: raise CuckooMachineError("VBoxManage failed starting the machine in %s mode: %s" % (mode.upper(), e)) self._wait_status(label, self.RUNNING)
def _check_vmx(self, host): """Checks a vmx file @param host: file path @raise CuckooMachineError: if file not found or not ending with .vmx """ if not host.endswith(".vmx"): raise CuckooMachineError( "Wrong configuration: vm path not ending with .vmx: %s)" % host) if not os.path.exists(self.options.vmware.path): raise CuckooMachineError("Vm file %s not found" % host)
def _connect(self): """Connects to libvirt subsystem. @raise CuckooMachineError: if cannot connect to libvirt or missing connection string. """ # Check if a connection string is available. if not self.dsn: raise CuckooMachineError( "You must provide a proper connection string") try: return libvirt.open(self.dsn) except libvirt.libvirtError: raise CuckooMachineError("Cannot connect to libvirt")
def _wait_status(self, label, state): """Waits for a vm status. @param label: virtual machine name. @param state: virtual machine status, accepts more than one states in a list. @raise CuckooMachineError: 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 cuckooseconds for machine %s to switch to status %s", waitme, label, state) if waitme > int(self.options_globals.timeouts.vm_state): raise CuckooMachineError( "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 CuckooMachineError: if cannot disconnect from libvirt. """ try: conn.close() except libvirt.libvirtError: raise CuckooMachineError("Cannot disconnect from libvirt")
def _revert(self, host, snapshot): """Revets machine to snapshot. @param host: file path @param snapshot: snapshot name @raise CuckooMachineError: if unable to revert """ log.debug("Revert snapshot for vm %s" % host) try: if subprocess.call( [self.options.vmware.path, "revertToSnapshot", host, snapshot], stdout=subprocess.PIPE, stderr=subprocess.PIPE): raise CuckooMachineError( "Unable to revert snapshot for machine %s: vmrun exited with error" % host) except OSError as e: raise CuckooMachineError( "Unable to revert snapshot for machine %s: %s" % (host, e))
def _initialize_check(self): """Check for configuration file and vmware setup. @raise CuckooMachineError: if configuration is missing or wrong. """ if not self.options.vmware.path: raise CuckooMachineError( "VMware vmrun path missing, please add it to vmware.conf") if not os.path.exists(self.options.vmware.path): raise CuckooMachineError( "VMware vmrun not found in specified path %s" % self.options.vmware.path) # Consistency checks. for machine in self.machines(): host, snapshot = self._parse_label(machine.label) self._check_vmx(host) self._check_snapshot(host, snapshot) # Base checks. super(VMware, 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) # 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 CuckooMachineError( "Error getting status for virtual machine {0}: {1}".format( label, e)) finally: self._disconnect(conn) if state: if state[0] == 1 or state[0] == 3: status = self.RUNNING 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 CuckooMachineError( "Unable to get status for {0}".format(label))
def _parse_label(self, label): """Parse configuration file label. @param label: configuration option from config file @return: tuple of host file path and snapshot name """ opts = label.strip().split(",") if len(opts) != 2: raise CuckooMachineError( "Wrong label syntax for %s in vmware.conf: %s" % label) label = opts[0].strip() snapshot = opts[1].strip() return label, snapshot
def _list(self): """List available virtual machines. @raise CuckooMachineError: if unable to list virtual machines. """ conn = self._connect() try: names = conn.listDefinedDomains() except libvirt.libvirtError: raise CuckooMachineError("Cannot list domains") finally: self._disconnect(conn) return names
def stop(self, label): """Stops a virtual machine. @param label: virtual machine identifier: path to vmx file and current snapshot name. @raise CuckooMachineError: if unable to stop. """ host, snapshot = self._parse_label(label) log.debug("Stopping vm %s" % host) if self._is_running(host): try: if subprocess.call( [self.options.vmware.path, "stop", host, "hard" ], # Machete never wait. stdout=subprocess.PIPE, stderr=subprocess.PIPE): raise CuckooMachineError("Error shutting down machine %s" % host) except OSError as e: raise CuckooMachineError("Error shutting down machine %s: %s" % (host, e)) else: log.warning("Trying to stop an already stopped machine: %s" % host)
def start(self, label): """Starts a virtual machine. @param label: virtual machine name. @raise CuckooMachineError: if unable to start virtual machine. """ log.debug("Staring machine %s", label) if self._status(label) == self.RUNNING: raise CuckooMachineError( "Trying to start an already started machine {0}".format(label)) # Get current snapshot. conn = self._connect() try: snap = self.vms[label].hasCurrentSnapshot(flags=0) except libvirt.libvirtError: self._disconnect(conn) raise CuckooMachineError( "Unable to get current snapshot for virtual machine {0}". format(label)) # Revert to latest snapshot. if snap: try: current = self.vms[label].snapshotCurrent(flags=0) self.vms[label].revertToSnapshot(current, flags=0) except libvirt.libvirtError: raise CuckooMachineError( "Unable to restore snapshot on virtual machine {0}".format( label)) finally: self._disconnect(conn) else: self._disconnect(conn) raise CuckooMachineError( "No snapshot found for virtual machine {0}".format(label)) # Check state. self._wait_status(label, self.RUNNING)
def _initialize_check(self): """Runs all checks when a machine manager is initialized. @raise CuckooMachineError: if libvirt version is not supported. """ # Version checks. if not self._version_check(): raise CuckooMachineError( "Libvirt version is not supported, please get an updated version" ) # Preload VMs self.vms = self._fetch_machines() # Base checks. super(LibVirtMachineManager, self)._initialize_check()
def _lookup(self, label): """Search for a virtual machine. @param conn: libvirt connection handle. @param label: virtual machine name. @raise CuckooMachineError: if virtual machine is not found. """ conn = self._connect() try: vm = conn.lookupByName(label) except libvirt.libvirtError: raise CuckooMachineError("Cannot found machine {0}".format(label)) finally: self._disconnect(conn) return vm
def _is_running(self, host): """Checks if host is running. @param host: file path @return: running status """ try: output, error = subprocess.Popen( [self.options.vmware.path, "list"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() if output: if host in output: return True else: return False else: raise CuckooMachineError( "Unable to check running status for %s. No output from `vmrun list`" % host) except OSError as e: raise CuckooMachineError( "Unable to check running status for %s. Reason: %s" % (host, e))
def _check_snapshot(self, host, snapshot): """Checks snapshot existance. @param host: file path @param snapshot: snapshot name @raise CuckooMachineError: if snapshot not found """ try: output, error = subprocess.Popen( [self.options.vmware.path, "listSnapshots", host], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() if output: if snapshot in output: return True else: return False else: raise CuckooMachineError( "Unable to get snapshot list for %s. No output from `vmrun listSnapshots`" % host) except OSError as e: raise CuckooMachineError( "Unable to get snapshot list for %s. Reason: %s" % (host, 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 CuckooMachineError( "Error dumping memory virtual machine {0}: {1}".format( label, e)) finally: self._disconnect(conn)
def dump_memory(self, label, path): """Takes a memory dump. @param path: path to where to store the memory dump. """ try: subprocess.call([self.options.virtualbox.path, "debugvm", label, "dumpguestcore", "--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 CuckooMachineError("VBoxManage failed to take a memory dump of the machine with label %s: %s" % (label, e))
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 CuckooMachineError("Unable to get status for %s" % label)
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 CuckooMachineError("VBoxManage error listing installed machines: %s" % e) machines = [] for line in output[0].split("\n"): try: label = line.split('"')[1] if label == "<inaccessible>": log.warning("Found an inaccessible vitual machine: please check his state") else: machines.append(label) except IndexError: continue return machines