VMware vmnets management almost complete.

This commit is contained in:
grossmj 2015-05-21 21:48:59 -06:00
parent 0287b4607d
commit 3729a10783
4 changed files with 214 additions and 54 deletions

View File

@ -504,7 +504,7 @@ class VirtualBoxVM(BaseVM):
""" """
Returns either GNS3 can use any VirtualBox adapter on this instance. Returns either GNS3 can use any VirtualBox adapter on this instance.
:returns: index :returns: boolean
""" """
return self._use_any_adapter return self._use_any_adapter
@ -520,7 +520,7 @@ class VirtualBoxVM(BaseVM):
if use_any_adapter: if use_any_adapter:
log.info("VirtualBox VM '{name}' [{id}] is allowed to use any adapter".format(name=self.name, id=self.id)) log.info("VirtualBox VM '{name}' [{id}] is allowed to use any adapter".format(name=self.name, id=self.id))
else: else:
log.info("VirtualBox VM '{name}' [{id}] is not allowd to use any adapter".format(name=self.name, id=self.id)) log.info("VirtualBox VM '{name}' [{id}] is not allowed to use any adapter".format(name=self.name, id=self.id))
self._use_any_adapter = use_any_adapter self._use_any_adapter = use_any_adapter
@property @property

View File

@ -21,11 +21,14 @@ VMware player/workstation server module.
import os import os
import sys import sys
import re
import shutil import shutil
import asyncio import asyncio
import subprocess import subprocess
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from gns3server.utils.interfaces import interfaces
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -42,6 +45,12 @@ class VMware(BaseManager):
super().__init__() super().__init__()
self._vmrun_path = None 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 @property
def vmrun_path(self): def vmrun_path(self):
@ -82,6 +91,62 @@ class VMware(BaseManager):
self._vmrun_path = vmrun_path self._vmrun_path = vmrun_path
return vmrun_path return vmrun_path
@staticmethod
def get_vmnet_interfaces():
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 more VMnet interfaces available")
return self._vmnets.pop(0)
def refresh_vmnet_list(self):
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
@property @property
def host_type(self): def host_type(self):
""" """

View File

