Automatically add vboxnet and DHCP server if not present for VirtualBox GNS3 VM. Ref #2266

pull/2286/head
grossmj 9 months ago
parent 285b19d97f
commit 702fea89fb

@ -15,11 +15,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import sys import sys
import aiohttp import aiohttp
import logging import logging
import asyncio import asyncio
import socket import socket
import ipaddress
from .base_gns3_vm import BaseGNS3VM from .base_gns3_vm import BaseGNS3VM
from .gns3_vm_error import GNS3VMError from .gns3_vm_error import GNS3VMError
@ -80,9 +82,6 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
except ValueError: except ValueError:
continue continue
self._system_properties[name.strip()] = value.strip() self._system_properties[name.strip()] = value.strip()
if "API Version" in self._system_properties:
# API version is not consistent between VirtualBox versions, the key is named "API Version" in VirtualBox 7
self._system_properties["API version"] = self._system_properties.pop("API Version")
async def _check_requirements(self): async def _check_requirements(self):
""" """
@ -92,16 +91,16 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
if not self._system_properties: if not self._system_properties:
await self._get_system_properties() await self._get_system_properties()
if "API version" not in self._system_properties: if "API version" not in self._system_properties:
raise VirtualBoxError("Can't access to VirtualBox API version:\n{}".format(self._system_properties)) raise GNS3VMError("Can't access to VirtualBox API version:\n{}".format(self._system_properties))
from cpuinfo import get_cpu_info from cpuinfo import get_cpu_info
cpu_info = await wait_run_in_executor(get_cpu_info) cpu_info = await wait_run_in_executor(get_cpu_info)
vendor_id = cpu_info.get('vendor_id_raw') vendor_id = cpu_info.get('vendor_id_raw')
if vendor_id == "GenuineIntel": if vendor_id == "GenuineIntel":
if parse_version(self._system_properties["API version"]) < parse_version("6_1"): if parse_version(self._system_properties["API version"]) < parse_version("6_1"):
raise VirtualBoxError("VirtualBox version 6.1 or above is required to run the GNS3 VM with nested virtualization enabled on Intel processors") raise GNS3VMError("VirtualBox version 6.1 or above is required to run the GNS3 VM with nested virtualization enabled on Intel processors")
elif vendor_id == "AuthenticAMD": elif vendor_id == "AuthenticAMD":
if parse_version(self._system_properties["API version"]) < parse_version("6_0"): if parse_version(self._system_properties["API version"]) < parse_version("6_0"):
raise VirtualBoxError("VirtualBox version 6.0 or above is required to run the GNS3 VM with nested virtualization enabled on AMD processors") raise GNS3VMError("VirtualBox version 6.0 or above is required to run the GNS3 VM with nested virtualization enabled on AMD processors")
else: else:
log.warning("Could not determine CPU vendor: {}".format(vendor_id)) log.warning("Could not determine CPU vendor: {}".format(vendor_id))
@ -162,6 +161,44 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
return True return True
return False return False
async def _add_dhcp_server(self, vboxnet):
"""
Add a DHCP server for vboxnet.
:param vboxnet: vboxnet name
"""
hostonlyifs = await self._execute("list", ["hostonlyifs"])
pattern = r"IPAddress:\s+(\d+\.\d+\.\d+\.\d+)\nNetworkMask:\s+(\d+\.\d+\.\d+\.\d+)"
match = re.search(pattern, hostonlyifs)
if match:
ip_address = match.group(1)
netmask = match.group(2)
else:
raise GNS3VMError("Could not find IP address and netmask for vboxnet {}".format(vboxnet))
try:
interface = ipaddress.IPv4Interface(f"{ip_address}/{netmask}")
subnet = ipaddress.IPv4Network(str(interface.network))
dhcp_server_ip = str(interface.ip + 1)
netmask = str(subnet.netmask)
lower_ip = str(interface.ip + 2)
upper_ip = str(subnet.network_address + subnet.num_addresses - 2)
except ValueError:
raise GNS3VMError("Invalid IP address and netmask for vboxnet {}: {}/{}".format(vboxnet, ip_address, netmask))
dhcp_server_args = [
"add",
"--network=HostInterfaceNetworking-{}".format(vboxnet),
"--server-ip={}".format(dhcp_server_ip),
"--netmask={}".format(netmask),
"--lower-ip={}".format(lower_ip),
"--upper-ip={}".format(upper_ip),
"--enable"
]
await self._execute("dhcpserver", dhcp_server_args)
async def _check_vboxnet_exists(self, vboxnet, vboxnet_type): async def _check_vboxnet_exists(self, vboxnet, vboxnet_type):
""" """
Check if the vboxnet interface exists Check if the vboxnet interface exists
@ -264,12 +301,20 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
await self.set_hostonly_network(interface_number, first_available_vboxnet) await self.set_hostonly_network(interface_number, first_available_vboxnet)
vboxnet = first_available_vboxnet vboxnet = first_available_vboxnet
else: else:
raise GNS3VMError('VirtualBox host-only network "{}" does not exist, please make the sure the network adapter {} configuration is valid for "{}"'.format(vboxnet, try:
interface_number, await self._execute("hostonlyif", ["create"])
self._vmname)) except GNS3VMError:
raise GNS3VMError('VirtualBox host-only network "{}" does not exist and could not be automatically created, please make the sure the network adapter {} configuration is valid for "{}"'.format(
vboxnet,
interface_number,
self._vmname
))
if backend_type == "hostonlyadapter" and not (await self._check_dhcp_server(vboxnet)): if backend_type == "hostonlyadapter" and not (await self._check_dhcp_server(vboxnet)):
raise GNS3VMError('DHCP must be enabled on VirtualBox host-only network "{}"'.format(vboxnet)) try:
await self._add_dhcp_server(vboxnet)
except GNS3VMError as e:
raise GNS3VMError("Could not add DHCP server for vboxnet {}: {}, please configure manually".format(vboxnet, e))
vm_state = await self._get_state() vm_state = await self._get_state()
log.info('"{}" state is {}'.format(self._vmname, vm_state)) log.info('"{}" state is {}'.format(self._vmname, vm_state))
@ -302,7 +347,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
except OSError as e: except OSError as e:
raise GNS3VMError("Error while getting random port: {}".format(e)) raise GNS3VMError("Error while getting random port: {}".format(e))
if (await self._check_vbox_port_forwarding()): if await self._check_vbox_port_forwarding():
# delete the GNS3VM NAT port forwarding rule if it exists # delete the GNS3VM NAT port forwarding rule if it exists
log.info("Removing GNS3VM NAT port forwarding rule from interface {}".format(nat_interface_number)) log.info("Removing GNS3VM NAT port forwarding rule from interface {}".format(nat_interface_number))
await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"]) await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"])

Loading…
Cancel
Save