Rename modules => hypervisor

This commit is contained in:
Julien Duponchelle committed 2016-03-08 16:12:46 +01:00
1 parent 6fa2491255
commit 3296b97f59
145 files changed
+308 -307

No files matched your search

+648
View File
@@ -0,0 +1,648 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
VMware player/workstation server module.
"""
import os
import sys
import re
import shutil
import asyncio
import subprocess
import logging
import codecs
from collections import OrderedDict
from gns3server.utils.interfaces import interfaces
from gns3server.utils.asyncio import subprocess_check_output
from pkg_resources import parse_version
log = logging.getLogger(__name__)
from ..base_manager import BaseManager
from .vmware_vm import VMwareVM
from .vmware_error import VMwareError
from .nio_vmnet import NIOVMNET
class VMware(BaseManager):
_VM_CLASS = VMwareVM
def __init__(self):
super().__init__()
self._execute_lock = asyncio.Lock()
self._vmware_inventory_lock = asyncio.Lock()
self._vmrun_path = None
self._vmnets = []
self._vmnet_start_range = 2
if sys.platform.startswith("win"):
self._vmnet_end_range = 19
else:
self._vmnet_end_range = 255
@property
def vmrun_path(self):
"""
Returns the path vmrun utility.
:returns: path
"""
return self._vmrun_path
@staticmethod
def _find_vmrun_registry(regkey):
import winreg
try:
# default path not used, let's look in the registry
hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, regkey)
install_path, _ = winreg.QueryValueEx(hkey, "InstallPath")
vmrun_path = os.path.join(install_path, "vmrun.exe")
winreg.CloseKey(hkey)
if os.path.exists(vmrun_path):
return vmrun_path
except OSError:
pass
return None
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 = shutil.which("vmrun")
if not vmrun_path:
raise VMwareError("Could not find vmrun")
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) 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
@staticmethod
def _find_vmware_version_registry(regkey):
import winreg
version = None
try:
# default path not used, let's look in the registry
hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, regkey)
version, _ = winreg.QueryValueEx(hkey, "ProductVersion")
winreg.CloseKey(hkey)
except OSError:
pass
if version is not None:
match = re.search("([0-9]+)\.", version)
if match:
version = match.group(1)
return version
@asyncio.coroutine
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))
if int(player_version) < 6:
raise VMwareError("Using VMware Player requires version 6 or above")
else:
log.warning("Could not find VMware version")
else:
log.debug("VMware Workstation version {} detected".format(ws_version))
if int(ws_version) < 10:
raise VMwareError("Using VMware Workstation requires version 10 or above")
return
else:
if sys.platform.startswith("darwin"):
if not os.path.isdir("/Applications/VMware Fusion.app"):
raise VMwareError("VMware Fusion is not installed")
return # FIXME: no version checking on Mac OS X
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 = yield from subprocess_check_output(vmware_path, "-v")
match = re.search("VMware Workstation ([0-9]+)\.", output)
version = None
if match:
version = match.group(1)
log.debug("VMware Workstation version {} detected".format(version))
if int(version) < 10:
raise VMwareError("Using VMware Workstation requires version 10 or above")
match = re.search("VMware Player ([0-9]+)\.", output)
if match:
version = match.group(1)
log.debug("VMware Player version {} detected".format(version))
if int(version) < 6:
raise VMwareError("Using VMware Player requires version 6 or above")
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))
@staticmethod
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 ("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
@staticmethod
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
@staticmethod
def _get_vmnet_interfaces_ubridge():
vmnet_interfaces = []
for interface in interfaces():
if sys.platform.startswith("win"):
if "netcard" in interface:
windows_name = interface["netcard"]
else:
windows_name = interface["name"]
match = re.search("(VMnet[0-9]+)", windows_name)
if match:
vmnet = match.group(1)
if vmnet not in ("VMnet1", "VMnet8"):
vmnet_interfaces.append(vmnet)
elif interface["name"].startswith("vmnet"):
vmnet = interface["name"]
if vmnet not in ("vmnet1", "vmnet8"):
vmnet_interfaces.append(interface["name"])
return vmnet_interfaces
def is_managed_vmnet(self, vmnet):
self._vmnet_start_range = self.config.get_section_config("VMware").getint("vmnet_start_range", self._vmnet_start_range)
self._vmnet_end_range = self.config.get_section_config("VMware").getint("vmnet_end_range", self._vmnet_end_range)
match = re.search("vmnet([0-9]+)$", vmnet, re.IGNORECASE)
if match:
vmnet_number = match.group(1)
if self._vmnet_start_range <= int(vmnet_number) <= self._vmnet_end_range:
return True
return False
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 refresh_vmnet_list(self, ubridge=True):
if ubridge:
# VMnet host adapters must be present when uBridge is used
vmnet_interfaces = self._get_vmnet_interfaces_ubridge()
else:
vmnet_interfaces = self._get_vmnet_interfaces()
# remove vmnets already in use
for vm in self._vms.values():
for used_vmnet in vm.vmnets:
if used_vmnet in vmnet_interfaces:
log.debug("{} is already in use".format(used_vmnet))
vmnet_interfaces.remove(used_vmnet)
# remove vmnets that are not managed
for vmnet in vmnet_interfaces.copy():
if vmnet in vmnet_interfaces and self.is_managed_vmnet(vmnet) is False:
vmnet_interfaces.remove(vmnet)
self._vmnets = vmnet_interfaces
def create_nio(self, executable, nio_settings):
if nio_settings["type"] == "nio_vmnet":
return NIOVMNET(nio_settings["vmnet"])
else:
return super().create_nio(None, nio_settings)
@property
def host_type(self):
"""
Returns the VMware host type.
player = VMware player
ws = VMware Workstation
fusion = VMware Fusion
:returns: host type (string)
"""
if sys.platform.startswith("darwin"):
host_type = "fusion"
else:
host_type = self.config.get_section_config("VMware").get("host_type", "ws")
return host_type
@asyncio.coroutine
def execute(self, subcommand, args, timeout=120, host_type=None):
with (yield from self._execute_lock):
vmrun_path = self.vmrun_path
if not vmrun_path:
vmrun_path = self.find_vmrun()
if host_type is None:
host_type = self.host_type
command = [vmrun_path, "-T", host_type, subcommand]
command.extend(args)
command_string = " ".join(command)
log.info("Executing vmrun with command: {}".format(command_string))
try:
process = yield from 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, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
except asyncio.TimeoutError:
raise VMwareError("vmrun has timed out after {} seconds!".format(timeout))
if process.returncode:
# vmrun print errors on stdout
vmrun_error = stdout_data.decode("utf-8", errors="ignore")
raise VMwareError("vmrun has returned an error: {}".format(vmrun_error))
return stdout_data.decode("utf-8", errors="ignore").splitlines()
@asyncio.coroutine
def check_vmrun_version(self):
with (yield from self._execute_lock):
vmrun_path = self.vmrun_path
if not vmrun_path:
vmrun_path = self.find_vmrun()
try:
output = yield from subprocess_check_output(vmrun_path)
match = re.search("vmrun version ([0-9\.]+)", output)
version = None
if match:
version = match.group(1)
log.debug("VMware vmrun version {} detected".format(version))
if parse_version(version) < parse_version("1.13"):
# VMware VIX library version must be at least >= 1.13
raise VMwareError("VMware vmrun executable version must be >= version 1.13")
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))
@asyncio.coroutine
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
"""
with (yield from 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))
@staticmethod
def parse_vmware_file(path):
"""
Parses a VMware file (VMX, preferences or inventory).
:param path: path to the VMware file
:returns: dict
"""
pairs = OrderedDict()
encoding = "utf-8"
# get the first line to read the .encoding value
with open(path, "rb") as f:
line = f.readline().decode(encoding, errors="ignore")
if line.startswith("#!"):
# skip the shebang
line = f.readline().decode(encoding, errors="ignore")
try:
key, value = line.split('=', 1)
if key.strip().lower() == ".encoding":
file_encoding = value.strip('" ')
try:
codecs.lookup(file_encoding)
encoding = file_encoding
except LookupError:
log.warning("Invalid file encoding detected in '{}': {}".format(path, file_encoding))
except ValueError:
log.warning("Couldn't find file encoding in {}, using {}...".format(path, encoding))
# read the file with the correct encoding
with open(path, encoding=encoding, errors="ignore") as f:
for line in f.read().splitlines():
try:
key, value = line.split('=', 1)
pairs[key.strip().lower()] = value.strip('" ')
except ValueError:
continue
return pairs
@staticmethod
def write_vmware_file(path, pairs):
"""
Write a VMware file (excepting VMX file).
:param path: path to the VMware file
:param pairs: settings to write
"""
encoding = "utf-8"
if ".encoding" in pairs:
file_encoding = pairs[".encoding"]
try:
codecs.lookup(file_encoding)
encoding = file_encoding
except LookupError:
log.warning("Invalid file encoding detected in '{}': {}".format(path, file_encoding))
with open(path, "w", encoding=encoding, errors="ignore") as f:
for key, value in pairs.items():
entry = '{} = "{}"\n'.format(key, value)
f.write(entry)
@staticmethod
def write_vmx_file(path, pairs):
"""
Write a VMware VMX file.
:param path: path to the VMX file
:param pairs: settings to write
"""
encoding = "utf-8"
if ".encoding" in pairs:
file_encoding = pairs[".encoding"]
try:
codecs.lookup(file_encoding)
encoding = file_encoding
except LookupError:
log.warning("Invalid file encoding detected in '{}': {}".format(path, file_encoding))
with open(path, "w", encoding=encoding, errors="ignore") as f:
if sys.platform.startswith("linux"):
# write the shebang on the first line on Linux
vmware_path = VMware._get_linux_vmware_binary()
if vmware_path:
f.write("#!{}\n".format(vmware_path))
for key, value in pairs.items():
entry = '{} = "{}"\n'.format(key, value)
f.write(entry)
def _get_vms_from_inventory(self, inventory_path):
"""
Searches for VMs by parsing a VMware inventory file.
:param inventory_path: path to the inventory file
:returns: list of VMs
"""
vm_entries = {}
vms = []
try:
log.debug('Reading VMware inventory file "{}"'.format(inventory_path))
pairs = self.parse_vmware_file(inventory_path)
for key, value in pairs.items():
if key.startswith("vmlist"):
try:
vm_entry, variable_name = key.split('.', 1)
except ValueError:
continue
if vm_entry not in vm_entries:
vm_entries[vm_entry] = {}
vm_entries[vm_entry][variable_name.strip()] = value
except OSError as e:
log.warning("Could not read VMware inventory file {}: {}".format(inventory_path, e))
for vm_settings in vm_entries.values():
if "displayname" in vm_settings and "config" in vm_settings:
log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayname"], vm_settings["config"]))
vms.append({"vmname": vm_settings["displayname"], "vmx_path": vm_settings["config"]})
return vms
def _get_vms_from_directory(self, directory):
"""
Searches for VMs in a given directory.
:param directory: path to the directory
:returns: list of VMs
"""
vms = []
for path, _, filenames in os.walk(directory):
for filename in filenames:
if os.path.splitext(filename)[1] == ".vmx":
vmx_path = os.path.join(path, filename)
log.debug('Reading VMware VMX file "{}"'.format(vmx_path))
try:
pairs = self.parse_vmware_file(vmx_path)
if "displayname" in pairs:
log.debug('Found VM named "{}"'.format(pairs["displayname"]))
vms.append({"vmname": pairs["displayname"], "vmx_path": vmx_path})
except OSError as e:
log.warning('Could not read VMware VMX file "{}": {}'.format(vmx_path, e))
continue
return vms
@staticmethod
def get_vmware_inventory_path():
"""
Returns VMware inventory file path.
:returns: path to the inventory file
"""
if sys.platform.startswith("win"):
return os.path.expandvars(r"%APPDATA%\Vmware\Inventory.vmls")
elif sys.platform.startswith("darwin"):
return os.path.expanduser("~/Library/Application Support/VMware Fusion/vmInventory")
else:
return os.path.expanduser("~/.vmware/inventory.vmls")
@staticmethod
def get_vmware_preferences_path():
"""
Returns VMware preferences file path.
:returns: path to the preferences file
"""
if sys.platform.startswith("win"):
return os.path.expandvars(r"%APPDATA%\VMware\preferences.ini")
elif sys.platform.startswith("darwin"):
return os.path.expanduser("~/Library/Preferences/VMware Fusion/preferences")
else:
return os.path.expanduser("~/.vmware/preferences")
@staticmethod
def get_vmware_default_vm_path():
"""
Returns VMware default VM directory path.
:returns: path to the default VM directory
"""
if sys.platform.startswith("win"):
from win32com.shell import shell, shellcon
documents_folder = shell.SHGetSpecialFolderPath(None, shellcon.CSIDL_PERSONAL)
windows_type = sys.getwindowsversion().product_type
if windows_type == 2 or windows_type == 3:
return '{}\My Virtual Machines'.format(documents_folder)
else:
return '{}\Virtual Machines'.format(documents_folder)
elif sys.platform.startswith("darwin"):
return os.path.expanduser("~/Documents/Virtual Machines.localized")
else:
return os.path.expanduser("~/vmware")
@asyncio.coroutine
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):
# FIXME: inventory may exist if VMware workstation has not been fully uninstalled, therefore VMware player VMs are not searched
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()
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)
if "prefvmx.defaultvmpath" in pairs:
default_vm_path = pairs["prefvmx.defaultvmpath"]
except OSError as e:
log.warning('Could not read VMware preferences file "{}": {}'.format(vmware_preferences_path, e))
if not os.path.isdir(default_vm_path):
raise VMwareError('Could not find the default VM directory: "{}"'.format(default_vm_path))
return self._get_vms_from_directory(default_vm_path)
@staticmethod
def _get_linux_vmware_binary():
"""
Return the path of the vmware binary on Linux or None
"""
path = shutil.which("vmware")
if path is None:
path = shutil.which("vmplayer")
return path
+53
View File
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for VMnet NIOs.
"""
from ..nios.nio import NIO
class NIOVMNET(NIO):
"""
VMnet NIO.
"""
def __init__(self, vmnet):
super().__init__()
self._vmnet = vmnet
@property
def vmnet(self):
"""
Returns vmnet interface used by this NIO.
:returns: vmnet interface name
"""
return self._vmnet
def __str__(self):
return "NIO VMNET"
def __json__(self):
return {"type": "nio_vmnet",
"vmnet": self._vmnet}
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for the VMware module.
"""
from ..vm_error import VMError
class VMwareError(VMError):
pass
+985
View File
@@ -0,0 +1,985 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
VMware VM instance.
"""
import sys
import os
import socket
import asyncio
import tempfile
from gns3server.utils.telnet_server import TelnetServer
from gns3server.utils.interfaces import get_windows_interfaces
from gns3server.utils.asyncio import wait_for_file_creation, wait_for_named_pipe_creation
from collections import OrderedDict
from .vmware_error import VMwareError
from ..nios.nio_udp import NIOUDP
from ..nios.nio_nat import NIONAT
from .nio_vmnet import NIOVMNET
from ..adapters.ethernet_adapter import EthernetAdapter
from ..base_vm import BaseVM
if sys.platform.startswith('win'):
import msvcrt
import win32file
import logging
log = logging.getLogger(__name__)
class VMwareVM(BaseVM):
"""
VMware VM implementation.
"""
def __init__(self, name, vm_id, project, manager, vmx_path, linked_clone, console=None):
super().__init__(name, vm_id, project, manager, console=console)
self._linked_clone = linked_clone
self._vmx_pairs = OrderedDict()
self._telnet_server_thread = None
self._serial_pipe = None
self._vmnets = []
self._maximum_adapters = 10
self._started = False
self._closed = False
# VMware VM settings
self._headless = False
self._vmx_path = vmx_path
self._enable_remote_console = False
self._acpi_shutdown = False
self._adapters = 0
self._ethernet_adapters = {}
self._adapter_type = "e1000"
self._use_ubridge = True
self._use_any_adapter = False
if not os.path.exists(vmx_path):
raise VMwareError('VMware VM "{name}" [{id}]: could not find VMX file "{vmx_path}"'.format(name=name, id=vm_id, vmx_path=vmx_path))
def __json__(self):
json = {"name": self.name,
"vm_id": self.id,
"console": self.console,
"project_id": self.project.id,
"vmx_path": self.vmx_path,
"headless": self.headless,
"acpi_shutdown": self.acpi_shutdown,
"enable_remote_console": self.enable_remote_console,
"adapters": self._adapters,
"adapter_type": self.adapter_type,
"use_ubridge": self.use_ubridge,
"use_any_adapter": self.use_any_adapter,
"vm_directory": self.working_dir}
return json
@property
def vmnets(self):
return self._vmnets
@asyncio.coroutine
def _control_vm(self, subcommand, *additional_args):
args = [self._vmx_path]
args.extend(additional_args)
result = yield from self.manager.execute(subcommand, args)
log.debug("Control VM '{}' result: {}".format(subcommand, result))
return result
def _read_vmx_file(self):
"""
Reads from the VMware VMX file corresponding to this VM.
"""
try:
self._vmx_pairs = self.manager.parse_vmware_file(self._vmx_path)
except OSError as e:
raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e))
def _write_vmx_file(self):
"""
Writes pairs to the VMware VMX file corresponding to this VM.
"""
try:
self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs)
except OSError as e:
raise VMwareError('Could not write VMware VMX file "{}": {}'.format(self._vmx_path, e))
@asyncio.coroutine
def is_running(self):
result = yield from self.manager.execute("list", [])
if self._vmx_path in result:
return True
return False
@asyncio.coroutine
def create(self):
"""
Creates this VM and handle linked clones.
"""
yield from self.manager.check_vmrun_version()
if self._linked_clone and not os.path.exists(os.path.join(self.working_dir, os.path.basename(self._vmx_path))):
# create the base snapshot for linked clones
base_snapshot_name = "GNS3 Linked Base for clones"
vmsd_path = os.path.splitext(self._vmx_path)[0] + ".vmsd"
if not os.path.exists(vmsd_path):
raise VMwareError("{} doesn't not exist".format(vmsd_path))
try:
vmsd_pairs = self.manager.parse_vmware_file(vmsd_path)
except OSError as e:
raise VMwareError('Could not read VMware VMSD file "{}": {}'.format(vmsd_path, e))
gns3_snapshot_exists = False
for value in vmsd_pairs.values():
if value == base_snapshot_name:
gns3_snapshot_exists = True
break
if not gns3_snapshot_exists:
log.info("Creating snapshot '{}'".format(base_snapshot_name))
yield from self._control_vm("snapshot", base_snapshot_name)
# create the linked clone based on the base snapshot
new_vmx_path = os.path.join(self.working_dir, self.name + ".vmx")
yield from self._control_vm("clone",
new_vmx_path,
"linked",
"-snapshot={}".format(base_snapshot_name),
"-cloneName={}".format(self.name))
try:
vmsd_pairs = self.manager.parse_vmware_file(vmsd_path)
except OSError as e:
raise VMwareError('Could not read VMware VMSD file "{}": {}'.format(vmsd_path, e))
snapshot_name = None
for name, value in vmsd_pairs.items():
if value == base_snapshot_name:
snapshot_name = name.split(".", 1)[0]
break
if snapshot_name is None:
raise VMwareError("Could not find the linked base snapshot in {}".format(vmsd_path))
num_clones_entry = "{}.numClones".format(snapshot_name)
if num_clones_entry in vmsd_pairs:
try:
nb_of_clones = int(vmsd_pairs[num_clones_entry])
except ValueError:
raise VMwareError("Value of {} in {} is not a number".format(num_clones_entry, vmsd_path))
vmsd_pairs[num_clones_entry] = str(nb_of_clones - 1)
for clone_nb in range(0, nb_of_clones):
clone_entry = "{}.clone{}".format(snapshot_name, clone_nb)
if clone_entry in vmsd_pairs:
del vmsd_pairs[clone_entry]
try:
self.manager.write_vmware_file(vmsd_path, vmsd_pairs)
except OSError as e:
raise VMwareError('Could not write VMware VMSD file "{}": {}'.format(vmsd_path, e))
# update the VMX file path
self._vmx_path = new_vmx_path
def _get_vmx_setting(self, name, value=None):
if name in self._vmx_pairs:
if value is not None:
if self._vmx_pairs[name] == value:
return value
else:
return self._vmx_pairs[name]
return None
def _set_network_options(self):
"""
Set up VMware networking.
"""
# first some sanity checks
for adapter_number in range(0, self._adapters):
# we want the vmnet interface to be connected when starting the VM
connected = "ethernet{}.startConnected".format(adapter_number)
if self._get_vmx_setting(connected):
del self._vmx_pairs[connected]
# check for adapter type
if self._adapter_type != "default":
adapter_type = "ethernet{}.virtualdev".format(adapter_number)
if adapter_type in self._vmx_pairs and self._vmx_pairs[adapter_type] != self._adapter_type:
raise VMwareError("Existing VMware network adapter {} is not of type {}, please fix or set adapter type to default in GNS3".format(adapter_number,
self._adapter_type))
# # check if any vmnet interface managed by GNS3 is being used on existing VMware adapters
# if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"):
# connection_type = "ethernet{}.connectiontype".format(adapter_number)
# if connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("hostonly", "custom"):
# vnet = "ethernet{}.vnet".format(adapter_number)
# if vnet in self._vmx_pairs:
# vmnet = os.path.basename(self._vmx_pairs[vnet])
# #nio = self._ethernet_adapters[adapter_number].get_nio(0)
# if self.manager.is_managed_vmnet(vmnet):
# raise VMwareError("Network adapter {} is already associated with VMnet interface {} which is managed by GNS3, please remove".format(adapter_number, vmnet))
# then configure VMware network adapters
self.manager.refresh_vmnet_list(ubridge=self._use_ubridge)
for adapter_number in range(0, self._adapters):
# add/update the interface
ethernet_adapter = {"ethernet{}.present".format(adapter_number): "TRUE",
"ethernet{}.addresstype".format(adapter_number): "generated",
"ethernet{}.generatedaddressoffset".format(adapter_number): "0"}
self._vmx_pairs.update(ethernet_adapter)
if self._adapter_type != "default":
self._vmx_pairs["ethernet{}.virtualdev".format(adapter_number)] = self._adapter_type
connection_type = "ethernet{}.connectiontype".format(adapter_number)
if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"):
continue
self._vmx_pairs["ethernet{}.connectiontype".format(adapter_number)] = "custom"
if self._use_ubridge:
# make sure we have a vmnet per adapter if we use uBridge
allocate_vmnet = False
# first check if a vmnet is already assigned to the adapter
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet in self._vmx_pairs:
vmnet = os.path.basename(self._vmx_pairs[vnet])
if self.manager.is_managed_vmnet(vmnet) or vmnet == "vmnet0":
# vmnet already managed, try to allocate a new one
allocate_vmnet = True
else:
# otherwise allocate a new one
allocate_vmnet = True
if allocate_vmnet:
try:
vmnet = self.manager.allocate_vmnet()
except:
# clear everything up in case of error (e.g. no enough vmnets)
self._vmnets.clear()
raise
# mark the vmnet managed by us
if vmnet not in self._vmnets:
self._vmnets.append(vmnet)
self._vmx_pairs["ethernet{}.vnet".format(adapter_number)] = vmnet
else:
# not connected to anything...
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet not in self._vmx_pairs:
self._vmx_pairs["ethernet{}.startconnected".format(adapter_number)] = "FALSE"
# disable remaining network adapters
for adapter_number in range(self._adapters, self._maximum_adapters):
if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"):
log.debug("disabling remaining adapter {}".format(adapter_number))
self._vmx_pairs["ethernet{}.startconnected".format(adapter_number)] = "FALSE"
@asyncio.coroutine
def _add_ubridge_connection(self, nio, adapter_number):
"""
Creates a connection in uBridge.
:param nio: NIO instance
:param adapter_number: adapter number
"""
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet not in self._vmx_pairs:
raise VMwareError("vnet {} not in VMX file".format(vnet))
yield from self._ubridge_hypervisor.send("bridge create {name}".format(name=vnet))
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
if sys.platform.startswith("linux"):
yield from self._ubridge_hypervisor.send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=vnet,
interface=vmnet_interface))
elif sys.platform.startswith("win"):
windows_interfaces = get_windows_interfaces()
npf = None
for interface in windows_interfaces:
if "netcard" in interface and vmnet_interface in interface["netcard"]:
npf = interface["id"]
elif vmnet_interface in interface["name"]:
npf = interface["id"]
if npf:
yield from self._ubridge_hypervisor.send('bridge add_nio_ethernet {name} "{interface}"'.format(name=vnet,
interface=npf))
else:
raise VMwareError("Could not find NPF id for VMnet interface {}".format(vmnet_interface))
elif sys.platform.startswith("darwin"):
yield from self._ubridge_hypervisor.send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet,
interface=vmnet_interface))
else:
yield from self._ubridge_hypervisor.send('bridge add_nio_ethernet {name} "{interface}"'.format(name=vnet,
interface=vmnet_interface))
if isinstance(nio, NIOUDP):
yield from self._ubridge_hypervisor.send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=vnet,
lport=nio.lport,
rhost=nio.rhost,
rport=nio.rport))
if nio.capturing:
yield from self._ubridge_hypervisor.send('bridge start_capture {name} "{pcap_file}"'.format(name=vnet,
pcap_file=nio.pcap_output_file))
yield from self._ubridge_hypervisor.send('bridge start {name}'.format(name=vnet))
@asyncio.coroutine
def _delete_ubridge_connection(self, adapter_number):
"""
Deletes a connection in uBridge.
:param adapter_number: adapter number
"""
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet not in self._vmx_pairs:
raise VMwareError("vnet {} not in VMX file".format(vnet))
yield from self._ubridge_hypervisor.send("bridge delete {name}".format(name=vnet))
@asyncio.coroutine
def _start_ubridge_capture(self, adapter_number, output_file):
"""
Start a packet capture in uBridge.
:param adapter_number: adapter number
:param output_file: PCAP destination file for the capture
"""
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet not in self._vmx_pairs:
raise VMwareError("vnet {} not in VMX file".format(vnet))
if not self._ubridge_hypervisor:
raise VMwareError("Cannot start the packet capture: uBridge is not running")
yield from self._ubridge_hypervisor.send('bridge start_capture {name} "{output_file}"'.format(name=vnet,
output_file=output_file))
@asyncio.coroutine
def _stop_ubridge_capture(self, adapter_number):
"""
Stop a packet capture in uBridge.
:param adapter_number: adapter number
"""
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet not in self._vmx_pairs:
raise VMwareError("vnet {} not in VMX file".format(vnet))
if not self._ubridge_hypervisor:
raise VMwareError("Cannot stop the packet capture: uBridge is not running")
yield from self._ubridge_hypervisor.send("bridge stop_capture {name}".format(name=vnet))
def check_hw_virtualization(self):
"""
Returns either hardware virtualization is activated or not.
:returns: boolean
"""
self._read_vmx_file()
if self._get_vmx_setting("vhv.enable", "TRUE"):
return True
return False
@asyncio.coroutine
def start(self):
"""
Starts this VMware VM.
"""
if (yield from self.is_running()):
raise VMwareError("The VM is already running in VMware")
ubridge_path = self.ubridge_path
if not ubridge_path or not os.path.isfile(ubridge_path):
raise VMwareError("ubridge is necessary to start a VMware VM")
if self._use_ubridge:
yield from self._start_ubridge()
self._read_vmx_file()
# check if there is enough RAM to run
if "memsize" in self._vmx_pairs:
self.check_available_ram(int(self._vmx_pairs["memsize"]))
self._set_network_options()
self._set_serial_console()
self._write_vmx_file()
if self._headless:
yield from self._control_vm("start", "nogui")
else:
yield from self._control_vm("start")
if self._use_ubridge and self._ubridge_hypervisor:
for adapter_number in range(0, self._adapters):
nio = self._ethernet_adapters[adapter_number].get_nio(0)
if nio:
yield from self._add_ubridge_connection(nio, adapter_number)
if self._enable_remote_console and self._console is not None:
try:
if sys.platform.startswith("win"):
yield from wait_for_named_pipe_creation(self._get_pipe_name())
else:
yield from wait_for_file_creation(self._get_pipe_name()) # wait for VMware to create the pipe file.
except asyncio.TimeoutError:
raise VMwareError('Pipe file "{}" for remote console has not been created by VMware'.format(self._get_pipe_name()))
self._start_remote_console()
if self._get_vmx_setting("vhv.enable", "TRUE"):
self._hw_virtualization = True
self._started = True
log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id))
@asyncio.coroutine
def stop(self):
"""
Stops this VMware VM.
"""
self._hw_virtualization = False
self._stop_remote_console()
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
yield from self._ubridge_hypervisor.stop()
try:
if self.acpi_shutdown:
# use ACPI to shutdown the VM
yield from self._control_vm("stop", "soft")
else:
yield from self._control_vm("stop")
finally:
self._started = False
self._read_vmx_file()
if self._use_ubridge:
self._vmnets.clear()
# remove the adapters managed by GNS3
for adapter_number in range(0, self._adapters):
if self._get_vmx_setting("ethernet{}.vnet".format(adapter_number)) or \
self._get_vmx_setting("ethernet{}.connectiontype".format(adapter_number)) is None:
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet in self._vmx_pairs:
vmnet = os.path.basename(self._vmx_pairs[vnet])
if not self.manager.is_managed_vmnet(vmnet):
continue
log.debug("removing adapter {}".format(adapter_number))
for key in list(self._vmx_pairs.keys()):
if key.startswith("ethernet{}.".format(adapter_number)):
del self._vmx_pairs[key]
# re-enable any remaining network adapters
for adapter_number in range(self._adapters, self._maximum_adapters):
if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"):
log.debug("enabling remaining adapter {}".format(adapter_number))
self._vmx_pairs["ethernet{}.startconnected".format(adapter_number)] = "TRUE"
self._write_vmx_file()
log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
@asyncio.coroutine
def suspend(self):
"""
Suspends this VMware VM.
"""
if self.manager.host_type != "ws":
raise VMwareError("Pausing a VM is only supported by VMware Workstation")
yield from self._control_vm("pause")
log.info("VMware VM '{name}' [{id}] paused".format(name=self.name, id=self.id))
@asyncio.coroutine
def resume(self):
"""
Resumes this VMware VM.
"""
if self.manager.host_type != "ws":
raise VMwareError("Unpausing a VM is only supported by VMware Workstation")
yield from self._control_vm("unpause")
log.info("VMware VM '{name}' [{id}] resumed".format(name=self.name, id=self.id))
@asyncio.coroutine
def reload(self):
"""
Reloads this VMware VM.
"""
yield from self._control_vm("reset")
log.info("VMware VM '{name}' [{id}] reloaded".format(name=self.name, id=self.id))
@asyncio.coroutine
def close(self):
"""
Closes this VMware VM.
"""
if not (yield from super().close()):
return False
for adapter in self._ethernet_adapters.values():
if adapter is not None:
for nio in adapter.ports.values():
if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
if nio and isinstance(nio, NIOVMNET) and nio.vmnet in self._vmnets:
self._vmnets.remove(nio.vmnet)
try:
self.acpi_shutdown = False
yield from self.stop()
except VMwareError:
pass
if self._linked_clone:
yield from self.manager.remove_from_vmware_inventory(self._vmx_path)
@property
def headless(self):
"""
Returns either the VM will start in headless mode
:returns: boolean
"""
return self._headless
@headless.setter
def headless(self, headless):
"""
Sets either the VM will start in headless mode
:param headless: boolean
"""
if headless:
log.info("VMware VM '{name}' [{id}] has enabled the headless mode".format(name=self.name, id=self.id))
else:
log.info("VMware VM '{name}' [{id}] has disabled the headless mode".format(name=self.name, id=self.id))
self._headless = headless
@property
def acpi_shutdown(self):
"""
Returns either the VM will use ACPI shutdown
:returns: boolean
"""
return self._acpi_shutdown
@acpi_shutdown.setter
def acpi_shutdown(self, acpi_shutdown):
"""
Sets either the VM will use ACPI shutdown
:param acpi_shutdown: boolean
"""
if acpi_shutdown:
log.info("VMware VM '{name}' [{id}] has enabled the ACPI shutdown mode".format(name=self.name, id=self.id))
else:
log.info("VMware VM '{name}' [{id}] has disabled the ACPI shutdown mode".format(name=self.name, id=self.id))
self._acpi_shutdown = acpi_shutdown
@property
def vmx_path(self):
"""
Returns the path to the vmx file.
:returns: VMware vmx file
"""
return self._vmx_path
@vmx_path.setter
def vmx_path(self, vmx_path):
"""
Sets the path to the vmx file.
:param vmx_path: VMware vmx file
"""
log.info("VMware VM '{name}' [{id}] has set the vmx file path to '{vmx}'".format(name=self.name, id=self.id, vmx=vmx_path))
self._vmx_path = vmx_path
@property
def enable_remote_console(self):
"""
Returns either the remote console is enabled or not
:returns: boolean
"""
return self._enable_remote_console
@enable_remote_console.setter
def enable_remote_console(self, enable_remote_console):
"""
Sets either the console is enabled or not
:param enable_remote_console: boolean
"""
if enable_remote_console:
log.info("VMware VM '{name}' [{id}] has enabled the console".format(name=self.name, id=self.id))
if self._started:
self._start_remote_console()
else:
log.info("VMware VM '{name}' [{id}] has disabled the console".format(name=self.name, id=self.id))
self._stop_remote_console()
self._enable_remote_console = enable_remote_console
@property
def adapters(self):
"""
Returns the number of adapters configured for this VMware VM.
:returns: number of adapters
"""
return self._adapters
@adapters.setter
def adapters(self, adapters):
"""
Sets the number of Ethernet adapters for this VMware VM instance.
:param adapters: number of adapters
"""
# VMware VMs are limited to 10 adapters
if adapters > 10:
raise VMwareError("Number of adapters above the maximum supported of 10")
self._ethernet_adapters.clear()
for adapter_number in range(0, adapters):
self._ethernet_adapters[adapter_number] = EthernetAdapter()
self._adapters = len(self._ethernet_adapters)
log.info("VMware VM '{name}' [{id}] has changed the number of Ethernet adapters to {adapters}".format(name=self.name,
id=self.id,
adapters=adapters))
@property
def adapter_type(self):
"""
Returns the adapter type for this VMware VM instance.
:returns: adapter type (string)
"""
return self._adapter_type
@adapter_type.setter
def adapter_type(self, adapter_type):
"""
Sets the adapter type for this VMware VM instance.
:param adapter_type: adapter type (string)
"""
self._adapter_type = adapter_type
log.info("VMware VM '{name}' [{id}]: adapter type changed to {adapter_type}".format(name=self.name,
id=self.id,
adapter_type=adapter_type))
@property
def use_ubridge(self):
"""
Returns either GNS3 can use uBridge for network connections.
:returns: boolean
"""
return self._use_ubridge
@use_ubridge.setter
def use_ubridge(self, use_ubridge):
"""
Allows GNS3 to use uBridge for network connections.
:param use_ubridge: boolean
"""
if use_ubridge:
log.info("VMware VM '{name}' [{id}] will use uBridge for network connections".format(name=self.name, id=self.id))
else:
log.info("VMware VM '{name}' [{id}] will not use uBridge for network connections".format(name=self.name, id=self.id))
self._use_ubridge = use_ubridge
@property
def use_any_adapter(self):
"""
Returns either GNS3 can use any VMware adapter on this instance.
:returns: boolean
"""
return self._use_any_adapter
@use_any_adapter.setter
def use_any_adapter(self, use_any_adapter):
"""
Allows GNS3 to use any VMware adapter on this instance.
:param use_any_adapter: boolean
"""
if use_any_adapter:
log.info("VMware VM '{name}' [{id}] is allowed to use any adapter".format(name=self.name, id=self.id))
else:
log.info("VMware VM '{name}' [{id}] is not allowed to use any adapter".format(name=self.name, id=self.id))
self._use_any_adapter = use_any_adapter
@asyncio.coroutine
def adapter_add_nio_binding(self, adapter_number, nio):
"""
Adds an adapter NIO binding.
:param adapter_number: adapter number
:param nio: NIO instance to add to the slot/port
"""
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
self._read_vmx_file()
if isinstance(nio, NIONAT):
if self._started:
raise VMwareError("Sorry, adding a link to NAT for a started VMware VM is not supported")
self._vmx_pairs["ethernet{}.connectiontype".format(adapter_number)] = "nat"
self._write_vmx_file()
else:
# check if trying to connect to a nat, bridged or host-only adapter
if not self._use_any_adapter and self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"):
# check for the connection type
connection_type = "ethernet{}.connectiontype".format(adapter_number)
if connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"):
raise VMwareError("Attachment ({}) already configured on network adapter {}. "
"Please remove it or allow GNS3 to use any adapter.".format(self._vmx_pairs[connection_type],
adapter_number))
if isinstance(nio, NIOVMNET):
if self._started:
raise VMwareError("Sorry, adding a link to a started VMware VM is not supported without uBridge enabled")
self._vmx_pairs["ethernet{}.vnet".format(adapter_number)] = nio.vmnet
self._write_vmx_file()
if nio.vmnet not in self._vmnets:
self._vmnets.append(nio.vmnet)
adapter.add_nio(0, nio)
if self._started and self._use_ubridge and self._ubridge_hypervisor:
yield from self._add_ubridge_connection(nio, adapter_number)
log.info("VMware VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
id=self.id,
nio=nio,
adapter_number=adapter_number))
@asyncio.coroutine
def adapter_remove_nio_binding(self, adapter_number):
"""
Removes an adapter NIO binding.
:param adapter_number: adapter number
:returns: NIO instance
"""
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
nio = adapter.get_nio(0)
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
if isinstance(nio, NIOVMNET):
self._read_vmx_file()
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet in self._vmx_pairs:
del self._vmx_pairs[vnet]
self._write_vmx_file()
if nio.vmnet in self._vmnets:
self._vmnets.remove(nio.vmnet)
adapter.remove_nio(0)
if self._started and self._use_ubridge and self._ubridge_hypervisor:
yield from self._delete_ubridge_connection(adapter_number)
log.info("VMware VM '{name}' [{id}]: {nio} removed from adapter {adapter_number}".format(name=self.name,
id=self.id,
nio=nio,
adapter_number=adapter_number))
return nio
def _get_pipe_name(self):
"""
Returns the pipe name to create a serial connection.
:returns: pipe path (string)
"""
if sys.platform.startswith("win"):
pipe_name = r"\\.\pipe\gns3_vmware\{}".format(self.id)
else:
pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vmware", "{}".format(self.id))
try:
os.makedirs(os.path.dirname(pipe_name), exist_ok=True)
except OSError as e:
raise VMwareError("Could not create the VMware pipe directory: {}".format(e))
return pipe_name
def _set_serial_console(self):
"""
Configures the first serial port to allow a serial console connection.
"""
pipe_name = self._get_pipe_name()
serial_port = {"serial0.present": "TRUE",
"serial0.filetype": "pipe",
"serial0.filename": pipe_name,
"serial0.pipe.endpoint": "server"}
self._vmx_pairs.update(serial_port)
def _start_remote_console(self):
"""
Starts remote console support for this VM.
"""
# starts the Telnet to pipe thread
pipe_name = self._get_pipe_name()
if sys.platform.startswith("win"):
try:
self._serial_pipe = open(pipe_name, "a+b")
except OSError as e:
raise VMwareError("Could not open the pipe {}: {}".format(pipe_name, e))
try:
self._telnet_server_thread = TelnetServer(self.name, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._manager.port_manager.console_host, self._console)
except OSError as e:
raise VMwareError("Unable to create Telnet server: {}".format(e))
self._telnet_server_thread.start()
else:
try:
self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self._serial_pipe.connect(pipe_name)
except OSError as e:
raise VMwareError("Could not connect to the pipe {}: {}".format(pipe_name, e))
try:
self._telnet_server_thread = TelnetServer(self.name, self._serial_pipe, self._manager.port_manager.console_host, self._console)
except OSError as e:
raise VMwareError("Unable to create Telnet server: {}".format(e))
self._telnet_server_thread.start()
def _stop_remote_console(self):
"""
Stops remote console support for this VM.
"""
if self._telnet_server_thread:
if self._telnet_server_thread.is_alive():
self._telnet_server_thread.stop()
self._telnet_server_thread.join(timeout=3)
if self._telnet_server_thread.is_alive():
log.warn("Serial pipe thread is still alive!")
self._telnet_server_thread = None
if self._serial_pipe:
if sys.platform.startswith("win"):
win32file.CloseHandle(msvcrt.get_osfhandle(self._serial_pipe.fileno()))
else:
self._serial_pipe.close()
self._serial_pipe = None
@asyncio.coroutine
def start_capture(self, adapter_number, output_file):
"""
Starts a packet capture.
:param adapter_number: adapter number
:param output_file: PCAP destination file for the capture
"""
try:
adapter = self._ethernet_adapters[adapter_number]
except KeyError:
raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
nio = adapter.get_nio(0)
if isinstance(nio, NIOVMNET):
raise VMwareError("Sorry, packet capture is not supported without uBridge enabled")
if not nio:
raise VMwareError("Adapter {} is not connected".format(adapter_number))
if nio.capturing:
raise VMwareError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number))
nio.startPacketCapture(output_file)
if self._started:
yield from self._start_ubridge_capture(adapter_number, output_file)
log.info("VMware VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
id=self.id,
adapter_number=adapter_number))
def stop_capture(self, adapter_number):
"""
Stops a packet capture.
:param adapter_number: adapter number
"""
try:
adapter = self._ethernet_adapters[adapter_number]
except KeyError:
raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
nio = adapter.get_nio(0)
if not nio:
raise VMwareError("Adapter {} is not connected".format(adapter_number))
nio.stopPacketCapture()
if self._started:
yield from self._stop_ubridge_capture(adapter_number)
log.info("VMware VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
id=self.id,
adapter_number=adapter_number))