mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-01-19 07:53:47 +02:00
Fix VMware support on macOS BigSur
This commit is contained in:
parent
bfd30f3547
commit
c892cf371b
@ -362,7 +362,7 @@ class Cloud(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Wireless adapters are not well supported by the libpcap on OSX
|
# 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")
|
raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
|
||||||
if port_info["interface"].startswith("vmnet"):
|
if port_info["interface"].startswith("vmnet"):
|
||||||
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them)
|
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them)
|
||||||
|
@ -27,6 +27,7 @@ import asyncio
|
|||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import codecs
|
import codecs
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from gns3server.utils.interfaces import interfaces
|
from gns3server.utils.interfaces import interfaces
|
||||||
@ -51,6 +52,7 @@ class VMware(BaseManager):
|
|||||||
self._vmrun_path = None
|
self._vmrun_path = None
|
||||||
self._host_type = None
|
self._host_type = None
|
||||||
self._vmnets = []
|
self._vmnets = []
|
||||||
|
self._vmnets_info = {}
|
||||||
self._vmnet_start_range = 2
|
self._vmnet_start_range = 2
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
self._vmnet_end_range = 19
|
self._vmnet_end_range = 19
|
||||||
@ -273,7 +275,7 @@ class VMware(BaseManager):
|
|||||||
else:
|
else:
|
||||||
# location on Linux
|
# location on Linux
|
||||||
vmware_networking_file = "/etc/vmware/networking"
|
vmware_networking_file = "/etc/vmware/networking"
|
||||||
vmnet_interfaces = []
|
vmnet_interfaces = {}
|
||||||
try:
|
try:
|
||||||
with open(vmware_networking_file, "r", encoding="utf-8") as f:
|
with open(vmware_networking_file, "r", encoding="utf-8") as f:
|
||||||
for line in f.read().splitlines():
|
for line in f.read().splitlines():
|
||||||
@ -281,7 +283,20 @@ class VMware(BaseManager):
|
|||||||
if match:
|
if match:
|
||||||
vmnet = "vmnet{}".format(match.group(1))
|
vmnet = "vmnet{}".format(match.group(1))
|
||||||
if vmnet not in ("vmnet0", "vmnet1", "vmnet8"):
|
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:
|
except OSError as e:
|
||||||
raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e))
|
raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e))
|
||||||
return vmnet_interfaces
|
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))
|
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)
|
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):
|
def refresh_vmnet_list(self, ubridge=True):
|
||||||
|
|
||||||
if ubridge:
|
if ubridge:
|
||||||
@ -332,6 +366,8 @@ class VMware(BaseManager):
|
|||||||
else:
|
else:
|
||||||
vmnet_interfaces = self._get_vmnet_interfaces()
|
vmnet_interfaces = self._get_vmnet_interfaces()
|
||||||
|
|
||||||
|
self._vmnets_info = vmnet_interfaces.copy()
|
||||||
|
vmnet_interfaces = list(vmnet_interfaces.keys())
|
||||||
# remove vmnets already in use
|
# remove vmnets already in use
|
||||||
for vmware_vm in self._nodes.values():
|
for vmware_vm in self._nodes.values():
|
||||||
for used_vmnet in vmware_vm.vmnets:
|
for used_vmnet in vmware_vm.vmnets:
|
||||||
@ -734,5 +770,4 @@ class VMware(BaseManager):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
vmware = VMware.instance()
|
vmware = VMware.instance()
|
||||||
print("=> Check version")
|
|
||||||
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))
|
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))
|
||||||
|
@ -23,9 +23,11 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import platform
|
||||||
|
|
||||||
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
|
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
|
||||||
from gns3server.utils.asyncio.serial import asyncio_open_serial
|
from gns3server.utils.asyncio.serial import asyncio_open_serial
|
||||||
|
from gns3server.utils import parse_version
|
||||||
from gns3server.utils.asyncio import locking
|
from gns3server.utils.asyncio import locking
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from .vmware_error import VMwareError
|
from .vmware_error import VMwareError
|
||||||
@ -252,8 +254,13 @@ class VMwareVM(BaseNode):
|
|||||||
if self._get_vmx_setting(connected):
|
if self._get_vmx_setting(connected):
|
||||||
del self._vmx_pairs[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
|
# then configure VMware network adapters
|
||||||
self.manager.refresh_vmnet_list()
|
|
||||||
for adapter_number in range(0, self._adapters):
|
for adapter_number in range(0, self._adapters):
|
||||||
|
|
||||||
custom_adapter = self._get_custom_adapter_settings(adapter_number)
|
custom_adapter = self._get_custom_adapter_settings(adapter_number)
|
||||||
@ -333,7 +340,16 @@ class VMwareVM(BaseNode):
|
|||||||
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
|
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
|
||||||
|
|
||||||
if sys.platform.startswith("darwin"):
|
if sys.platform.startswith("darwin"):
|
||||||
# special case on OSX, we cannot bind VMnet interfaces using the libpcap
|
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))
|
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
|
||||||
else:
|
else:
|
||||||
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
|
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
|
||||||
@ -426,7 +442,7 @@ class VMwareVM(BaseNode):
|
|||||||
if self.status == "started":
|
if self.status == "started":
|
||||||
return
|
return
|
||||||
|
|
||||||
if (await self.is_running()):
|
if await self.is_running():
|
||||||
raise VMwareError("The VM is already running in VMware")
|
raise VMwareError("The VM is already running in VMware")
|
||||||
|
|
||||||
ubridge_path = self.ubridge_path
|
ubridge_path = self.ubridge_path
|
||||||
@ -476,7 +492,7 @@ class VMwareVM(BaseNode):
|
|||||||
await self._stop_ubridge()
|
await self._stop_ubridge()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if (await self.is_running()):
|
if await self.is_running():
|
||||||
if self.on_close == "save_vm_state":
|
if self.on_close == "save_vm_state":
|
||||||
await self._control_vm("suspend")
|
await self._control_vm("suspend")
|
||||||
elif self.on_close == "shutdown_signal":
|
elif self.on_close == "shutdown_signal":
|
||||||
@ -728,7 +744,7 @@ class VMwareVM(BaseNode):
|
|||||||
# check for the connection type
|
# check for the connection type
|
||||||
connection_type = "ethernet{}.connectiontype".format(adapter_number)
|
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 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}. "
|
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],
|
"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,
|
adapter_number=adapter_number,
|
||||||
|
Loading…
Reference in New Issue
Block a user