Merge pull request #1913 from GNS3/vmware-bigsur

Fix VMware VM support on macOS BigSur
pull/1916/head
Jeremy Grossmann 3 years ago committed by GitHub
commit 94acf60aef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -362,12 +362,12 @@ class Cloud(BaseNode):
"""
# Wireless adapters are not well supported by the libpcap on OSX
if (await self._is_wifi_adapter_osx(port_info["interface"])):
if await self._is_wifi_adapter_osx(port_info["interface"]):
raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
if port_info["interface"].startswith("vmnet"):
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them)
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
interface=port_info["interface"]))
interface=port_info["interface"]))
return
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))

@ -27,6 +27,7 @@ import asyncio
import subprocess
import logging
import codecs
import ipaddress
from collections import OrderedDict
from gns3server.utils.interfaces import interfaces
@ -51,6 +52,7 @@ class VMware(BaseManager):
self._vmrun_path = None
self._host_type = None
self._vmnets = []
self._vmnets_info = {}
self._vmnet_start_range = 2
if sys.platform.startswith("win"):
self._vmnet_end_range = 19
@ -273,7 +275,7 @@ class VMware(BaseManager):
else:
# location on Linux
vmware_networking_file = "/etc/vmware/networking"
vmnet_interfaces = []
vmnet_interfaces = {}
try:
with open(vmware_networking_file, "r", encoding="utf-8") as f:
for line in f.read().splitlines():
@ -281,7 +283,20 @@ class VMware(BaseManager):
if match:
vmnet = "vmnet{}".format(match.group(1))
if vmnet not in ("vmnet0", "vmnet1", "vmnet8"):
vmnet_interfaces.append(vmnet)
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
@ -324,6 +339,25 @@ class VMware(BaseManager):
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 find_bridge_interface(self, vmnet_interface):
"""
Find the bridge interface that is used for the vmnet interface in VMware.
"""
if vmnet_interface in self._vmnets_info.keys():
subnet = self._vmnets_info[vmnet_interface].get("subnet", None)
netmask = self._vmnets_info[vmnet_interface].get("netmask", None)
if subnet and netmask:
for interface in interfaces():
try:
network = ipaddress.ip_network(f"{subnet}/{netmask}")
ip = ipaddress.ip_address(interface["ip_address"])
except ValueError:
continue
if ip in network:
return interface["name"]
return None
def refresh_vmnet_list(self, ubridge=True):
if ubridge:
@ -332,6 +366,8 @@ class VMware(BaseManager):
else:
vmnet_interfaces = self._get_vmnet_interfaces()
self._vmnets_info = vmnet_interfaces.copy()
vmnet_interfaces = list(vmnet_interfaces.keys())
# remove vmnets already in use
for vmware_vm in self._nodes.values():
for used_vmnet in vmware_vm.vmnets:
@ -734,5 +770,4 @@ class VMware(BaseManager):
if __name__ == '__main__':
loop = asyncio.get_event_loop()
vmware = VMware.instance()
print("=> Check version")
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))

@ -23,9 +23,11 @@ import sys
import os
import asyncio
import tempfile
import platform
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
from gns3server.utils.asyncio.serial import asyncio_open_serial
from gns3server.utils import parse_version
from gns3server.utils.asyncio import locking
from collections import OrderedDict
from .vmware_error import VMwareError
@ -252,8 +254,13 @@ class VMwareVM(BaseNode):
if self._get_vmx_setting(connected):
del self._vmx_pairs[connected]
use_ubridge = True
# use alternative method to find vmnet interfaces on macOS >= 11.0 (BigSur)
# because "bridge" interfaces are used instead and they are only created on the VM starts
if sys.platform.startswith("darwin") and parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"):
use_ubridge = False
self.manager.refresh_vmnet_list(ubridge=use_ubridge)
# then configure VMware network adapters
self.manager.refresh_vmnet_list()
for adapter_number in range(0, self._adapters):
custom_adapter = self._get_custom_adapter_settings(adapter_number)
@ -333,8 +340,17 @@ class VMwareVM(BaseNode):
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
if sys.platform.startswith("darwin"):
# special case on OSX, we cannot bind VMnet interfaces using the libpcap
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
if parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"):
# a bridge interface (bridge100, bridge101 etc.) is used instead of a vmnet interface
# on macOS >= 11.0 (Big Sur)
vmnet_interface = self.manager.find_bridge_interface(vmnet_interface)
if not vmnet_interface:
raise VMwareError(f"fCould not find bridge interface linked with {vmnet_interface}")
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
else:
# special case on macOS, we cannot bind VMnet interfaces using the libpcap
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
else:
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
@ -426,7 +442,7 @@ class VMwareVM(BaseNode):
if self.status == "started":
return
if (await self.is_running()):
if await self.is_running():
raise VMwareError("The VM is already running in VMware")
ubridge_path = self.ubridge_path
@ -476,7 +492,7 @@ class VMwareVM(BaseNode):
await self._stop_ubridge()
try:
if (await self.is_running()):
if await self.is_running():
if self.on_close == "save_vm_state":
await self._control_vm("suspend")
elif self.on_close == "shutdown_signal":
@ -728,7 +744,7 @@ class VMwareVM(BaseNode):
# check for the connection 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"):
if (await self.is_running()):
if await self.is_running():
raise VMwareError("Attachment '{attachment}' is configured on network adapter {adapter_number}. "
"Please stop VMware VM '{name}' to link to this adapter and allow GNS3 to change the attachment type.".format(attachment=self._vmx_pairs[connection_type],
adapter_number=adapter_number,

Loading…
Cancel
Save