async def _execute(self, subcommand, args, timeout=120, log_level=logging.INFO): if self.host_type is None: await self.check_vmware_version() vmrun_path = self.vmrun_path if not vmrun_path: vmrun_path = self.find_vmrun() command = [vmrun_path, "-T", self.host_type, subcommand] command.extend(args) command_string = " ".join([shlex_quote(c) for c in command]) log.log(log_level, "Executing vmrun with command: {}".format(command_string)) try: process = await asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) except (OSError, subprocess.SubprocessError) as e: raise VMwareError("Could not execute vmrun: {}".format(e)) try: stdout_data, _ = await asyncio.wait_for(process.communicate(), timeout=timeout) except asyncio.TimeoutError: raise VMwareError("vmrun has timed out after {} seconds!\nTry to run {} in a terminal to see more details.\n\nMake sure GNS3 and VMware run under the same user and whitelist vmrun.exe in your antivirus.".format(timeout, command_string)) if process.returncode: # vmrun print errors on stdout vmrun_error = stdout_data.decode("utf-8", errors="ignore") raise VMwareError("vmrun has returned an error: {}\nTry to run {} in a terminal to see more details.\nAnd make sure GNS3 and VMware run under the same user.".format(vmrun_error, command_string)) return stdout_data.decode("utf-8", errors="ignore").splitlines()
async def check_vmrun_version(self, minimum_required_version="1.13.0"): """ Checks the vmrun version. VMware VIX library version must be at least >= 1.13 by default VIX 1.13 was the release for VMware Fusion 6, Workstation 10, and Player 6. VIX 1.14 was the release for VMware Fusion 7, Workstation 11 and Player 7. VIX 1.15 was the release for VMware Fusion 8, Workstation Pro 12 and Workstation Player 12. :param required_version: required vmrun version number """ vmrun_path = self.vmrun_path if not vmrun_path: vmrun_path = self.find_vmrun() try: output = await subprocess_check_output(vmrun_path) match = re.search(r"vmrun version ([0-9\.]+)", output) version = None if match: version = match.group(1) log.debug("VMware vmrun version {} detected, minimum required: {}".format(version, minimum_required_version)) if parse_version(version) < parse_version(minimum_required_version): raise VMwareError("VMware vmrun executable version must be >= version {}".format(minimum_required_version)) if version is None: log.warning("Could not find VMware vmrun version. Output: {}".format(output)) raise VMwareError("Could not find VMware vmrun version. Output: {}".format(output)) except (OSError, subprocess.SubprocessError) as e: log.error("Error while looking for the VMware vmrun version: {}".format(e)) raise VMwareError("Error while looking for the VMware vmrun version: {}".format(e))
async def check_vmware_version(self): """ Check VMware version """ if sys.platform.startswith("win"): # look for vmrun.exe using the directory listed in the registry ws_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Workstation") if ws_version is None: player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player") if player_version: log.debug("VMware Player version {} detected".format(player_version)) await self._check_vmware_player_requirements(player_version) else: log.warning("Could not find VMware version") self._host_type = "ws" else: log.debug("VMware Workstation version {} detected".format(ws_version)) await self._check_vmware_workstation_requirements(ws_version) else: if sys.platform.startswith("darwin"): if not os.path.isdir("/Applications/VMware Fusion.app"): raise VMwareError("VMware Fusion is not installed in the standard location /Applications/VMware Fusion.app") self._host_type = "fusion" return # FIXME: no version checking on Mac OS X but we support all versions of fusion vmware_path = VMware._get_linux_vmware_binary() if vmware_path is None: raise VMwareError("VMware is not installed (vmware or vmplayer executable could not be found in $PATH)") try: output = await subprocess_check_output(vmware_path, "-v") match = re.search(r"VMware Workstation ([0-9]+)\.", output) version = None if match: # VMware Workstation has been detected version = match.group(1) log.debug("VMware Workstation version {} detected".format(version)) await self._check_vmware_workstation_requirements(version) match = re.search(r"VMware Player ([0-9]+)\.", output) if match: # VMware Player has been detected version = match.group(1) log.debug("VMware Player version {} detected".format(version)) await self._check_vmware_player_requirements(version) if version is None: log.warning("Could not find VMware version. Output of VMware: {}".format(output)) raise VMwareError("Could not find VMware version. Output of VMware: {}".format(output)) except (OSError, subprocess.SubprocessError) as e: log.error("Error while looking for the VMware version: {}".format(e)) raise VMwareError("Error while looking for the VMware version: {}".format(e))
async def remove_from_vmware_inventory(self, vmx_path): """ Removes a linked clone from the VMware inventory file. :param vmx_path: path of the linked clone VMX file """ async with self._vmware_inventory_lock: inventory_path = self.get_vmware_inventory_path() if os.path.exists(inventory_path): try: inventory_pairs = self.parse_vmware_file(inventory_path) except OSError as e: log.warning('Could not read VMware inventory file "{}": {}'.format(inventory_path, e)) return vmlist_entry = None for name, value in inventory_pairs.items(): if value == vmx_path: vmlist_entry = name.split(".", 1)[0] break if vmlist_entry is not None: for name in inventory_pairs.copy().keys(): if name.startswith(vmlist_entry): del inventory_pairs[name] try: self.write_vmware_file(inventory_path, inventory_pairs) except OSError as e: raise VMwareError('Could not write VMware inventory file "{}": {}'.format(inventory_path, e))
def allocate_vmnet(self): if not self._vmnets: raise VMwareError( "No VMnet interface available between vmnet{} and vmnet{}. Go to preferences VMware / Network / Configure to add more interfaces." .format(self._vmnet_start_range, self._vmnet_end_range)) return self._vmnets.pop(0)
def _check_vmware_workstation_requirements(self, ws_version): """ Check minimum requirements to use VMware Workstation. VIX 1.13 was the release for Workstation 10. VIX 1.14 was the release for Workstation 11. VIX 1.15 was the release for Workstation Pro 12. :param ws_version: VMware Workstation major version. """ ws_version = int(ws_version) if ws_version < 10: raise VMwareError( "Using VMware Workstation requires version 10 or above") elif ws_version == 10: yield from self.check_vmrun_version( minimum_required_version="1.13.0") elif ws_version == 11: yield from self.check_vmrun_version( minimum_required_version="1.14.0") elif ws_version >= 12: yield from self.check_vmrun_version( minimum_required_version="1.15.0") self._host_type = "ws"
def _check_vmware_player_requirements(self, player_version): """ Check minimum requirements to use VMware Player. VIX 1.13 was the release for Player 6. VIX 1.14 was the release for Player 7. VIX 1.15 was the release for Workstation Player 12. :param player_version: VMware Player major version. """ player_version = int(player_version) if player_version < 6: raise VMwareError( "Using VMware Player requires version 6 or above") elif player_version == 6: yield from self.check_vmrun_version( minimum_required_version="1.13.0") elif player_version == 7: yield from self.check_vmrun_version( minimum_required_version="1.14.0") elif player_version >= 12: yield from self.check_vmrun_version( minimum_required_version="1.15.0") self._host_type = "player"
def _get_vmnet_interfaces(): if sys.platform.startswith("win"): return VMware._get_vmnet_interfaces_registry() elif sys.platform.startswith("darwin"): vmware_networking_file = "/Library/Preferences/VMware Fusion/networking" else: # location on Linux vmware_networking_file = "/etc/vmware/networking" vmnet_interfaces = {} try: with open(vmware_networking_file, "r", encoding="utf-8") as f: for line in f.read().splitlines(): match = re.search(r"VNET_([0-9]+)_VIRTUAL_ADAPTER", line) if match: vmnet = "vmnet{}".format(match.group(1)) if vmnet not in ("vmnet0", "vmnet1", "vmnet8"): vmnet_interfaces[vmnet] = {} with open(vmware_networking_file, "r", encoding="utf-8") as f: for line in f.read().splitlines(): match = re.search(r"VNET_([0-9]+)_HOSTONLY_SUBNET\s+(.*)", line) if match: vmnet = "vmnet{}".format(match.group(1)) if vmnet in vmnet_interfaces.keys(): vmnet_interfaces[vmnet]["subnet"] = match.group(2) match = re.search(r"VNET_([0-9]+)_HOSTONLY_NETMASK\s+(.*)", line) if match: vmnet = "vmnet{}".format(match.group(1)) if vmnet in vmnet_interfaces.keys(): vmnet_interfaces[vmnet]["netmask"] = match.group(2) except OSError as e: raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e)) return vmnet_interfaces
def find_vmrun(self): """ Searches for vmrun. :returns: path to vmrun """ # look for vmrun vmrun_path = self.config.get_section_config("VMware").get("vmrun_path") if not vmrun_path: if sys.platform.startswith("win"): vmrun_path = shutil.which("vmrun") if vmrun_path is None: # look for vmrun.exe using the VMware Workstation directory listed in the registry vmrun_path = self._find_vmrun_registry( r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Workstation" ) if vmrun_path is None: # look for vmrun.exe using the VIX directory listed in the registry vmrun_path = self._find_vmrun_registry( r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware VIX") elif sys.platform.startswith("darwin"): vmrun_path = "/Applications/VMware Fusion.app/Contents/Library/vmrun" else: vmrun_path = "vmrun" if vmrun_path and not os.path.isabs(vmrun_path): vmrun_path = shutil.which(vmrun_path) if not vmrun_path: raise VMwareError( "Could not find VMware vmrun, please make sure it is installed" ) if not os.path.isfile(vmrun_path): raise VMwareError("vmrun {} is not accessible".format(vmrun_path)) if not os.access(vmrun_path, os.X_OK): raise VMwareError("vmrun is not executable") if os.path.basename(vmrun_path).lower() not in ["vmrun", "vmrun.exe"]: raise VMwareError("Invalid vmrun executable name {}".format( os.path.basename(vmrun_path))) self._vmrun_path = vmrun_path return vmrun_path
async def list_vms(self): """ Gets VMware VM list. """ # check for the right VMware version await self.check_vmware_version() vmware_vms = [] inventory_path = self.get_vmware_inventory_path() if os.path.exists(inventory_path) and self.host_type != "player": # inventory may exist for VMware player if VMware workstation has been previously installed vmware_vms = self._get_vms_from_inventory(inventory_path) if not vmware_vms: # backup methods when no VMware inventory file exists or for VMware player which has no inventory file vmware_preferences_path = self.get_vmware_preferences_path() pairs = {} if os.path.exists(vmware_preferences_path): # the default vm path may be present in VMware preferences file. try: pairs = self.parse_vmware_file(vmware_preferences_path) except OSError as e: log.warning('Could not read VMware preferences file "{}": {}'.format(vmware_preferences_path, e)) if "prefvmx.defaultvmpath" in pairs: default_vm_path = pairs["prefvmx.defaultvmpath"] if not os.path.isdir(default_vm_path): raise VMwareError('Could not find or access the default VM directory: "{default_vm_path}". Please change "prefvmx.defaultvmpath={default_vm_path}" in "{vmware_preferences_path}"'.format(default_vm_path=default_vm_path, vmware_preferences_path=vmware_preferences_path)) vmware_vms = self._get_vms_from_directory(default_vm_path) if not vmware_vms: # the default vm path is not in the VMware preferences file or that directory is empty # let's search the default locations for VMs for default_vm_path in self.get_vmware_default_vm_paths(): if os.path.isdir(default_vm_path): vmware_vms.extend(self._get_vms_from_directory(default_vm_path)) if not vmware_vms: log.warning("Could not find any VMware VM in default locations") # look for VMX paths in the preferences file in case not all VMs are in a default directory for key, value in pairs.items(): m = re.match(r'pref.mruVM(\d+)\.filename', key) if m: display_name = "pref.mruVM{}.displayName".format(m.group(1)) if display_name in pairs: found = False for vmware_vm in vmware_vms: if vmware_vm["vmname"] == display_name: found = True if found is False: vmware_vms.append({"vmname": pairs[display_name], "vmx_path": value}) return vmware_vms
def list_vms(self): """ Gets VMware VM list. """ # check for the right VMware version yield from self.check_vmware_version() inventory_path = self.get_vmware_inventory_path() if os.path.exists(inventory_path) and self.host_type != "player": # inventory may exist for VMware player if VMware workstation has been previously installed return self._get_vms_from_inventory(inventory_path) else: # VMware player has no inventory file, let's search the default location for VMs vmware_preferences_path = self.get_vmware_preferences_path() default_vm_path = self.get_vmware_default_vm_path() pairs = {} if os.path.exists(vmware_preferences_path): # the default vm path may be present in VMware preferences file. try: pairs = self.parse_vmware_file(vmware_preferences_path) except OSError as e: log.warning( 'Could not read VMware preferences file "{}": {}'. format(vmware_preferences_path, e)) if "prefvmx.defaultvmpath" in pairs: default_vm_path = pairs["prefvmx.defaultvmpath"] if not os.path.isdir(default_vm_path): raise VMwareError( 'Could not find the default VM directory: "{}". Due to limitation of the free version of VMware Workstation you need to import the GNS3 VM in the default location.' .format(default_vm_path)) vmware_vms = self._get_vms_from_directory(default_vm_path) # looks for VMX paths in the preferences file in case not all VMs are in the default directory for key, value in pairs.items(): m = re.match(r'pref.mruVM(\d+)\.filename', key) if m: display_name = "pref.mruVM{}.displayName".format( m.group(1)) if display_name in pairs: found = False for vmware_vm in vmware_vms: if vmware_vm["vmname"] == display_name: found = True if found is False: vmware_vms.append({ "vmname": pairs[display_name], "vmx_path": value }) return vmware_vms
def _get_vmnet_interfaces_registry(): import winreg vmnet_interfaces = [] regkey = r"SOFTWARE\Wow6432Node\VMware, Inc.\VMnetLib\VMnetConfig" try: hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, regkey) for index in range(winreg.QueryInfoKey(hkey)[0]): vmnet = winreg.EnumKey(hkey, index) hkeyvmnet = winreg.OpenKey(hkey, vmnet) if winreg.QueryInfoKey(hkeyvmnet)[1]: # the vmnet has not been configure if the key has no values vmnet = vmnet.replace("vm", "VM") if vmnet not in ("VMnet0", "VMnet1", "VMnet8"): vmnet_interfaces.append(vmnet) winreg.CloseKey(hkeyvmnet) winreg.CloseKey(hkey) except OSError as e: raise VMwareError("Could not read registry key {}: {}".format(regkey, e)) return vmnet_interfaces
def _get_vmnet_interfaces(): if sys.platform.startswith("win"): return VMware._get_vmnet_interfaces_registry() elif sys.platform.startswith("darwin"): vmware_networking_file = "/Library/Preferences/VMware Fusion/networking" else: # location on Linux vmware_networking_file = "/etc/vmware/networking" vmnet_interfaces = [] try: with open(vmware_networking_file, "r", encoding="utf-8") as f: for line in f.read().splitlines(): match = re.search("VNET_([0-9]+)_VIRTUAL_ADAPTER", line) if match: vmnet = "vmnet{}".format(match.group(1)) if vmnet not in ("vmnet1", "vmnet8"): vmnet_interfaces.append(vmnet) except OSError as e: raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e)) return vmnet_interfaces