@ -30,9 +30,9 @@ import configparser
import shutil import shutil
import asyncio import asyncio
from gns3server.utils.interfaces import interfaces
from gns3server.utils.asyncio import wait_for_process_termination from gns3server.utils.asyncio import wait_for_process_termination
from gns3server.utils.asyncio import monitor_process from gns3server.utils.asyncio import monitor_process
from collections import OrderedDict
from pkg_resources import parse_version from pkg_resources import parse_version
from .vmware_error import VMwareError from .vmware_error import VMwareError
from ..nios.nio_udp import NIOUDP from ..nios.nio_udp import NIOUDP
@ -56,8 +56,11 @@ class VMwareVM(BaseVM):
super().__init__(name, vm_id, project, manager, console=console) super().__init__(name, vm_id, project, manager, console=console)
self._linked_clone = linked_clone self._linked_clone = linked_clone
self._vmx_pairs = OrderedDict()
self._ubridge_process = None self._ubridge_process = None
self._ubridge_stdout_file = "" self._ubridge_stdout_file = ""
self._vmnets = []
self._maximum_adapters = 10
self._closed = False self._closed = False
# VMware VM settings # VMware VM settings
@ -67,6 +70,7 @@ class VMwareVM(BaseVM):
self._adapters = 0 self._adapters = 0
self._ethernet_adapters = {} self._ethernet_adapters = {}
self._adapter_type = "e1000" self._adapter_type = "e1000"
self._use_any_adapter = False
if not os.path.exists(vmx_path): if not os.path.exists(vmx_path):
raise VMwareError('VMware VM "{name}" [{id}]: could not find VMX file "{}"'.format(name, vmx_path)) raise VMwareError('VMware VM "{name}" [{id}]: could not find VMX file "{}"'.format(name, vmx_path))
@ -81,7 +85,13 @@ class VMwareVM(BaseVM):
"headless": self.headless, "headless": self.headless,
"enable_remote_console": self.enable_remote_console, "enable_remote_console": self.enable_remote_console,
"adapters": self._adapters, "adapters": self._adapters,
"adapter_type": self.adapter_type} "adapter_type": self.adapter_type,
"use_any_adapter": self.use_any_adapter}
@property
def vmnets(self):
return self._vmnets
@asyncio.coroutine @asyncio.coroutine
def _control_vm(self, subcommand, *additional_args): def _control_vm(self, subcommand, *additional_args):
@ -92,25 +102,15 @@ class VMwareVM(BaseVM):
log.debug("Control VM '{}' result: {}".format(subcommand, result)) log.debug("Control VM '{}' result: {}".format(subcommand, result))
return result return result
def _get_vmnet_interfaces(self): def _get_vmx_setting(self, name, value=None):
vmnet_intefaces = [] if name in self._vmx_pairs:
for interface in interfaces(): if value is not None:
if sys.platform.startswith("win"): if self._vmx_pairs[name] == value:
if "netcard" in interface: return value
windows_name = interface["netcard"] else:
else: return self._vmx_pairs[name]
windows_name = interface["name"] return None
match = re.search("(VMnet[0-9]+)", windows_name)
if match:
vmnet = match.group(1)
if vmnet not in ("VMnet1", "VMnet8"):
vmnet_intefaces.append(vmnet)
elif interface["name"].startswith("vmnet"):
vmnet = interface["name"]
if vmnet not in ("vmnet1", "vmnet8"):
vmnet_intefaces.append(interface["name"])
return vmnet_intefaces
def _set_network_options(self): def _set_network_options(self):
@ -119,43 +119,70 @@ class VMwareVM(BaseVM):
except OSError as e: except OSError as e:
raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e)) raise VMwareError('Could not read VMware VMX file "{}": {}'.format(self._vmx_path, e))
vmnet_interfaces = self._get_vmnet_interfaces() # first to some sanity checks
for adapter_number in range(0, self._adapters): for adapter_number in range(0, self._adapters):
nio = self._ethernet_adapters[adapter_number].get_nio(0) connected = "ethernet{}.startConnected".format(adapter_number)
if nio: if self._get_vmx_setting(connected):
if "ethernet{}.present".format(adapter_number) in self._vmx_pairs: del self._vmx_pairs[connected]
# check for the connection type # check if any vmnet interface managed by GNS3 is being used on existing VMware adapters
connection_type = "ethernet{}.connectionType".format(adapter_number) if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"):
if connection_type in self._vmx_pairs: connection_type = "ethernet{}.connectionType".format(adapter_number)
if self._vmx_pairs[connection_type] not in ("hostonly", "custom"): if self._vmx_pairs[connection_type] in ("hostonly", "custom"):
raise VMwareError("Attachment ({}) already configured on adapter {}. "
"Please set it to 'hostonly' or 'custom' to allow GNS3 to use it.".format(self._vmx_pairs[connection_type],
adapter_number))
# check for the vmnet interface
vnet = "ethernet{}.vnet".format(adapter_number) vnet = "ethernet{}.vnet".format(adapter_number)
if vnet in self._vmx_pairs: if vnet in self._vmx_pairs:
vmnet = os.path.basename(self._vmx_pairs[vnet]) vmnet = os.path.basename(self._vmx_pairs[vnet])
if vmnet in vmnet_interfaces: if self.manager.is_managed_vmnet(vmnet):
vmnet_interfaces.remove(vmnet) raise VMwareError("Network adapter {} is already associated with VMnet interface {} which is managed by GNS3, please remove".format(adapter_number, vmnet))
else:
raise VMwareError("Network adapter {} is not associated with a VMnet interface".format(adapter_number))
# check for adapter type # check for adapter type
# adapter_type = "ethernet{}.virtualDev".format(adapter_number) if self._adapter_type != "default":
# if adapter_type in self._vmx_pairs and self._vmx_pairs[adapter_type] != self._adapter_type: adapter_type = "ethernet{}.virtualDev".format(adapter_number)
# raise VMwareError("Network adapter {} is not of type {}".format(self._adapter_type)) if adapter_type in self._vmx_pairs and self._vmx_pairs[adapter_type] != self._adapter_type:
# else: raise VMwareError("Network adapter {} is not of type {}, please fix or remove it".format(self._adapter_type))
# self._vmx_pairs[adapter_type] = self._adapter_type
else:
new_ethernet_adapter = {"ethernet{}.present".format(adapter_number): "TRUE",
"ethernet{}.connectionType".format(adapter_number): "custom",
"ethernet{}.vnet".format(adapter_number): "vmnet1",
"ethernet{}.addressType".format(adapter_number): "generated",
"ethernet{}.generatedAddressOffset".format(adapter_number): "0"}
self._vmx_pairs.update(new_ethernet_adapter)
#raise VMwareError("Network adapter {} does not exist".format(adapter_number)) # check if connected to an adapter configured for nat or bridge
if self._ethernet_adapters[adapter_number].get_nio(0) and not self._use_any_adapter:
if 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:
if 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))
# now configure VMware network adapters
self.manager.refresh_vmnet_list()
for adapter_number in range(0, self._adapters):
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
vnet = "ethernet{}.vnet".format(adapter_number)
if vnet in self._vmx_pairs:
vmnet = os.path.basename(self._vmx_pairs[vnet])
else:
try:
vmnet = self.manager.allocate_vmnet()
finally:
self._vmnets.clear()
self._vmnets.append(vmnet)
self._vmx_pairs["ethernet{}.connectionType".format(adapter_number)] = "custom"
self._vmx_pairs["ethernet{}.vnet".format(adapter_number)] = vmnet
# 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"
self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs) self.manager.write_vmx_file(self._vmx_path, self._vmx_pairs)
self._update_ubridge_config() self._update_ubridge_config()
@ -180,7 +207,7 @@ class VMwareVM(BaseVM):
if sys.platform.startswith("linux"): if sys.platform.startswith("linux"):
config[bridge_name] = {"source_linux_raw": vmnet_interface} config[bridge_name] = {"source_linux_raw": vmnet_interface}
elif sys.platform.startswith("win"): elif sys.platform.startswith("win"):
windows_interfaces = interfaces() windows_interfaces = self.manager.get_vmnet_interfaces()
npf = None npf = None
for interface in windows_interfaces: for interface in windows_interfaces:
if "netcard" in interface and vmnet_interface in interface["netcard"]: if "netcard" in interface and vmnet_interface in interface["netcard"]:
@ -333,7 +360,39 @@ class VMwareVM(BaseVM):
self._ubridge_process.kill() self._ubridge_process.kill()
self._ubridge_process = None self._ubridge_process = None
yield from self._control_vm("stop") try:
yield from self._control_vm("stop")
finally:
self._vmnets.clear()
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))
# 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 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.manager.write_vmx_file(self._vmx_path, self._vmx_pairs)
log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id)) log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
@asyncio.coroutine @asyncio.coroutine
@ -521,6 +580,30 @@ class VMwareVM(BaseVM):
id=self.id, id=self.id,
adapter_type=adapter_type)) adapter_type=adapter_type))
@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
def adapter_add_nio_binding(self, adapter_number, nio): def adapter_add_nio_binding(self, adapter_number, nio):
""" """
Adds an adapter NIO binding. Adds an adapter NIO binding.

View File

@ -67,6 +67,10 @@ VMWARE_CREATE_SCHEMA = {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
}, },
"use_any_adapter": {
"description": "allow GNS3 to use any VMware adapter",
"type": "boolean",
},
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["name", "vmx_path", "linked_clone"], "required": ["name", "vmx_path", "linked_clone"],
@ -112,6 +116,10 @@ VMWARE_UPDATE_SCHEMA = {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
}, },
"use_any_adapter": {
"description": "allow GNS3 to use any VMware adapter",
"type": "boolean",
},
}, },
"additionalProperties": False, "additionalProperties": False,
} }
@ -164,6 +172,10 @@ VMWARE_OBJECT_SCHEMA = {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
}, },
"use_any_adapter": {
"description": "allow GNS3 to use any VMware adapter",
"type": "boolean",
},
"console": { "console": {
"description": "console TCP port", "description": "console TCP port",
"minimum": 1, "minimum": 1,