1
0
mirror of https://github.com/GNS3/gns3-server synced 2025-06-28 02:42:39 +00:00

Add new method to find the IP address of a VBox GNS3 VM + allow NAT Network

This commit is contained in:
grossmj 2025-04-21 16:29:32 +07:00
parent a5a663bc99
commit a24d2e25ed
No known key found for this signature in database
GPG Key ID: 1E7DD6DBB53FF3D7

View File

@ -247,6 +247,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
return True return True
return False return False
async def list(self): async def list(self):
""" """
List all VirtualBox VMs List all VirtualBox VMs
@ -267,8 +268,8 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
# get a NAT interface number # get a NAT interface number
nat_interface_number = await self._look_for_interface("nat") nat_interface_number = await self._look_for_interface("nat")
if nat_interface_number < 0: if nat_interface_number < 0 and await self._look_for_interface("natnetwork") < 0:
raise GNS3VMError('VM "{}" must have a NAT interface configured in order to start'.format(self.vmname)) raise GNS3VMError('VM "{}" must have a NAT or NAT Network interface configured in order to start'.format(self.vmname))
if sys.platform.startswith("darwin") and parse_version(self._system_properties["API version"]) >= parse_version("7_0"): if sys.platform.startswith("darwin") and parse_version(self._system_properties["API version"]) >= parse_version("7_0"):
# VirtualBox 7.0+ on macOS requires a host-only network interface # VirtualBox 7.0+ on macOS requires a host-only network interface
@ -337,39 +338,68 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
elif vm_state == "paused": elif vm_state == "paused":
args = [self._vmname, "resume"] args = [self._vmname, "resume"]
await self._execute("controlvm", args) await self._execute("controlvm", args)
ip_address = "127.0.0.1"
try:
# get a random port on localhost
with socket.socket() as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((ip_address, 0))
api_port = s.getsockname()[1]
except OSError as e:
raise GNS3VMError("Error while getting random port: {}".format(e))
if await self._check_vbox_port_forwarding(): log.info("Retrieving IP address from GNS3 VM...")
# delete the GNS3VM NAT port forwarding rule if it exists ip = await self._get_ip_from_guest_property()
log.info("Removing GNS3VM NAT port forwarding rule from interface {}".format(nat_interface_number)) if ip:
await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"]) self.ip_address = ip
else:
# if we can't get the IP address from the guest property, we try to get it from the GNS3 server (a NAT interface is required)
if nat_interface_number < 0:
raise GNS3VMError("Could not find guest IP address for {}".format(self.vmname))
log.warning("Could not find IP address from guest property, trying to get it from GNS3 server")
ip_address = "127.0.0.1"
try:
# get a random port on localhost
with socket.socket() as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((ip_address, 0))
api_port = s.getsockname()[1]
except OSError as e:
raise GNS3VMError("Error while getting random port: {}".format(e))
# add a GNS3VM NAT port forwarding rule to redirect 127.0.0.1 with random port to the port in the VM if await self._check_vbox_port_forwarding():
log.info("Adding GNS3VM NAT port forwarding rule with port {} to interface {}".format(api_port, nat_interface_number)) # delete the GNS3VM NAT port forwarding rule if it exists
await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), log.info("Removing GNS3VM NAT port forwarding rule from interface {}".format(nat_interface_number))
"GNS3VM,tcp,{},{},,{}".format(ip_address, api_port, self.port)]) await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"])
self.ip_address = await self._get_ip(interface_number, api_port) # add a GNS3VM NAT port forwarding rule to redirect 127.0.0.1 with random port to the port in the VM
log.info("GNS3 VM has been started with IP {}".format(self.ip_address)) log.info("Adding GNS3VM NAT port forwarding rule with port {} to interface {}".format(api_port, nat_interface_number))
await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number),
"GNS3VM,tcp,{},{},,{}".format(ip_address, api_port, self.port)])
self.ip_address = await self._get_ip_from_server(interface_number, api_port)
log.info("GNS3 VM has been started with IP '{}'".format(self.ip_address))
self.running = True self.running = True
async def _get_ip(self, hostonly_interface_number, api_port): async def _get_ip_from_guest_property(self):
""" """
Get the IP from VirtualBox. Get the IP from VirtualBox by retrieving the guest property (Guest Additions must be installed).
"""
remaining_try = 180 # try for 3 minutes
while remaining_try > 0:
result = await self._execute("guestproperty", ["get", self._vmname, "/VirtualBox/GuestInfo/Net/0/V4/IP"])
for info in result.splitlines():
if ':' in info:
name, value = info.split(':', 1)
if name == "Value":
return value.strip()
remaining_try -= 1
await asyncio.sleep(1)
return None
async def _get_ip_from_server(self, hostonly_interface_number, api_port):
"""
Get the IP from VirtualBox by sending a request to the GNS3 server.
Due to VirtualBox limitation the only way is to send request each Due to VirtualBox limitation the only way is to send request each
second to a GNS3 endpoint in order to get the list of the interfaces and second to a GNS3 endpoint in order to get the list of the interfaces and
their IP and after that match it with VirtualBox host only. their IP and after that match it with VirtualBox host only.
""" """
remaining_try = 300
remaining_try = 180 # try for 3 minutes
while remaining_try > 0: while remaining_try > 0:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
try: try: