mirror of
https://github.com/GNS3/gns3-server
synced 2024-12-25 00:08:11 +00:00
Merge branch '2.2' into 3.0
# Conflicts: # gns3server/compute/base_node.py # gns3server/compute/docker/__init__.py # gns3server/compute/qemu/qemu_vm.py # gns3server/controller/compute.py # gns3server/controller/gns3vm/virtualbox_gns3_vm.py # gns3server/controller/node.py # gns3server/controller/project.py # gns3server/crash_report.py # gns3server/handlers/api/controller/template_handler.py # gns3server/static/web-ui/index.html # gns3server/static/web-ui/main.8448c96e4facbe79a613.js # gns3server/version.py # tests/compute/iou/test_iou_vm.py # tests/compute/qemu/test_qemu_vm.py # tests/handlers/api/controller/test_template.py
This commit is contained in:
commit
1ff23348d3
15
CHANGELOG
15
CHANGELOG
@ -1,5 +1,20 @@
|
||||
# Change Log
|
||||
|
||||
## 2.2.40.1 10/06/2023
|
||||
|
||||
* Re-bundle Web-Ui v2.2.40. Fixes #2239
|
||||
|
||||
## 2.2.40 06/06/2023
|
||||
|
||||
* qemu : with network adapter_type equal to "virtio-net-pci", fix the speed to 10000 and duplex to full. The values are actually fake. (https://github.com/GNS3/gns3-gui/issues/3476)
|
||||
* Parse name for request to node creation from template
|
||||
* Remove Xvfb + x11vnc support
|
||||
* Require a Host-Only Network to start the VirtualBox GNS3 VM on macOS with VirtualBox 7
|
||||
* Properly catch aiohttp client exception. Ref #2228
|
||||
* Catch ConnectionResetError when waiting for the wrap console
|
||||
* Fix open IPv6 address for HTTP consoles on controller. Fixes https://github.com/GNS3/gns3-gui/issues/3448
|
||||
* Use proc.communicate() when checking for subprocess output As recommended in https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.subprocess.Process.stderr
|
||||
|
||||
## 2.2.39 08/05/2023
|
||||
|
||||
* Install web-ui v2.2.39
|
||||
|
@ -37,7 +37,8 @@
|
||||
"version": "1.0",
|
||||
"md5sum": "72fb52af76e9561d125dd99224e2c1d1",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/AlmaLinux/almalinux-cloud-init-data.iso"
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/AlmaLinux",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/AlmaLinux/almalinux-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
|
@ -32,6 +32,13 @@
|
||||
"process_priority": "normal"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"filename": "arubaoscx-disk-image-genericx86-p4-20230531220439.vmdk",
|
||||
"version": "10.12.0006",
|
||||
"md5sum": "c4f80fecd02ef93b431b75dd610e0063",
|
||||
"filesize": 384638464,
|
||||
"download_url": "https://asp.arubanetworks.com/"
|
||||
},
|
||||
{
|
||||
"filename": "arubaoscx-disk-image-genericx86-p4-20221130174651.vmdk",
|
||||
"version": "10.11.0001",
|
||||
@ -111,6 +118,12 @@
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"name": "10.12.0006",
|
||||
"images": {
|
||||
"hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20230531220439.vmdk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "10.11.0001",
|
||||
"images": {
|
||||
|
@ -26,33 +26,37 @@
|
||||
"options": "-nographic"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"filename": "CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2",
|
||||
"version": "8.4 (2105)",
|
||||
"md5sum": "032eed270415526546eac07628905a62",
|
||||
"filesize": 1309652992,
|
||||
"download_url": "https://cloud.centos.org/centos/8/x86_64/images",
|
||||
"direct_download_url": "https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2"
|
||||
},
|
||||
{
|
||||
"filename": "CentOS-7-x86_64-GenericCloud-2111.qcow2",
|
||||
"version": "7 (2111)",
|
||||
"md5sum": "730b8662695831670721c8245be61dac",
|
||||
"filesize": 897384448,
|
||||
"download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2111.qcow2"
|
||||
"download_url": "https://cloud.centos.org/centos/7/images",
|
||||
"direct_download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2111.qcow2"
|
||||
},
|
||||
{
|
||||
"filename": "CentOS-7-x86_64-GenericCloud-1809.qcow2",
|
||||
"version": "7 (1809)",
|
||||
"md5sum": "da79108d1324b27bd1759362b82fbe40",
|
||||
"filesize": 914948096,
|
||||
"download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1809.qcow2"
|
||||
},
|
||||
{
|
||||
"filename": "CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2",
|
||||
"version": "8.4 (2105)",
|
||||
"md5sum": "032eed270415526546eac07628905a62",
|
||||
"filesize": 1309652992,
|
||||
"download_url": "https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2"
|
||||
"download_url": "https://cloud.centos.org/centos/7/images",
|
||||
"direct_download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1809.qcow2"
|
||||
},
|
||||
{
|
||||
"filename": "centos-cloud-init-data.iso",
|
||||
"version": "1.0",
|
||||
"md5sum": "15ca60c12db6d13b8eeae1a19613fd6e",
|
||||
"filesize": 378880,
|
||||
"download_url": "https://github.com/asenci/gns3-centos-cloud-init-data/raw/master/centos-cloud-init-data.iso"
|
||||
"version": "1.1",
|
||||
"md5sum": "59ea8223fd659d8bce9081ff175912e9",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/centos-cloud",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/centos-cloud/centos-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
|
@ -44,7 +44,8 @@
|
||||
"version": "1.0",
|
||||
"md5sum": "43f6bf70c178a9d3c270b5c24971e578",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/Debian/debian-cloud-init-data.iso"
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/Debian",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/Debian/debian-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
|
@ -31,14 +31,16 @@
|
||||
"version": "35-1.2",
|
||||
"md5sum": "cfa9cdcfb946e5f4cf9dd4d7906008d0",
|
||||
"filesize": 376897536,
|
||||
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/x86_64/images/Fedora-Cloud-Base-35-1.2.x86_64.qcow2"
|
||||
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/x86_64/images",
|
||||
"direct_download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/x86_64/images/Fedora-Cloud-Base-35-1.2.x86_64.qcow2"
|
||||
},
|
||||
{
|
||||
"filename": "fedora-cloud-init-data.iso",
|
||||
"version": "1.0",
|
||||
"md5sum": "3d0d6391d3f5ece1180c70b9667c4dca",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/fedora-cloud/fedora-cloud-init-data.iso"
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/fedora-cloud",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/fedora-cloud/fedora-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
|
19
gns3server/appliances/mikrotik-winbox.gns3a
Normal file
19
gns3server/appliances/mikrotik-winbox.gns3a
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"appliance_id": "b770027f-1822-4ab6-b2f9-73336ca0983d",
|
||||
"name": "Mikrotik WinBox",
|
||||
"category": "guest",
|
||||
"description": "Mikrotik's WinBox router management software for GNS3",
|
||||
"vendor_name": "Mikrotik",
|
||||
"vendor_url": "https://mikrotik.com",
|
||||
"product_name": "Mikrotik WinBox",
|
||||
"registry_version": 4,
|
||||
"status": "stable",
|
||||
"availability": "free",
|
||||
"maintainer": "Alexander Horner",
|
||||
"maintainer_email": "contact@alexhorner.cc",
|
||||
"docker": {
|
||||
"adapters": 1,
|
||||
"image": "gns3/mikrotik-winbox",
|
||||
"console_type": "vnc"
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
"usage": "In the web interface login as admin/admin\n\nPersistent configuration:\n- Add \"/var/lib/redis\" as an additional persistent directory.\n- Use \"redis-cli save\" in an auxiliary console to save the configuration.",
|
||||
"docker": {
|
||||
"adapters": 1,
|
||||
"image": "ntop/ntopng:latest",
|
||||
"image": "ntop/ntopng:stable",
|
||||
"start_command": "--dns-mode 2 --interface eth0",
|
||||
"console_type": "http",
|
||||
"console_http_port": 3000,
|
||||
|
70
gns3server/appliances/oracle-linux-cloud.gns3a
Normal file
70
gns3server/appliances/oracle-linux-cloud.gns3a
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"appliance_id": "88e67f45-e0de-4e5e-9f36-2dc83e4a6c60",
|
||||
"name": "Oracle Linux Cloud Guest",
|
||||
"category": "guest",
|
||||
"description": "A highly performant and secure operating environment, Oracle Linux delivers virtualization, management, automation, and cloud native computing tools, along with the operating system, in a single, easy-to-manage support offering. Oracle Linux provides a 100% application binary compatible alternative to Red Hat Enterprise Linux and CentOS Linux and is supported across both hybrid and multicloud environments.",
|
||||
"vendor_name": "Oracle Corporation",
|
||||
"vendor_url": "https://www.oracle.com",
|
||||
"documentation_url": "https://docs.oracle.com/en-us/iaas/images/",
|
||||
"product_name": "Oracle Linux Cloud Guest",
|
||||
"product_url": "https://www.oracle.com/au/linux/",
|
||||
"registry_version": 4,
|
||||
"status": "stable",
|
||||
"maintainer": "GNS3 Team",
|
||||
"maintainer_email": "developers@gns3.net",
|
||||
"usage": "Username: oracle\nPassword: oracle",
|
||||
"port_name_format": "Ethernet{0}",
|
||||
"qemu": {
|
||||
"adapter_type": "virtio-net-pci",
|
||||
"adapters": 1,
|
||||
"ram": 1024,
|
||||
"hda_disk_interface": "virtio",
|
||||
"arch": "x86_64",
|
||||
"console_type": "telnet",
|
||||
"boot_priority": "c",
|
||||
"kvm": "require",
|
||||
"options": "-cpu host -nographic"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"filename": "OL9U1_x86_64-kvm-b158.qcow",
|
||||
"version": "9.1",
|
||||
"md5sum": "9f32851b96fc38191892197fa11f7435",
|
||||
"filesize": 539033600,
|
||||
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
|
||||
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL9/u1/x86_64/OL9U1_x86_64-kvm-b158.qcow"
|
||||
},
|
||||
{
|
||||
"filename": "OL8U7_x86_64-kvm-b148.qcow",
|
||||
"version": "8.7",
|
||||
"md5sum": "962cdde7e810888b9914e937222f687f",
|
||||
"filesize": 913965056,
|
||||
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
|
||||
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL8/u7/x86_64/OL8U7_x86_64-kvm-b148.qcow"
|
||||
},
|
||||
{
|
||||
"filename": "oracle-cloud-init-data.iso",
|
||||
"version": "1.1",
|
||||
"md5sum": "cb51bc42ae9dfb7345bfa7362a313baf",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/oracle-cloud",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/oracle-cloud/oracle-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"name": "9.1",
|
||||
"images": {
|
||||
"hda_disk_image": "OL9U1_x86_64-kvm-b158.qcow",
|
||||
"cdrom_image": "oracle-cloud-init-data.iso"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "8.7",
|
||||
"images": {
|
||||
"hda_disk_image": "OL8U7_x86_64-kvm-b148.qcow",
|
||||
"cdrom_image": "oracle-cloud-init-data.iso"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -39,7 +39,8 @@
|
||||
"version": "1.0",
|
||||
"md5sum": "33ffda3a81436e305f37fb913edd6d43",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/rocky-cloud/rocky-cloud-init-data.iso"
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/rocky-cloud",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/rocky-cloud/rocky-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
|
@ -29,51 +29,34 @@
|
||||
{
|
||||
"filename": "ubuntu-22.04-server-cloudimg-amd64.img",
|
||||
"version": "22.04 (LTS)",
|
||||
"md5sum": "ac2351289daa173fa1ed6b2b81d81d7c",
|
||||
"filesize": 624295936,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
"md5sum": "3ce0b84f9592482fb645e8253b979827",
|
||||
"filesize": 686096384,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/jammy/release",
|
||||
"direct_download_url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
},
|
||||
{
|
||||
"filename": "ubuntu-20.04-server-cloudimg-amd64.img",
|
||||
"version": "20.04 (LTS)",
|
||||
"md5sum": "044bc979b2238192ee3edb44e2bb6405",
|
||||
"filesize": 552337408,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20210119.1/ubuntu-20.04-server-cloudimg-amd64.img"
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20210119.1/",
|
||||
"direct_download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20210119.1/ubuntu-20.04-server-cloudimg-amd64.img"
|
||||
},
|
||||
{
|
||||
"filename": "ubuntu-18.04-server-cloudimg-amd64.img",
|
||||
"version": "18.04 (LTS)",
|
||||
"md5sum": "f4134e7fa16d7fa766c7467cbe25c949",
|
||||
"filesize": 336134144,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/18.04/release-20180426.2/ubuntu-18.04-server-cloudimg-amd64.img"
|
||||
},
|
||||
{
|
||||
"filename": "ubuntu-17.10-server-cloudimg-amd64.img",
|
||||
"version": "17.10",
|
||||
"md5sum": "331b44f2b05858c251b3ea92c8b65152",
|
||||
"filesize": 320405504,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/17.10/release-20180404/ubuntu-17.10-server-cloudimg-amd64.img"
|
||||
},
|
||||
{
|
||||
"filename": "ubuntu-16.04-server-cloudimg-amd64-disk1.img",
|
||||
"version": "16.04 (LTS)",
|
||||
"md5sum": "22c124ba65ea096cdef8b0a197dd613a",
|
||||
"filesize": 290193408,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/16.04/release-20180405/ubuntu-16.04-server-cloudimg-amd64-disk1.img"
|
||||
},
|
||||
{
|
||||
"filename": "ubuntu-14.04-server-cloudimg-amd64-disk1.img",
|
||||
"version": "14.04 (LTS)",
|
||||
"md5sum": "d11b89321d41d0eeddcacf73bf0d2262",
|
||||
"filesize": 262668800,
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/14.04/release-20180404/ubuntu-14.04-server-cloudimg-amd64-disk1.img"
|
||||
"download_url": "https://cloud-images.ubuntu.com/releases/18.04/release-20180426.2/",
|
||||
"direct_download_url": "https://cloud-images.ubuntu.com/releases/18.04/release-20180426.2/ubuntu-18.04-server-cloudimg-amd64.img"
|
||||
},
|
||||
{
|
||||
"filename": "ubuntu-cloud-init-data.iso",
|
||||
"version": "1.0",
|
||||
"md5sum": "328469100156ae8dbf262daa319c27ff",
|
||||
"filesize": 131072,
|
||||
"download_url": "https://github.com/asenci/gns3-ubuntu-cloud-init-data/raw/master/ubuntu-cloud-init-data.iso"
|
||||
"version": "1.1",
|
||||
"md5sum": "9a90ee8f88736204c756015b3cd86500",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/ubuntu-cloud",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/ubuntu-cloud/ubuntu-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
@ -97,27 +80,6 @@
|
||||
"hda_disk_image": "ubuntu-18.04-server-cloudimg-amd64.img",
|
||||
"cdrom_image": "ubuntu-cloud-init-data.iso"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "17.10",
|
||||
"images": {
|
||||
"hda_disk_image": "ubuntu-17.10-server-cloudimg-amd64.img",
|
||||
"cdrom_image": "ubuntu-cloud-init-data.iso"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "16.04 (LTS)",
|
||||
"images": {
|
||||
"hda_disk_image": "ubuntu-16.04-server-cloudimg-amd64-disk1.img",
|
||||
"cdrom_image": "ubuntu-cloud-init-data.iso"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "14.04 (LTS)",
|
||||
"images": {
|
||||
"hda_disk_image": "ubuntu-14.04-server-cloudimg-amd64-disk1.img",
|
||||
"cdrom_image": "ubuntu-cloud-init-data.iso"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ class Docker(BaseManager):
|
||||
)
|
||||
except aiohttp.ClientError as e:
|
||||
raise DockerError(f"Docker has returned an error: {e}")
|
||||
except (asyncio.TimeoutError):
|
||||
except asyncio.TimeoutError:
|
||||
raise DockerError("Docker timeout " + method + " " + path)
|
||||
if response.status >= 300:
|
||||
body = await response.read()
|
||||
|
@ -107,7 +107,6 @@ class DockerVM(BaseNode):
|
||||
self._ethernet_adapters = []
|
||||
self._temporary_directory = None
|
||||
self._telnet_servers = []
|
||||
self._xvfb_process = None
|
||||
self._vnc_process = None
|
||||
self._vncconfig_process = None
|
||||
self._console_resolution = console_resolution
|
||||
@ -681,8 +680,8 @@ class DockerVM(BaseNode):
|
||||
self._display = self._get_free_display_port()
|
||||
tigervnc_path = shutil.which("Xtigervnc") or shutil.which("Xvnc")
|
||||
|
||||
if not (tigervnc_path or shutil.which("Xvfb") and shutil.which("x11vnc")):
|
||||
raise DockerError("Please install TigerVNC (recommended) or Xvfb + x11vnc before using VNC support")
|
||||
if not tigervnc_path:
|
||||
raise DockerError("Please install TigerVNC server before using VNC support")
|
||||
|
||||
if tigervnc_path:
|
||||
with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd:
|
||||
@ -696,29 +695,6 @@ class DockerVM(BaseNode):
|
||||
"-SecurityTypes", "None",
|
||||
":{}".format(self._display),
|
||||
stdout=fd, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
if restart is False:
|
||||
self._xvfb_process = await asyncio.create_subprocess_exec("Xvfb",
|
||||
"-nolisten", "tcp",
|
||||
"-extension", "MIT-SHM",
|
||||
":{}".format(self._display),
|
||||
"-screen", "0",
|
||||
self._console_resolution + "x16")
|
||||
|
||||
# We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569
|
||||
with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd:
|
||||
self._vnc_process = await asyncio.create_subprocess_exec("x11vnc",
|
||||
"-forever",
|
||||
"-nopw",
|
||||
"-shared",
|
||||
"-noshm",
|
||||
"-geometry", self._console_resolution,
|
||||
"-display", "WAIT:{}".format(self._display),
|
||||
"-rfbport", str(self.console),
|
||||
"-rfbportv6", str(self.console),
|
||||
"-noncache",
|
||||
"-listen", self._manager.port_manager.console_host,
|
||||
stdout=fd, stderr=subprocess.STDOUT)
|
||||
|
||||
async def _start_vnc(self):
|
||||
"""
|
||||
@ -727,8 +703,8 @@ class DockerVM(BaseNode):
|
||||
|
||||
self._display = self._get_free_display_port()
|
||||
tigervnc_path = shutil.which("Xtigervnc") or shutil.which("Xvnc")
|
||||
if not (tigervnc_path or shutil.which("Xvfb") and shutil.which("x11vnc")):
|
||||
raise DockerError("Please install TigerVNC server (recommended) or Xvfb + x11vnc before using VNC support")
|
||||
if not tigervnc_path:
|
||||
raise DockerError("Please install TigerVNC server before using VNC support")
|
||||
await self._start_vnc_process()
|
||||
x11_socket = os.path.join("/tmp/.X11-unix/", f"X{self._display}")
|
||||
try:
|
||||
@ -1002,12 +978,6 @@ class DockerVM(BaseNode):
|
||||
await self._vnc_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
if self._xvfb_process:
|
||||
try:
|
||||
self._xvfb_process.terminate()
|
||||
await self._xvfb_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
|
||||
if self._display:
|
||||
display = f"/tmp/.X11-unix/X{self._display}"
|
||||
|
@ -2374,6 +2374,8 @@ class QemuVM(BaseNode):
|
||||
mac = int_to_macaddress(macaddress_to_int(custom_mac_address))
|
||||
|
||||
device_string = f"{adapter_type},mac={mac}"
|
||||
if adapter_type == "virtio-net-pci":
|
||||
device_string = "{},speed=10000,duplex=full".format(device_string)
|
||||
bridge_id = math.floor(pci_device_id / 32)
|
||||
if bridge_id > 0:
|
||||
if pci_bridges_created < bridge_id:
|
||||
|
@ -124,18 +124,18 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
continue
|
||||
return interface
|
||||
|
||||
async def _look_for_vboxnet(self, interface_number):
|
||||
async def _look_for_vboxnet(self, backend_type, interface_number):
|
||||
"""
|
||||
Look for the VirtualBox network name associated with a host only interface.
|
||||
Look for the VirtualBox network name associated with an interface.
|
||||
|
||||
:returns: None or vboxnet name
|
||||
"""
|
||||
|
||||
result = await self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in result.splitlines():
|
||||
if "=" in info:
|
||||
name, value = info.split("=", 1)
|
||||
if name == f"hostonlyadapter{interface_number}":
|
||||
if '=' in info:
|
||||
name, value = info.split('=', 1)
|
||||
if name == "{}{}".format(backend_type, interface_number):
|
||||
return value.strip('"')
|
||||
return None
|
||||
|
||||
@ -161,7 +161,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def _check_vboxnet_exists(self, vboxnet):
|
||||
async def _check_vboxnet_exists(self, vboxnet, vboxnet_type):
|
||||
"""
|
||||
Check if the vboxnet interface exists
|
||||
|
||||
@ -169,7 +169,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
properties = await self._execute("list", ["hostonlyifs"])
|
||||
properties = await self._execute("list", ["{}".format(vboxnet_type)])
|
||||
for prop in properties.splitlines():
|
||||
try:
|
||||
name, value = prop.split(":", 1)
|
||||
@ -232,25 +232,43 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
if nat_interface_number < 0:
|
||||
raise GNS3VMError(f'VM "{self.vmname}" must have a NAT interface configured in order to start')
|
||||
|
||||
hostonly_interface_number = await self._look_for_interface("hostonly")
|
||||
if hostonly_interface_number < 0:
|
||||
raise GNS3VMError(f'VM "{self.vmname}" must have a host-only interface configured in order to start')
|
||||
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
|
||||
backend_type = "hostonly-network"
|
||||
backend_description = "host-only network"
|
||||
vboxnet_type = "hostonlynets"
|
||||
interface_number = await self._look_for_interface("hostonlynetwork")
|
||||
if interface_number < 0:
|
||||
raise GNS3VMError('VM "{}" must have a network adapter attached to a host-only network in order to start'.format(self.vmname))
|
||||
else:
|
||||
backend_type = "hostonlyadapter"
|
||||
backend_description = "host-only adapter"
|
||||
vboxnet_type = "hostonlyifs"
|
||||
interface_number = await self._look_for_interface("hostonly")
|
||||
|
||||
vboxnet = await self._look_for_vboxnet(hostonly_interface_number)
|
||||
if interface_number < 0:
|
||||
raise GNS3VMError('VM "{}" must have a network adapter attached to a {} in order to start'.format(self.vmname, backend_description))
|
||||
|
||||
vboxnet = await self._look_for_vboxnet(backend_type, interface_number)
|
||||
if vboxnet is None:
|
||||
raise GNS3VMError(
|
||||
f'A VirtualBox host-only network could not be found on network adapter {hostonly_interface_number} for "{self._vmname}"'
|
||||
)
|
||||
raise GNS3VMError('A VirtualBox host-only network could not be found on network adapter {} for "{}"'.format(interface_number, self._vmname))
|
||||
|
||||
if not (await self._check_vboxnet_exists(vboxnet)):
|
||||
raise GNS3VMError(
|
||||
'VirtualBox host-only network "{}" does not exist, please make the sure the network adapter {} configuration is valid for "{}"'.format(
|
||||
vboxnet, hostonly_interface_number, self._vmname
|
||||
)
|
||||
)
|
||||
if not (await self._check_vboxnet_exists(vboxnet, vboxnet_type)):
|
||||
if sys.platform.startswith("win") and vboxnet == "vboxnet0":
|
||||
# The GNS3 VM is configured with vboxnet0 by default which is not available
|
||||
# on Windows. Try to patch this with the first available vboxnet we find.
|
||||
first_available_vboxnet = await self._find_first_available_vboxnet()
|
||||
if first_available_vboxnet is None:
|
||||
raise GNS3VMError('Please add a VirtualBox host-only network with DHCP enabled and attached it to network adapter {} for "{}"'.format(interface_number, self._vmname))
|
||||
await self.set_hostonly_network(interface_number, first_available_vboxnet)
|
||||
vboxnet = first_available_vboxnet
|
||||
else:
|
||||
raise GNS3VMError('VirtualBox host-only network "{}" does not exist, please make the sure the network adapter {} configuration is valid for "{}"'.format(vboxnet,
|
||||
interface_number,
|
||||
self._vmname))
|
||||
|
||||
if not (await self._check_dhcp_server(vboxnet)):
|
||||
raise GNS3VMError(f'DHCP must be enabled on VirtualBox host-only network "{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))
|
||||
|
||||
vm_state = await self._get_state()
|
||||
log.info(f'"{self._vmname}" state is {vm_state}')
|
||||
@ -295,8 +313,8 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
[self._vmname, f"natpf{nat_interface_number}", f"GNS3VM,tcp,{ip_address},{api_port},,{self.port}"],
|
||||
)
|
||||
|
||||
self.ip_address = await self._get_ip(hostonly_interface_number, api_port)
|
||||
log.info(f"GNS3 VM has been started with IP {self.ip_address}")
|
||||
self.ip_address = await self._get_ip(interface_number, api_port)
|
||||
log.info("GNS3 VM has been started with IP {}".format(self.ip_address))
|
||||
self.running = True
|
||||
|
||||
async def _get_ip(self, hostonly_interface_number, api_port):
|
||||
|
1
gns3server/static/web-ui/26.49028ab13de5de406c90.js
Normal file
1
gns3server/static/web-ui/26.49028ab13de5de406c90.js
Normal file
File diff suppressed because one or more lines are too long
1
gns3server/static/web-ui/runtime.baa1121a4737aeb68bb7.js
Normal file
1
gns3server/static/web-ui/runtime.baa1121a4737aeb68bb7.js
Normal file
@ -0,0 +1 @@
|
||||
!function(){"use strict";var e,v={},g={};function n(e){var a=g[e];if(void 0!==a)return a.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e](t,t.exports,n),t.loaded=!0,t.exports}n.m=v,e=[],n.O=function(a,t,u,o){if(!t){var r=1/0;for(i=0;i<e.length;i++){t=e[i][0],u=e[i][1],o=e[i][2];for(var l=!0,f=0;f<t.length;f++)(!1&o||r>=o)&&Object.keys(n.O).every(function(b){return n.O[b](t[f])})?t.splice(f--,1):(l=!1,o<r&&(r=o));if(l){e.splice(i--,1);var s=u();void 0!==s&&(a=s)}}return a}o=o||0;for(var i=e.length;i>0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,u,o]},n.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(a,{a:a}),a},n.d=function(e,a){for(var t in a)n.o(a,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce(function(a,t){return n.f[t](e,a),a},[]))},n.u=function(e){return e+".49028ab13de5de406c90.js"},n.miniCssF=function(e){return"styles.f8555f2eecf8cf87f666.css"},n.hmd=function(e){return(e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:function(){throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e},n.o=function(e,a){return Object.prototype.hasOwnProperty.call(e,a)},function(){var e={},a="gns3-web-ui:";n.l=function(t,u,o,i){if(e[t])e[t].push(u);else{var r,l;if(void 0!==o)for(var f=document.getElementsByTagName("script"),s=0;s<f.length;s++){var c=f[s];if(c.getAttribute("src")==t||c.getAttribute("data-webpack")==a+o){r=c;break}}r||(l=!0,(r=document.createElement("script")).charset="utf-8",r.timeout=120,n.nc&&r.setAttribute("nonce",n.nc),r.setAttribute("data-webpack",a+o),r.src=n.tu(t)),e[t]=[u];var d=function(h,b){r.onerror=r.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],r.parentNode&&r.parentNode.removeChild(r),_&&_.forEach(function(m){return m(b)}),h)return h(b)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=d.bind(null,r.onerror),r.onload=d.bind(null,r.onload),l&&document.head.appendChild(r)}}}(),n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},function(){var e;n.tu=function(a){return void 0===e&&(e={createScriptURL:function(t){return t}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(a)}}(),n.p="",function(){var e={666:0};n.f.j=function(u,o){var i=n.o(e,u)?e[u]:void 0;if(0!==i)if(i)o.push(i[2]);else if(666!=u){var r=new Promise(function(c,d){i=e[u]=[c,d]});o.push(i[2]=r);var l=n.p+n.u(u),f=new Error;n.l(l,function(c){if(n.o(e,u)&&(0!==(i=e[u])&&(e[u]=void 0),i)){var d=c&&("load"===c.type?"missing":c.type),p=c&&c.target&&c.target.src;f.message="Loading chunk "+u+" failed.\n("+d+": "+p+")",f.name="ChunkLoadError",f.type=d,f.request=p,i[1](f)}},"chunk-"+u,u)}else e[u]=0},n.O.j=function(u){return 0===e[u]};var a=function(u,o){var f,s,i=o[0],r=o[1],l=o[2],c=0;for(f in r)n.o(r,f)&&(n.m[f]=r[f]);if(l)var d=l(n);for(u&&u(o);c<i.length;c++)n.o(e,s=i[c])&&e[s]&&e[s][0](),e[i[c]]=0;return n.O(d)},t=self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[];t.forEach(a.bind(null,0)),t.push=a.bind(null,t.push.bind(t))}()}();
|
@ -70,10 +70,10 @@ async def subprocess_check_output(*args, cwd=None, env=None, stderr=False):
|
||||
|
||||
if stderr:
|
||||
proc = await asyncio.create_subprocess_exec(*args, stderr=asyncio.subprocess.PIPE, cwd=cwd, env=env)
|
||||
output = await proc.stderr.read()
|
||||
_, output = await proc.communicate()
|
||||
else:
|
||||
proc = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL, cwd=cwd, env=env)
|
||||
output = await proc.stdout.read()
|
||||
output, _ = await proc.communicate()
|
||||
if output is None:
|
||||
return ""
|
||||
# If we received garbage we ignore invalid characters
|
||||
|
@ -26,6 +26,7 @@ function help {
|
||||
echo "--with-openvpn: Install OpenVPN" >&2
|
||||
echo "--with-iou: Install IOU" >&2
|
||||
echo "--with-i386-repository: Add the i386 repositories required by IOU if they are not already available on the system. Warning: this will replace your source.list in order to use the official Ubuntu mirror" >&2
|
||||
echo "--with-welcome: Install GNS3-VM welcome.py script" >&2
|
||||
echo "--without-kvm: Disable KVM, required if system do not support it (limitation in some hypervisors and cloud providers). Warning: only disable KVM if strictly necessary as this will degrade performance" >&2
|
||||
echo "--unstable: Use the GNS3 unstable repository"
|
||||
echo "--help: This help" >&2
|
||||
@ -48,8 +49,9 @@ USE_IOU=0
|
||||
I386_REPO=0
|
||||
DISABLE_KVM=0
|
||||
UNSTABLE=0
|
||||
WELCOME_SETUP=0
|
||||
|
||||
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,without-kvm,unstable,help -n 'gns3-remote-install.sh' -- "$@"`
|
||||
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,with-welcome,without-kvm,unstable,help -n 'gns3-remote-install.sh' -- "$@"`
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
help
|
||||
@ -72,6 +74,10 @@ while true ; do
|
||||
I386_REPO=1
|
||||
shift
|
||||
;;
|
||||
--with-welcome)
|
||||
WELCOME_SETUP=1
|
||||
shift
|
||||
;;
|
||||
--without-kvm)
|
||||
DISABLE_KVM=1
|
||||
shift
|
||||
@ -159,7 +165,7 @@ apt-get install -y gns3-server
|
||||
log "Create user GNS3 with /opt/gns3 as home directory"
|
||||
if [ ! -d "/opt/gns3/" ]
|
||||
then
|
||||
useradd -d /opt/gns3/ -m gns3
|
||||
useradd -m -d /opt/gns3/ gns3
|
||||
fi
|
||||
|
||||
|
||||
@ -296,6 +302,37 @@ fi
|
||||
|
||||
log "GNS3 installed with success"
|
||||
|
||||
if [ $WELCOME_SETUP == 1 ]
|
||||
then
|
||||
NEEDRESTART_MODE=a apt-get install -y net-tools
|
||||
NEEDRESTART_MODE=a apt-get install -y python3-pip
|
||||
NEEDRESTART_MODE=a apt-get install -y dialog
|
||||
pip install --no-input --upgrade pip
|
||||
pip install --no-input pythondialog
|
||||
|
||||
#Pull down welcome script from repo
|
||||
curl https://raw.githubusercontent.com/GNS3/gns3-server/master/scripts/welcome.py > /usr/local/bin/welcome.py
|
||||
|
||||
chmod 755 /usr/local/bin/welcome.py
|
||||
chown gns3:gns3 /usr/local/bin/welcome.py
|
||||
|
||||
mkdir /etc/systemd/system/getty@tty1.service.d
|
||||
cat <<EOFI > /etc/systemd/system/getty@tty1.service.d/override.conf
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=-/sbin/agetty -a gns3 --noclear %I \$TERM
|
||||
EOFI
|
||||
|
||||
chmod 755 /etc/systemd/system/getty@tty1.service.d/override.conf
|
||||
chown root:root /etc/systemd/system/getty@tty1.service.d/override.conf
|
||||
|
||||
echo "python3 /usr/local/bin/welcome.py" >> /opt/gns3/.bashrc
|
||||
echo "gns3:gns3" | chpasswd
|
||||
usermod --shell /bin/bash gns3
|
||||
usermod -aG sudo gns3
|
||||
|
||||
fi
|
||||
|
||||
if [ $USE_VPN == 1 ]
|
||||
then
|
||||
log "Setup VPN"
|
||||
@ -417,3 +454,12 @@ service gns3 start
|
||||
log "Download http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn to setup your OpenVPN client after rebooting the server"
|
||||
|
||||
fi
|
||||
|
||||
if [ $WELCOME_SETUP == 1 ]
|
||||
then
|
||||
NEEDRESTART_MODE=a apt-get update
|
||||
NEEDRESTART_MODE=a apt-get upgrade
|
||||
python3 -c 'import sys; sys.path.append("/usr/local/bin/"); import welcome; ws = welcome.Welcome_dialog(); ws.repair_remote_install()'
|
||||
cd /opt/gns3
|
||||
su gns3
|
||||
fi
|
479
scripts/welcome.py
Normal file
479
scripts/welcome.py
Normal file
@ -0,0 +1,479 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2015 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import locale
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import configparser
|
||||
from json import loads as convert
|
||||
import urllib.request
|
||||
from dialog import Dialog, PythonDialogBug
|
||||
|
||||
|
||||
class Welcome_dialog:
|
||||
def __init__(self):
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except locale.Error:
|
||||
# Not supported via SSH
|
||||
pass
|
||||
self.display = Dialog(dialog="dialog", autowidgetsize=True)
|
||||
if self.gns3_version() is None:
|
||||
self.display.set_background_title("GNS3")
|
||||
else:
|
||||
self.display.set_background_title("GNS3 {}".format(self.gns3_version()))
|
||||
|
||||
def get_ip(self):
|
||||
"""
|
||||
Return the active IP
|
||||
"""
|
||||
#request 'ip addr' data in JSON format from shell
|
||||
ip_addr_response = subprocess.run(['ip', '--json', 'addr'],capture_output=True)
|
||||
|
||||
#process response, decode and use json.loads to convert the string to a dict
|
||||
ip_addr_data = convert(ip_addr_response.stdout.decode("utf-8"))
|
||||
|
||||
#search ip_addr_data for the first ip adress that is not under a virtual bridge or loopback interface
|
||||
for i in ip_addr_data:
|
||||
if ('virbr' in i['ifname']) or ('lo' in i['ifname']):
|
||||
continue
|
||||
try:
|
||||
if 'UP' in i['flags']:
|
||||
ip_addr = i['addr_info'][0]['local']
|
||||
break
|
||||
except:
|
||||
continue
|
||||
ip_addr = None
|
||||
|
||||
return ip_addr
|
||||
|
||||
def repair_remote_install(self):
|
||||
"""
|
||||
This method is only called by remote-install.sh during setup to ensure it is setting the same IP as shown by Dialog
|
||||
"""
|
||||
ip_addr = self.get_ip()
|
||||
subprocess.run(["sed", "-i", f"s/host = 0.0.0.0/host = {ip_addr}/", "/etc/gns3/gns3_server.conf"],capture_output=False)
|
||||
subprocess.run(["service", "gns3", "stop"],capture_output=False)
|
||||
subprocess.run(["service", "gns3", "start"],capture_output=False)
|
||||
|
||||
|
||||
def get_config(self):
|
||||
"""
|
||||
Read the config
|
||||
"""
|
||||
config = configparser.RawConfigParser()
|
||||
path = os.path.expanduser("~/.config/GNS3/gns3_server.conf")
|
||||
config.read([path], encoding="utf-8")
|
||||
return config
|
||||
|
||||
|
||||
def write_config(self, config):
|
||||
"""
|
||||
Write the config file
|
||||
"""
|
||||
|
||||
with open(os.path.expanduser("~/.config/GNS3/gns3_server.conf"), 'w') as f:
|
||||
config.write(f)
|
||||
|
||||
|
||||
def gns3_major_version(self):
|
||||
"""
|
||||
Returns the GNS3 major server version
|
||||
"""
|
||||
|
||||
version = self.gns3_version()
|
||||
if version:
|
||||
match = re.search(r"\d+.\d+", version)
|
||||
return match.group(0)
|
||||
return ""
|
||||
|
||||
|
||||
def gns3_version(self):
|
||||
"""
|
||||
Return the GNS3 server version
|
||||
"""
|
||||
try:
|
||||
return subprocess.check_output(["gns3server", "--version"]).strip().decode()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return None
|
||||
|
||||
|
||||
def gns3vm_version(self):
|
||||
"""
|
||||
Return the GNS3 VM version
|
||||
"""
|
||||
try:
|
||||
with open('/home/gns3/.config/GNS3/gns3vm_version') as f:
|
||||
return f.read().strip()
|
||||
except FileNotFoundError:
|
||||
return "Remote Install"
|
||||
|
||||
|
||||
def mode(self):
|
||||
if self.display.yesno("This feature is for testers only. You may break your GNS3 installation. Are you REALLY sure you want to continue?", yes_label="Exit (Safe option)", no_label="Continue") == self.display.OK:
|
||||
return
|
||||
code, tag = self.display.menu("Select the GNS3 version",
|
||||
choices=[("2.1", "Stable release for this GNS3 VM (RECOMMENDED)"),
|
||||
("2.1dev", "Development version for stable release"),
|
||||
("2.2", "Latest stable release")])
|
||||
self.display.clear()
|
||||
if code == Dialog.OK:
|
||||
os.makedirs(os.path.expanduser("~/.config/GNS3"), exist_ok=True)
|
||||
with open(os.path.expanduser("~/.config/GNS3/gns3_release"), "w+") as f:
|
||||
f.write(tag)
|
||||
|
||||
self.update(force=True)
|
||||
|
||||
|
||||
def get_release(self):
|
||||
try:
|
||||
with open(os.path.expanduser("~/.config/GNS3/gns3_release")) as f:
|
||||
content = f.read()
|
||||
|
||||
# Support old VM versions
|
||||
if content == "stable":
|
||||
content = "1.5"
|
||||
elif content == "testing":
|
||||
content = "1.5"
|
||||
elif content == "unstable":
|
||||
content = "1.5dev"
|
||||
|
||||
return content
|
||||
except OSError:
|
||||
return "1.5"
|
||||
|
||||
|
||||
def update(self, force=False):
|
||||
if not force:
|
||||
if self.display.yesno("PLEASE SNAPSHOT THE VM BEFORE RUNNING THE UPGRADE IN CASE OF FAILURE. The server will reboot at the end of the upgrade process. Continue?") != self.display.OK:
|
||||
return
|
||||
release = self.get_release()
|
||||
if release == "2.2":
|
||||
if self.display.yesno("It is recommended to run GNS3 version 2.2 with lastest GNS3 VM based on Ubuntu 18.04 LTS, please download this VM from our website or continue at your own risk!") != self.display.OK:
|
||||
return
|
||||
if release.endswith("dev"):
|
||||
ret = os.system("curl -Lk https://raw.githubusercontent.com/GNS3/gns3-vm/unstable/scripts/update_{}.sh > /tmp/update.sh && bash -x /tmp/update.sh".format(release))
|
||||
else:
|
||||
ret = os.system("curl -Lk https://raw.githubusercontent.com/GNS3/gns3-vm/master/scripts/update_{}.sh > /tmp/update.sh && bash -x /tmp/update.sh".format(release))
|
||||
if ret != 0:
|
||||
print("ERROR DURING UPGRADE PROCESS PLEASE TAKE A SCREENSHOT IF YOU NEED SUPPORT")
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
def migrate(self):
|
||||
"""
|
||||
Migrate GNS3 VM data.
|
||||
"""
|
||||
|
||||
code, option = self.display.menu("Select an option",
|
||||
choices=[("Setup", "Configure this VM to send data to another GNS3 VM"),
|
||||
("Send", "Send images and projects to another GNS3 VM")])
|
||||
self.display.clear()
|
||||
if code == Dialog.OK:
|
||||
(answer, destination) = self.display.inputbox("What is IP address or hostname of the other GNS3 VM?", init="172.16.1.128")
|
||||
if answer != self.display.OK:
|
||||
return
|
||||
if destination == self.get_ip():
|
||||
self.display.msgbox("The destination cannot be the same as this VM IP address ({})".format(destination))
|
||||
return
|
||||
if option == "Send":
|
||||
# first make sure they are no files belonging to root
|
||||
os.system("sudo chown -R gns3:gns3 /opt/gns3")
|
||||
# then rsync the data
|
||||
command = r"rsync -az --progress -e 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /home/gns3/.ssh/gns3-vm-key' /opt/gns3 gns3@{}:/opt".format(destination)
|
||||
ret = os.system('bash -c "{}"'.format(command))
|
||||
time.sleep(10)
|
||||
if ret != 0:
|
||||
self.display.msgbox("Could not send data to the other GNS3 VM located at {}".format(destination))
|
||||
else:
|
||||
self.display.msgbox("Images and projects have been successfully sent to the other GNS3 VM located at {}".format(destination))
|
||||
elif option == "Setup":
|
||||
script = """
|
||||
if [ ! -f ~/.ssh/gns3-vm-key ]
|
||||
then
|
||||
ssh-keygen -f ~/.ssh/gns3-vm-key -N '' -C gns3@{}
|
||||
fi
|
||||
ssh-copy-id -i ~/.ssh/gns3-vm-key gns3@{}
|
||||
""".format(self.get_ip(), destination)
|
||||
ret = os.system('bash -c "{}"'.format(script))
|
||||
time.sleep(10)
|
||||
if ret != 0:
|
||||
self.display.msgbox("Error while setting up the migrate feature")
|
||||
else:
|
||||
self.display.msgbox("Configuration successful, you can now send data to the GNS3 VM located at {} without password".format(destination))
|
||||
|
||||
|
||||
def shrink_disk(self):
|
||||
|
||||
ret = os.system("lspci | grep -i vmware")
|
||||
if ret != 0:
|
||||
self.display.msgbox("Shrinking the disk is only supported when running inside VMware")
|
||||
return
|
||||
|
||||
if self.display.yesno("Would you like to shrink the VM disk? The VM will reboot at the end of the process. Continue?") != self.display.OK:
|
||||
return
|
||||
|
||||
os.system("sudo service gns3 stop")
|
||||
os.system("sudo service docker stop")
|
||||
os.system("sudo vmware-toolbox-cmd disk shrink /opt")
|
||||
os.system("sudo vmware-toolbox-cmd disk shrink /")
|
||||
|
||||
self.display.msgbox("The GNS3 VM will reboot")
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "reboot"])
|
||||
|
||||
def vm_information(self):
|
||||
"""
|
||||
Show IP, SSH settings....
|
||||
"""
|
||||
|
||||
content = "Welcome to GNS3 appliance\n\n"
|
||||
|
||||
version = self.gns3_version()
|
||||
if version is None:
|
||||
content += "GNS3 is not installed please install it with sudo pip3 install gns3-server. Or download a preinstalled VM.\n\n"
|
||||
else:
|
||||
content = "GNS3 version: {gns3_version}\nVM version: {gns3vm_version}\nKVM support available: {kvm}\n\n".format(
|
||||
gns3vm_version=self.gns3vm_version(),
|
||||
gns3_version=version,
|
||||
kvm=self.kvm_support())
|
||||
|
||||
ip = self.get_ip()
|
||||
|
||||
if ip:
|
||||
content += f"""
|
||||
IP: {ip}
|
||||
Web UI: http://{ip}:3080
|
||||
|
||||
To log in using SSH:
|
||||
ssh gns3@{ip}
|
||||
Password: gns3
|
||||
|
||||
Images and projects are located in /opt/gns3
|
||||
""".strip()
|
||||
|
||||
else:
|
||||
content += "eth0 is not configured. Please manually configure it via the Networking menu."
|
||||
|
||||
content += "\n\nRelease channel: " + self.get_release()
|
||||
|
||||
try:
|
||||
self.display.msgbox(content)
|
||||
# If it's an scp command or any bugs
|
||||
except:
|
||||
os.execvp("bash", ['/bin/bash'])
|
||||
|
||||
|
||||
def check_internet_connectivity(self):
|
||||
self.display.pause("Please wait...\n\n")
|
||||
try:
|
||||
response = urllib.request.urlopen('http://pypi.python.org/', timeout=5)
|
||||
except urllib.request.URLError as err:
|
||||
self.display.infobox("Can't connect to Internet (pypi.python.org): {}".format(str(err)))
|
||||
time.sleep(15)
|
||||
return
|
||||
self.display.infobox("Connection to Internet: OK")
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
def keyboard_configuration():
|
||||
"""
|
||||
Allow user to change the keyboard layout
|
||||
"""
|
||||
os.system("/usr/bin/sudo dpkg-reconfigure keyboard-configuration")
|
||||
|
||||
|
||||
def set_security(self):
|
||||
config = self.get_config()
|
||||
if self.display.yesno("Enable server authentication?") == self.display.OK:
|
||||
if not config.has_section("Server"):
|
||||
config.add_section("Server")
|
||||
config.set("Server", "auth", True)
|
||||
(answer, text) = self.display.inputbox("Login?")
|
||||
if answer != self.display.OK:
|
||||
return
|
||||
config.set("Server", "user", text)
|
||||
(answer, text) = self.display.passwordbox("Password?")
|
||||
if answer != self.display.OK:
|
||||
return
|
||||
config.set("Server", "password", text)
|
||||
else:
|
||||
config.set("Server", "auth", False)
|
||||
|
||||
self.write_config(config)
|
||||
|
||||
|
||||
def log(self):
|
||||
os.system("/usr/bin/sudo chmod 755 /var/log/upstart/gns3.log")
|
||||
with open("/var/log/upstart/gns3.log") as f:
|
||||
try:
|
||||
while True:
|
||||
line = f.readline()
|
||||
sys.stdout.write(line)
|
||||
except (KeyboardInterrupt, MemoryError):
|
||||
return
|
||||
|
||||
|
||||
def edit_config(self):
|
||||
"""
|
||||
Edit GNS3 configuration file
|
||||
"""
|
||||
|
||||
major_version = self.gns3_major_version()
|
||||
if major_version == "2.2":
|
||||
os.system("nano ~/.config/GNS3/{}/gns3_server.conf".format(major_version))
|
||||
else:
|
||||
os.system("nano ~/.config/GNS3/gns3_server.conf")
|
||||
|
||||
|
||||
def edit_network(self):
|
||||
"""
|
||||
Edit network configuration file
|
||||
"""
|
||||
if self.display.yesno("The server will reboot at the end of the process. Continue?") != self.display.OK:
|
||||
return
|
||||
os.system("sudo nano /etc/network/interfaces")
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "reboot"])
|
||||
|
||||
|
||||
def edit_proxy(self):
|
||||
"""
|
||||
Configure proxy settings
|
||||
"""
|
||||
res, http_proxy = self.display.inputbox(text="HTTP proxy string, for example http://<user>:<password>@<proxy>:<port>. Leave empty for no proxy.")
|
||||
if res != self.display.OK:
|
||||
return
|
||||
res, https_proxy = self.display.inputbox(text="HTTPS proxy string, for example http://<user>:<password>@<proxy>:<port>. Leave empty for no proxy.")
|
||||
if res != self.display.OK:
|
||||
return
|
||||
|
||||
with open('/tmp/00proxy', 'w+') as f:
|
||||
f.write('Acquire::http::Proxy "' + http_proxy + '";')
|
||||
os.system("sudo mv /tmp/00proxy /etc/apt/apt.conf.d/00proxy")
|
||||
os.system("sudo chown root /etc/apt/apt.conf.d/00proxy")
|
||||
os.system("sudo chmod 744 /etc/apt/apt.conf.d/00proxy")
|
||||
|
||||
with open('/tmp/proxy.sh', 'w+') as f:
|
||||
f.write('export http_proxy="' + http_proxy + '"\n')
|
||||
f.write('export https_proxy="' + https_proxy + '"\n')
|
||||
f.write('export HTTP_PROXY="' + http_proxy + '"\n')
|
||||
f.write('export HTTPS_PROXY="' + https_proxy + '"\n')
|
||||
os.system("sudo mv /tmp/proxy.sh /etc/profile.d/proxy.sh")
|
||||
os.system("sudo chown root /etc/profile.d/proxy.sh")
|
||||
os.system("sudo chmod 744 /etc/profile.d/proxy.sh")
|
||||
os.system("sudo cp /etc/profile.d/proxy.sh /etc/default/docker")
|
||||
|
||||
self.display.msgbox("The GNS3 VM will reboot")
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "reboot"])
|
||||
|
||||
|
||||
def kvm_support(self):
|
||||
"""
|
||||
Returns true if KVM is available
|
||||
"""
|
||||
return subprocess.call("kvm-ok") == 0
|
||||
|
||||
|
||||
def kvm_control(self):
|
||||
"""
|
||||
Check if KVM is correctly configured
|
||||
"""
|
||||
|
||||
kvm_ok = self.kvm_support()
|
||||
config = self.get_config()
|
||||
try:
|
||||
if config.getboolean("Qemu", "enable_kvm") is True:
|
||||
if kvm_ok is False:
|
||||
if self.display.yesno("KVM is not available!\n\nQemu VM will crash!!\n\nThe reason could be unsupported hardware or another virtualization solution is already running.\n\nDisable KVM and get lower performances?") == self.display.OK:
|
||||
config.set("Qemu", "enable_kvm", False)
|
||||
self.write_config(config)
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "reboot"])
|
||||
else:
|
||||
if kvm_ok is True:
|
||||
if self.display.yesno("KVM is available on your computer.\n\nEnable KVM and get better performances?") == self.display.OK:
|
||||
config.set("Qemu", "enable_kvm", True)
|
||||
self.write_config(config)
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "reboot"])
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
|
||||
def display_loop(self):
|
||||
try:
|
||||
while True:
|
||||
code, tag = self.display.menu("GNS3 {}".format(self.gns3_version()),
|
||||
choices=[("Information", "Display VM information"),
|
||||
("Upgrade", "Upgrade GNS3"),
|
||||
("Migrate", "Migrate data to another GNS3 VM"),
|
||||
("Shell", "Open a console"),
|
||||
("Security", "Configure authentication"),
|
||||
("Keyboard", "Change keyboard layout"),
|
||||
("Configure", "Edit server configuration (advanced users ONLY)"),
|
||||
("Proxy", "Configure proxy settings"),
|
||||
("Networking", "Configure networking settings"),
|
||||
("Log", "Show server log"),
|
||||
("Test", "Check internet connection"),
|
||||
("Shrink", "Shrink the VM disk"),
|
||||
("Version", "Select the GNS3 version"),
|
||||
("Restore", "Restore the VM (if you have trouble for upgrade)"),
|
||||
("Reboot", "Reboot the VM"),
|
||||
("Shutdown", "Shutdown the VM")])
|
||||
self.display.clear()
|
||||
if code == Dialog.OK:
|
||||
if tag == "Shell":
|
||||
os.execvp("bash", ['/bin/bash'])
|
||||
elif tag == "Version":
|
||||
self.mode()
|
||||
elif tag == "Restore":
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "/usr/local/bin/gns3restore"])
|
||||
elif tag == "Reboot":
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "reboot"])
|
||||
elif tag == "Shutdown":
|
||||
os.execvp("sudo", ['/usr/bin/sudo', "poweroff"])
|
||||
elif tag == "Upgrade":
|
||||
self.update()
|
||||
elif tag == "Information":
|
||||
self.vm_information()
|
||||
elif tag == "Log":
|
||||
self.log()
|
||||
elif tag == "Migrate":
|
||||
self.migrate()
|
||||
elif tag == "Configure":
|
||||
self.edit_config()
|
||||
elif tag == "Networking":
|
||||
self.edit_network()
|
||||
elif tag == "Security":
|
||||
self.set_security()
|
||||
elif tag == "Keyboard":
|
||||
self.keyboard_configuration()
|
||||
elif tag == "Test":
|
||||
self.check_internet_connectivity()
|
||||
elif tag == "Proxy":
|
||||
self.edit_proxy()
|
||||
elif tag == "Shrink":
|
||||
self.shrink_disk()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
ws = Welcome_dialog()
|
||||
ws.vm_information()
|
||||
ws.kvm_control()
|
||||
ws.display_loop()
|
@ -1350,8 +1350,7 @@ async def test_close(vm, port_manager):
|
||||
async def test_close_vnc(vm):
|
||||
|
||||
vm._console_type = "vnc"
|
||||
vm._x11vnc_process = MagicMock()
|
||||
vm._xvfb_process = MagicMock()
|
||||
vm._vnc_process = MagicMock()
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query") as mock_query:
|
||||
@ -1359,7 +1358,7 @@ async def test_close_vnc(vm):
|
||||
mock_query.assert_called_with("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1})
|
||||
|
||||
assert vm._closed is True
|
||||
assert vm._xvfb_process.terminate.called
|
||||
assert vm._vnc_process.terminate.called
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -98,6 +98,7 @@ async def test_start(vm):
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as mock_exec:
|
||||
mock_process.returncode = None
|
||||
mock_process.communicate = AsyncioMagicMock(return_value=(None, None))
|
||||
await vm.start()
|
||||
assert vm.is_running()
|
||||
assert vm.command_line == ' '.join(mock_exec.call_args[0])
|
||||
@ -127,6 +128,7 @@ async def test_start_with_iourc(vm, tmpdir, config):
|
||||
config.settings.IOU.iourc_path = fake_file
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as exec_mock:
|
||||
mock_process.returncode = None
|
||||
mock_process.communicate = AsyncioMagicMock(return_value=(None, None))
|
||||
await vm.start()
|
||||
assert vm.is_running()
|
||||
arsgs, kwargs = exec_mock.call_args
|
||||
@ -164,11 +166,12 @@ async def test_stop(vm):
|
||||
future = asyncio.Future()
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
process.returncode = None
|
||||
process.communicate = AsyncioMagicMock(return_value=(None, None))
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
|
||||
await vm.start()
|
||||
process.returncode = None
|
||||
assert vm.is_running()
|
||||
await vm.stop()
|
||||
assert vm.is_running() is False
|
||||
@ -190,6 +193,7 @@ async def test_reload(vm, fake_iou_bin):
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
process.returncode = None
|
||||
process.communicate = AsyncioMagicMock(return_value=(None, None))
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
|
||||
@ -203,10 +207,13 @@ async def test_reload(vm, fake_iou_bin):
|
||||
@pytest.mark.asyncio
|
||||
async def test_close(vm, port_manager):
|
||||
|
||||
process = MagicMock()
|
||||
process.returncode = None
|
||||
process.communicate = AsyncioMagicMock(return_value=(None, None))
|
||||
vm._start_ubridge = AsyncioMagicMock(return_value=True)
|
||||
vm._ubridge_send = AsyncioMagicMock()
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
await vm.start()
|
||||
port = vm.console
|
||||
await vm.close()
|
||||
|
@ -751,6 +751,20 @@ async def test_build_command_large_number_of_adapters(vm):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
|
||||
await vm._build_command()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_command_with_virtio_net_pci_adapter(vm):
|
||||
"""
|
||||
Test virtio-net-pci adapter which has parameters speed=1000 & duplex=full hard-coded
|
||||
"""
|
||||
|
||||
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.4.0")
|
||||
vm.adapters = 1
|
||||
vm.mac_address = "00:00:ab:0e:0f:09"
|
||||
vm._adapter_type = "virtio-net-pci"
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
|
||||
cmd = await vm._build_command()
|
||||
assert "virtio-net-pci,mac=00:00:ab:0e:0f:09,speed=10000,duplex=full,netdev=gns3-0" in cmd
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_command_with_invalid_options(vm):
|
||||
|
Loading…
Reference in New Issue
Block a user