# -*- coding: utf-8 -*- # # Copyright (c) 2018 German Mendez Bravo (Kronuz) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # from __future__ import absolute_import import os import sys import logging import subprocess import tempfile from .compat import PY3, b2s logger = logging.getLogger(__name__) def get_fallback_executable(): if 'PATH' in os.environ: for path in os.environ['PATH'].split(os.pathsep): vmrun = os.path.join(path, 'vmrun') if os.path.exists(vmrun): return vmrun vmrun = os.path.join(path, 'vmrun.exe') if os.path.exists(vmrun): return vmrun def get_darwin_executable(): vmrun = '/Applications/VMware Fusion.app/Contents/Library/vmrun' if os.path.exists(vmrun): return vmrun return get_fallback_executable() def get_win32_executable(): if PY3: import winreg else: import _winreg as winreg reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) try: key = winreg.OpenKey(reg, 'SOFTWARE\\VMware, Inc.\\VMware Workstation') try: return os.path.join(winreg.QueryValueEx(key, 'InstallPath')[0], 'vmrun.exe') finally: winreg.CloseKey(key) except WindowsError: key = winreg.OpenKey(reg, 'SOFTWARE\\WOW6432Node\\VMware, Inc.\\VMware Workstation') try: return os.path.join(winreg.QueryValueEx(key, 'InstallPath')[0], 'vmrun.exe') finally: winreg.CloseKey(key) finally: reg.Close() return get_fallback_executable() def get_provider(vmrun_exe): """ identifies the right hosttype for vmrun command (ws | fusion | player) """ if sys.platform == 'darwin': return 'fusion' for provider in ['ws', 'player', 'fusion']: try: startupinfo = None if os.name == "nt": startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.SW_HIDE | subprocess.STARTF_USESHOWWINDOW proc = subprocess.Popen([vmrun_exe, '-T', provider, 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo) except OSError: pass stdoutdata, stderrdata = map(b2s, proc.communicate()) if proc.returncode == 0: return provider class VMrun(object): if sys.platform == 'darwin': default_executable = get_darwin_executable() elif sys.platform == 'win32': default_executable = get_win32_executable() else: default_executable = get_fallback_executable() default_provider = get_provider(default_executable) def __init__(self, vmx_file=None, user=None, password=None, executable=None, provider=None): self.vmx_file = vmx_file self.user = user self.password = password self.executable = executable or self.default_executable self.provider = provider or self.default_provider def vmrun(self, cmd, *args, **kwargs): quiet = kwargs.pop('quiet', False) arguments = kwargs.pop('arguments', ()) cmds = [self.executable] cmds.append('-T') cmds.append(self.provider) if self.user: cmds.append('-gu') cmds.append(self.user) if self.password: cmds.append('-gp') cmds.append(self.password) cmds.append(cmd) cmds.extend(filter(None, args)) cmds.extend(filter(None, arguments)) logger.debug(" ".join("'{}'".format(c.replace("'", "\\'")) if ' ' in c else c for c in cmds)) startupinfo = None if os.name == "nt": startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.SW_HIDE | subprocess.STARTF_USESHOWWINDOW proc = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo) stdoutdata, stderrdata = map(b2s, proc.communicate()) if stderrdata and not quiet: logger.error(stderrdata.strip()) logger.debug("(⏎ %s)" % proc.returncode) if not proc.returncode: stdoutdata = stdoutdata.strip() logger.debug(repr(stdoutdata)) return stdoutdata if stdoutdata and not quiet: logger.error(stdoutdata.strip()) ############################################################################ # POWER COMMANDS PARAMETERS DESCRIPTION # -------------- ---------- ----------- # start Path to vmx file Start a VM or Team # [gui|nogui] # # stop Path to vmx file Stop a VM or Team # [hard|soft] # # reset Path to vmx file Reset a VM or Team # [hard|soft] # # suspend Path to vmx file Suspend a VM or Team # [hard|soft] # # pause Path to vmx file Pause a VM # # unpause Path to vmx file Unpause a VM # def start(self, gui=False, quiet=False): '''Start a VM or Team''' return self.vmrun('start', self.vmx_file, 'gui' if gui else 'nogui', quiet=quiet) def stop(self, mode='soft', quiet=False): '''Stop a VM or Team''' return self.vmrun('stop', self.vmx_file, mode, quiet=quiet) def reset(self, mode='soft', quiet=False): '''Reset a VM or Team''' return self.vmrun('reset', self.vmx_file, mode, quiet=quiet) def suspend(self, mode='soft', quiet=False): '''Suspend a VM or Team''' return self.vmrun('suspend', self.vmx_file, mode, quiet=quiet) def pause(self, quiet=False): '''Pause a VM''' return self.vmrun('pause', self.vmx_file, quiet=quiet) def unpause(self, quiet=False): '''Unpause a VM''' return self.vmrun('unpause', self.vmx_file, quiet=quiet) ############################################################################ # SNAPSHOT COMMANDS PARAMETERS DESCRIPTION # ----------------- ---------- ----------- # listSnapshots Path to vmx file List all snapshots in a VM # [showTree] # # snapshot Path to vmx file Create a snapshot of a VM # Snapshot name # # deleteSnapshot Path to vmx file Remove a snapshot from a VM # Snapshot name # [andDeleteChildren] # # revertToSnapshot Path to vmx file Set VM state to a snapshot # Snapshot name # def listSnapshots(self, show_tree=False, quiet=False): '''List all snapshots in a VM''' return self.vmrun('listSnapshots', self.vmx_file, 'showTree' if show_tree else None, quiet=quiet) def snapshot(self, snap_name, quiet=False): '''Create a snapshot of a VM''' return self.vmrun('snapshot', self.vmx_file, snap_name, quiet=quiet) def deleteSnapshot(self, snap_name, and_delete_children=False, quiet=False): '''Remove a snapshot from a VM''' return self.vmrun('deleteSnapshot', self.vmx_file, snap_name, 'andDeleteChildren' if and_delete_children else None, quiet=quiet) def revertToSnapshot(self, snap_name, quiet=False): '''Set VM state to a snapshot''' return self.vmrun('revertToSnapshot', self.vmx_file, snap_name, quiet=quiet) ############################################################################ # NETWORKADAPTER COMMANDS PARAMETERS DESCRIPTION # ----------------------- ---------- ----------- # listNetworkAdapters Path to vmx file List all network adapters in a VM # # # addNetworkAdapter Path to vmx file Add a network adapter on a VM # Network adapter type # [Host nework] # # # setNetworkAdapter Path to vmx file Update a network adapter on a VM # Network adapter index # Network adapter type # [Host network] # # # deleteNetworkAdapter Path to vmx file Remove a network adapter on a VM # Network adapter index def listNetworkAdapters(self, quiet=False): '''List all network adapters in a VM''' return self.vmrun('listNetworkAdapters', self.vmx_file, quiet=quiet) def addNetworkAdapter(self, adapter_type, host_network=None, quiet=False): '''Add a network adapter on a VM''' return self.vmrun('addNetworkAdapter', self.vmx_file, adapter_type, host_network, quiet=quiet) def setNetworkAdapter(self, adapter_index, adapter_type, host_network=None, quiet=False): '''Update a network adapter on a VM''' return self.vmrun('setNetworkAdapter', self.vmx_file, adapter_index, adapter_type, host_network, quiet=quiet) def deleteNetworkAdapter(self, adapter_index, quiet=False): '''Remove a network adapter on a VM''' return self.vmrun('deleteNetworkAdapter', self.vmx_file, adapter_index, quiet=quiet) ############################################################################ # HOST NETWORK COMMANDS PARAMETERS DESCRIPTION # --------------------- ---------- ----------- # listHostNetworks List all networks in the host # # listPortForwardings Host network name List all available port forwardings on a host network # # # setPortForwarding Host network name Add or update a port forwarding on a host network # Protocol # Host port # Guest ip # Guest port # [Description] # # deletePortForwarding Host network name Delete a port forwarding on a host network # Protocol # Host port def listHostNetworks(self, quiet=False): '''List all networks in the host''' return self.vmrun('listHostNetworks', quiet=quiet) def listPortForwardings(self, host_network, quiet=False): '''List all available port forwardings on a host network''' return self.vmrun('listPortForwardings', host_network, quiet=quiet) def setPortForwarding(self, host_network, protocol, host_port, guest_ip, guest_port, description=None, quiet=False): '''Add or update a port forwarding on a host network''' return self.vmrun('setPortForwarding', host_network, protocol, host_port, guest_ip, guest_port, description, quiet=quiet) def deletePortForwarding(self, host_network, protocol, host_port, quiet=False): '''Delete a port forwarding on a host network''' return self.vmrun('deletePortForwarding', host_network, protocol, host_port, quiet=quiet) ############################################################################ # GUEST OS COMMANDS PARAMETERS DESCRIPTION # ----------------- ---------- ----------- # runProgramInGuest Path to vmx file Run a program in Guest OS # [-noWait] # [-activeWindow] # [-interactive] # Complete-Path-To-Program # [Program arguments] # # fileExistsInGuest Path to vmx file Check if a file exists in Guest OS # Path to file in guest # # directoryExistsInGuest Path to vmx file Check if a directory exists in Guest OS # Path to directory in guest # # setSharedFolderState Path to vmx file Modify a Host-Guest shared folder # Share name # Host path # writable | readonly # # addSharedFolder Path to vmx file Add a Host-Guest shared folder # Share name # New host path # # removeSharedFolder Path to vmx file Remove a Host-Guest shared folder # Share name # # enableSharedFolders Path to vmx file Enable shared folders in Guest # [runtime] # # disableSharedFolders Path to vmx file Disable shared folders in Guest # [runtime] # # listProcessesInGuest Path to vmx file List running processes in Guest OS # # killProcessInGuest Path to vmx file Kill a process in Guest OS # process id # # runScriptInGuest Path to vmx file Run a script in Guest OS # [-noWait] # [-activeWindow] # [-interactive] # Interpreter path # Script text # # deleteFileInGuest Path to vmx file Delete a file in Guest OS # Path in guest # # createDirectoryInGuest Path to vmx file Create a directory in Guest OS # Directory path in guest # # deleteDirectoryInGuest Path to vmx file Delete a directory in Guest OS # Directory path in guest # # createTempfileInGuest Path to vmx file Create a temporary file in Guest OS # # listDirectoryInGuest Path to vmx file List a directory in Guest OS # Directory path in guest # # copyFileFromHostToGuest Path to vmx file Copy a file from host OS to guest OS # Path on host # Path in guest # # copyFileFromGuestToHost Path to vmx file Copy a file from guest OS to host OS # Path in guest # Path on host # # renameFileInGuest Path to vmx file Rename a file in Guest OS # Original name # New name # # typeKeystrokesInGuest Path to vmx file Type Keystrokes in Guest OS # keystroke string # # connectNamedDevice Path to vmx file Connect the named device in the Guest OS # device name # # disconnectNamedDevice Path to vmx file Disconnect the named device in the Guest OS # device name # # captureScreen Path to vmx file Capture the screen of the VM to a local file # Path on host # # writeVariable Path to vmx file Write a variable in the VM state # [runtimeConfig|guestEnv|guestVar] # variable name # variable value # # readVariable Path to vmx file Read a variable in the VM state # [runtimeConfig|guestEnv|guestVar] # variable name # # getGuestIPAddress Path to vmx file Gets the IP address of the guest # [-wait] # def runProgramInGuest(self, program_path, program_arguments=[], wait=True, activate_window=False, interactive=False, quiet=False): return self.vmrun('runProgramInGuest', self.vmx_file, None if wait else '-noWait', '-activateWindow' if activate_window else None, '-interactive' if interactive else None, program_path, arguments=program_arguments, quiet=quiet) def fileExistsInGuest(self, file, quiet=False): '''Check if a file exists in Guest OS''' return 'not' not in self.execute('fileExistsInGuest', self.vmx_file, file) def directoryExistsInGuest(self, path, quiet=False): '''Check if a directory exists in Guest OS''' return 'not' not in self.execute('directoryExistsInGuest', self.vmx_file, path) def setSharedFolderState(self, share_name, new_path, mode='readonly', quiet=False): '''Modify a Host-Guest shared folder''' return self.vmrun('setSharedFolderState', self.vmx_file, share_name, new_path, mode, quiet=quiet) def addSharedFolder(self, share_name, host_path, quiet=False): '''Add a Host-Guest shared folder''' return self.vmrun('addSharedFolder', self.vmx_file, share_name, host_path, quiet=quiet) def removeSharedFolder(self, share_name, quiet=False): '''Remove a Host-Guest shared folder''' return self.vmrun('removeSharedFolder', self.vmx_file, share_name, quiet=quiet) def enableSharedFolders(self, runtime=None, quiet=False): return self.vmrun('enableSharedFolders', self.vmx_file, runtime, quiet=quiet) def disableSharedFolders(self, runtime=None, quiet=False): '''Disable shared folders in Guest''' return self.vmrun('disableSharedFolders', self.vmx_file, runtime, quiet=quiet) def listProcessesInGuest(self, quiet=False): '''List running processes in Guest OS''' return self.vmrun('listProcessesInGuest', self.vmx_file, quiet=quiet) def killProcessInGuest(self, pid, quiet=False): '''Kill a process in Guest OS''' return self.vmrun('killProcessInGuest', self.vmx_file, pid, quiet=quiet) def runScriptInGuest(self, interpreter_path, script, wait=True, activate_window=False, interactive=False, quiet=False): '''Run a script in Guest OS''' return self.vmrun('runScriptInGuest', self.vmx_file, interpreter_path, script, None if wait else '-noWait', '-activateWindow' if activate_window else None, '-interactive' if interactive else None, quiet=quiet) def deleteFileInGuest(self, file, quiet=False): '''Delete a file in Guest OS''' return self.vmrun('deleteFileInGuest', self.vmx_file, file, quiet=quiet) def createDirectoryInGuest(self, path, quiet=False): '''Create a directory in Guest OS''' return self.vmrun('createDirectoryInGuest', self.vmx_file, path, quiet=quiet) def deleteDirectoryInGuest(self, path, quiet=False): '''Delete a directory in Guest OS''' return self.vmrun('deleteDirectoryInGuest', self.vmx_file, path, quiet=quiet) def createTempfileInGuest(self, quiet=False): '''Create a temporary file in Guest OS''' return self.vmrun('createTempfileInGuest', self.vmx_file, quiet=quiet) def listDirectoryInGuest(self, path, quiet=False): '''List a directory in Guest OS''' return self.vmrun('listDirectoryInGuest', self.vmx_file, path, quiet=quiet) def copyFileFromHostToGuest(self, host_path, guest_path, quiet=False): '''Copy a file from host OS to guest OS''' return self.vmrun('copyFileFromHostToGuest', self.vmx_file, host_path, guest_path, quiet=quiet) def copyFileFromGuestToHost(self, guest_path, host_path, quiet=False): '''Copy a file from guest OS to host OS''' return self.vmrun('copyFileFromGuestToHost', self.vmx_file, guest_path, host_path, quiet=quiet) def renameFileInGuest(self, original_name, new_name, quiet=False): '''Rename a file in Guest OS''' return self.vmrun('renameFileInGuest', self.vmx_file, original_name, new_name, quiet=quiet) def typeKeystrokesInGuest(self, keystroke, quiet=False): '''Type Keystrokes in Guest OS''' return self.vmrun('typeKeystrokesInGuest', self.vmx_file, keystroke, quiet=quiet) def connectNamedDevice(self, device_name, quiet=False): '''Connect the named device in the Guest OS''' return self.vmrun('connectNamedDevice', self.vmx_file, device_name, quiet=quiet) def disconnectNamedDevice(self, device_name, quiet=False): '''Disconnect the named device in the Guest OS''' return self.vmrun('disconnectNamedDevice', self.vmx_file, device_name, quiet=quiet) def captureScreen(self, path_on_host, quiet=False): '''Capture the screen of the VM to a local file''' return self.vmrun('captureScreen', self.vmx_file, path_on_host, quiet=quiet) def writeVariable(self, var_name, var_value, mode=None, quiet=False): '''Write a variable in the VM state''' return self.vmrun('writeVariable', self.vmx_file, mode, var_name, var_value, quiet=quiet) def readVariable(self, var_name, mode=None, quiet=False): '''Read a variable in the VM state''' return self.vmrun('readVariable', self.vmx_file, mode, var_name, quiet=quiet) def getGuestIPAddress(self, wait=True, quiet=False, lookup=False): '''Gets the IP address of the guest''' if lookup is True: self.runScriptInGuest('/bin/sh', "ifconfig | grep -Eo 'inet (addr:)?([0-9]*\\.){3}[0-9]*' | grep -Eo '([0-9]*\\.){3}[0-9]*' | grep -v '127.0.0.1' > /tmp/ip_address", quiet=quiet) fp = tempfile.NamedTemporaryFile(delete=False) try: fp.close() self.copyFileFromGuestToHost('/tmp/ip_address', fp.name, quiet=quiet) ip_addresses = open(fp.name).read().split() if ip_addresses: return ip_addresses[0] else: return None finally: os.unlink(fp.name) ip = self.vmrun('getGuestIPAddress', self.vmx_file, '-wait' if wait else None, quiet=quiet) if ip == 'unknown': ip = '' return ip ############################################################################ # GENERAL COMMANDS PARAMETERS DESCRIPTION # ---------------- ---------- ----------- # list List all running VMs # # upgradevm Path to vmx file Upgrade VM file format, virtual hw # # installTools Path to vmx file Install Tools in Guest # # checkToolsState Path to vmx file Check the current Tools state # # deleteVM Path to vmx file Delete a VM # # clone Path to vmx file Create a copy of the VM # Path to destination vmx file # full|linked # [-snapshot=Snapshot Name] # [-cloneName=Name] def list(self, quiet=False): '''List all running VMs''' return self.vmrun('list', self.vmx_file, quiet=quiet) def upgradevm(self, quiet=False): '''Upgrade VM file format, virtual hw''' return self.vmrun('upgradevm', self.vmx_file, quiet=quiet) def installTools(self, quiet=False): '''Install Tools in Guest OS''' return self.vmrun('installTools', self.vmx_file, quiet=quiet) def checkToolsState(self, quiet=False): '''Check the current Tools state''' return self.vmrun('checkToolsState', self.vmx_file, quiet=quiet) def register(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Register a VM''' return self.vmrun('register', self.vmx_file, quiet=quiet) def unregister(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Unregister a VM''' return self.vmrun('unregister', self.vmx_file, quiet=quiet) def listRegisteredVM(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''List registered VMs''' return self.vmrun('listRegisteredVM', self.vmx_file, quiet=quiet) def deleteVM(self, quiet=False): '''Delete a VM''' return self.vmrun('deleteVM', self.vmx_file, quiet=quiet) def clone(self, dest_vmx, mode, snap_name=None, quiet=False): '''Create a copy of the VM''' return self.vmrun('clone', self.vmx_file, dest_vmx, mode, snap_name, quiet=quiet) ############################################################################ # RECORD/REPLAY COMMANDS PARAMETERS DESCRIPTION # ---------------------- ---------- ----------- # beginRecording Path to vmx file Begin recording a VM # Snapshot name # # endRecording Path to vmx file End recording a VM # # beginReplay Path to vmx file Begin replaying a VM # Snapshot name # # endReplay Path to vmx file End replaying a VM def beginRecording(self, snap_name, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Begin recording a VM''' return self.vmrun('beginRecording', self.vmx_file, snap_name, quiet=quiet) def endRecording(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''End recording a VM''' return self.vmrun('endRecording', self.vmx_file, quiet=quiet) def beginReplay(self, snap_name, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Begin replaying a VM''' return self.vmrun('beginReplay', self.vmx_file, snap_name, quiet=quiet) def endReplay(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''End replaying a VM''' return self.vmrun('endReplay', self.vmx_file, quiet=quiet) ############################################################################ # VPROBE COMMANDS PARAMETERS DESCRIPTION # --------------- ---------- ----------- # vprobeVersion Path to vmx file List VP version # # vprobeLoad Path to vmx file Load VP script # 'VP script text' # # vprobeLoadFile Path to vmx file Load VP file # Path to VP file # # vprobeReset Path to vmx file Disable all vprobes # # vprobeListProbes Path to vmx file List probes # # vprobeListGlobals Path to vmx file List global variables def vprobeVersion(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''List VP version''' return self.vmrun('vprobeVersion', self.vmx_file, quiet=quiet) def vprobeLoad(self, script, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Load VP script''' return self.vmrun('vprobeLoad', self.vmx_file, script, quiet=quiet) def vprobeLoadFile(self, vp, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Load VP file''' return self.vmrun('vprobeLoadFile', self.vmx_file, vp, quiet=quiet) def vprobeReset(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''Disable all vprobes''' return self.vmrun('vprobeReset', self.vmx_file, quiet=quiet) def vprobeListProbes(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''List probes''' return self.vmrun('vprobeListProbes', self.vmx_file, quiet=quiet) def vprobeListGlobals(self, quiet=False): # unavailable in VMware Fusion 10 (OS X)? '''List global variables''' return self.vmrun('vprobeListGlobals', self.vmx_file, quiet=quiet) ############################################################################ def installedTools(self, quiet=False): state = self.checkToolsState(quiet=quiet) return state in ('installed', 'running')