1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 09:18:08 +00:00

Merge pull request #2430 from GNS3/2.2

Release v2.2.50
This commit is contained in:
Jeremy Grossmann 2024-10-21 13:21:24 +10:00 committed by GitHub
commit 97e5a5edc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 757 additions and 319 deletions

View File

@ -13,10 +13,16 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: ["ubuntu-latest"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
#include:
# only test with Python 3.10 on Windows
# - os: windows-latest
# python-version: "3.10"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -27,7 +33,15 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
if [ -f dev-requirements.txt ]; then pip install -r dev-requirements.txt; fi python -m pip install -r dev-requirements.txt
- name: Install Windows specific dependencies
if: runner.os == 'Windows'
run: |
python -m pip install -r win-requirements.txt
curl -O "http://www.win10pcap.org/download/Win10Pcap-v10.2-5002.msi"
msiexec /i "Win10Pcap-v10.2-5002.msi" /qn /norestart
- name: Lint with flake8 - name: Lint with flake8
run: | run: |
# stop the build if there are Python syntax errors or undefined names # stop the build if there are Python syntax errors or undefined names

View File

@ -1,5 +1,19 @@
# Change Log # Change Log
## 2.2.50 21/10/2024
* Bundle web-ui v2.2.50
* Symbolic links support for project export/import
* Add comment to indicate sentry-sdk is optional. Ref https://github.com/GNS3/gns3-server/issues/2423
* Fix issues with recent busybox versions
* Support to reset MAC addresses for Docker nodes and some adjustments for fast duplication.
* Update README.md to change the minimum required Python version.
* Faster project duplication for local projects (no remote compute)
* Improve error message when a project cannot be parsed.
* Fix for running Docker containers with user namespaces enabled
* Support for configuring MAC address in Docker containers
* Upgrade aiohttp to v3.10.3
## 2.2.49 06/08/2024 ## 2.2.49 06/08/2024
* Bundle web-ui v2.2.49 * Bundle web-ui v2.2.49

View File

@ -79,7 +79,7 @@ must be connected to the Internet in order to install the dependencies.
Dependencies: Dependencies:
- Python 3.6, setuptools and the ones listed - Python >= 3.8, setuptools and the ones listed
[here](https://github.com/GNS3/gns3-server/blob/master/requirements.txt) [here](https://github.com/GNS3/gns3-server/blob/master/requirements.txt)
The following commands will install some of these dependencies: The following commands will install some of these dependencies:

View File

@ -1,22 +0,0 @@
version: '{build}-{branch}'
image: Visual Studio 2022
platform: x64
environment:
PYTHON: "C:\\Python38-x64"
DISTUTILS_USE_SDK: "1"
API_TOKEN:
secure: VEKn4bYH3QO0ixtQW5ni4Enmn8cS1NlZV246ludBDgQ=
install:
- cinst nmap
- "%PYTHON%\\python.exe -m pip install -U pip setuptools" # upgrade pip & setuptools first
- "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt"
- "%PYTHON%\\python.exe -m pip install -r win-requirements.txt"
build: off
test_script:
- "%PYTHON%\\python.exe -m pytest -v"

View File

@ -1,6 +1,6 @@
-rrequirements.txt -rrequirements.txt
pytest==8.3.2 pytest==8.3.3
flake8==7.1.0 flake8==7.1.0
pytest-timeout==2.3.1 pytest-timeout==2.3.1
pytest-aiohttp==1.0.5 pytest-aiohttp==1.0.5

View File

@ -30,24 +30,24 @@
"version": "9.2", "version": "9.2",
"md5sum": "c5bc76e8c95ac9f810a3482c80a54cc7", "md5sum": "c5bc76e8c95ac9f810a3482c80a54cc7",
"filesize": 563347456, "filesize": 563347456,
"download_url": "https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/", "download_url": "https://vault.almalinux.org/9.2/cloud/x86_64/images/",
"direct_download_url": "https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-9.2-20230513.x86_64.qcow2" "direct_download_url": "https://vault.almalinux.org/9.2/cloud/x86_64/images/AlmaLinux-9-GenericCloud-9.2-20230513.x86_64.qcow2"
}, },
{ {
"filename": "AlmaLinux-8-GenericCloud-8.8-20230524.x86_64.qcow2", "filename": "AlmaLinux-8-GenericCloud-8.8-20230524.x86_64.qcow2",
"version": "8.8", "version": "8.8",
"md5sum": "3958c5fc25770ef63cf97aa5d93f0a0b", "md5sum": "3958c5fc25770ef63cf97aa5d93f0a0b",
"filesize": 565444608, "filesize": 565444608,
"download_url": "https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/", "download_url": "https://vault.almalinux.org/8.8/cloud/x86_64/images/",
"direct_download_url": "https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-8.8-20230524.x86_64.qcow2" "direct_download_url": "https://vault.almalinux.org/8.8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-8.8-20230524.x86_64.qcow2"
}, },
{ {
"filename": "AlmaLinux-8-GenericCloud-8.7-20221111.x86_64.qcow2", "filename": "AlmaLinux-8-GenericCloud-8.7-20221111.x86_64.qcow2",
"version": "8.7", "version": "8.7",
"md5sum": "b2b8c7fd3b6869362f3f8ed47549c804", "md5sum": "b2b8c7fd3b6869362f3f8ed47549c804",
"filesize": 566231040, "filesize": 566231040,
"download_url": "https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/", "download_url": "https://vault.almalinux.org/8.7/cloud/x86_64/images/",
"direct_download_url": "https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-8.7-20221111.x86_64.qcow2" "direct_download_url": "https://vault.almalinux.org/8.7/cloud/x86_64/images/AlmaLinux-8-GenericCloud-8.7-20221111.x86_64.qcow2"
}, },
{ {
"filename": "almalinux-cloud-init-data.iso", "filename": "almalinux-cloud-init-data.iso",

View File

@ -29,10 +29,10 @@
}, },
"images": [ "images": [
{ {
"filename": "FGT_VM64_KVM-v7.4.4.F-build2573-FORTINET.out.kvm.qcow2", "filename": "FGT_VM64_KVM-v7.4.4.F-build2662-FORTINET.out.kvm.qcow2",
"version": "7.4.4", "version": "7.4.4",
"md5sum": "dfe0e78827ec728631539669001bb23f", "md5sum": "dfe0e78827ec728631539669001bb23f",
"filesize": 100728832, "filesize": 102170624,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
}, },
{ {
@ -391,7 +391,7 @@
{ {
"name": "7.4.4", "name": "7.4.4",
"images": { "images": {
"hda_disk_image": "FGT_VM64_KVM-v7.4.4.F-build2573-FORTINET.out.kvm.qcow2", "hda_disk_image": "FGT_VM64_KVM-v7.4.4.F-build2662-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2" "hdb_disk_image": "empty30G.qcow2"
} }
}, },

View File

@ -0,0 +1,62 @@
{
"appliance_id": "ac98ab6f-7966-444b-842f-9507c965b8b7",
"name": "HBCD-PE",
"category": "guest",
"description": "Hirens BootCD PE (Preinstallation Environment) is a restored edition of Hirens BootCD based on Windows 11 PE x64. ",
"vendor_name": "hirensbootcd.org",
"vendor_url": "https://www.hirensbootcd.org/",
"documentation_url": "https://www.hirensbootcd.org/howtos/",
"product_name": "Hirens BootCD PE",
"product_url": "https://www.hirensbootcd.org/",
"registry_version": 4,
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"qemu": {
"adapter_type": "e1000",
"adapters": 1,
"ram": 4096,
"hda_disk_interface": "sata",
"arch": "x86_64",
"console_type": "vnc",
"boot_priority": "c",
"kvm": "require"
},
"images": [
{
"filename": "HBCD_PE_x64.iso",
"version": "1.0.8",
"md5sum": "45baab64b088431bdf3370292e9a74b0",
"filesize": 3291686912,
"download_url": "https://www.hirensbootcd.org/download/",
"direct_download_url": "https://www.hirensbootcd.org/files/HBCD_PE_x64.iso"
},
{
"filename": "empty30G.qcow2",
"version": "1.0",
"md5sum": "3411a599e822f2ac6be560a26405821a",
"filesize": 197120,
"download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty30G.qcow2/download"
},
{
"filename": "OVMF-edk2-stable202305.fd",
"version": "stable202305",
"md5sum": "6c4cf1519fec4a4b95525d9ae562963a",
"filesize": 4194304,
"download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/OVMF-edk2-stable202305.fd.zip/download",
"compression": "zip"
}
],
"versions": [
{
"name": "1.0.8",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "empty30G.qcow2",
"cdrom_image": "HBCD_PE_x64.iso"
}
}
]
}

View File

@ -28,149 +28,80 @@
}, },
"images": [ "images": [
{ {
"filename": "chr-7.14.2.img", "filename": "chr-7.16.img",
"version": "7.14.2", "version": "7.16",
"md5sum": "531901dac85b67b23011e946a62abc7b", "md5sum": "a4c2d00a87e73b3129cd66a4e0743c9a",
"filesize": 134217728, "filesize": 134217728,
"download_url": "http://www.mikrotik.com/download", "download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.14.2/chr-7.14.2.img.zip", "direct_download_url": "https://download.mikrotik.com/routeros/7.16/chr-7.16.img.zip",
"compression": "zip" "compression": "zip"
}, },
{ {
"filename": "chr-7.11.2.img", "filename": "chr-7.15.3.img",
"version": "7.11.2", "version": "7.15.3",
"md5sum": "fbffd097d2c5df41fc3335c3977f782c", "md5sum": "5af8c748a0de4e8e8b303180738721a9",
"filesize": 134217728, "filesize": 134217728,
"download_url": "http://www.mikrotik.com/download", "download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.11.2/chr-7.11.2.img.zip", "direct_download_url": "https://download.mikrotik.com/routeros/7.15.3/chr-7.15.3.img.zip",
"compression": "zip" "compression": "zip"
}, },
{ {
"filename": "chr-7.10.1.img", "filename": "chr-7.14.3.img",
"version": "7.10.1", "version": "7.14.3",
"md5sum": "917729e79b9992562f4160d461b21cac", "md5sum": "73f527efef81b529b267a0683cb87617",
"filesize": 134217728, "filesize": 134217728,
"download_url": "http://www.mikrotik.com/download", "download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.10.1/chr-7.10.1.img.zip", "direct_download_url": "https://download.mikrotik.com/routeros/7.14.3/chr-7.14.3.img.zip",
"compression": "zip" "compression": "zip"
}, },
{ {
"filename": "chr-7.7.img", "filename": "chr-6.49.17.img",
"version": "7.7", "version": "6.49.17",
"md5sum": "efc4fdeb1cc06dc240a14f1215fd59b3", "md5sum": "ad9f4bd8cd4965a403350deeb5d35b96",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.7/chr-7.7.img.zip",
"compression": "zip"
},
{
"filename": "chr-7.6.img",
"version": "7.6",
"md5sum": "864482f9efaea9d40910c050318f65b9",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.6/chr-7.6.img.zip",
"compression": "zip"
},
{
"filename": "chr-7.3.1.img",
"version": "7.3.1",
"md5sum": "99f8ea75f8b745a8bf5ca3cc1bd325e3",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.3.1/chr-7.3.1.img.zip",
"compression": "zip"
},
{
"filename": "chr-7.1.5.img",
"version": "7.1.5",
"md5sum": "9c0be05f891df2b1400bdae5e719898e",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.1.5/chr-7.1.5.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.49.10.img",
"version": "6.49.10",
"md5sum": "49ae1ecfe310aea1df37b824aa13cf84",
"filesize": 67108864, "filesize": 67108864,
"download_url": "http://www.mikrotik.com/download", "download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.49.10/chr-6.49.10.img.zip", "direct_download_url": "https://download.mikrotik.com/routeros/6.49.17/chr-6.49.17.img.zip",
"compression": "zip" "compression": "zip"
}, },
{ {
"filename": "chr-6.49.6.img", "filename": "chr-6.49.13.img",
"version": "6.49.6", "version": "6.49.13",
"md5sum": "ae27d38acc9c4dcd875e0f97bcae8d97", "md5sum": "18349e1c3209495e571bcbee8a7e3259",
"filesize": 67108864, "filesize": 67108864,
"download_url": "http://www.mikrotik.com/download", "download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.49.6/chr-6.49.6.img.zip", "direct_download_url": "https://download.mikrotik.com/routeros/6.49.13/chr-6.49.13.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.48.6.img",
"version": "6.48.6",
"md5sum": "875574a561570227ff8f395aabe478c6",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.48.6/chr-6.48.6.img.zip",
"compression": "zip" "compression": "zip"
} }
], ],
"versions": [ "versions": [
{ {
"name": "7.11.2", "name": "7.16",
"images": { "images": {
"hda_disk_image": "chr-7.11.2.img" "hda_disk_image": "chr-7.16.img"
} }
}, },
{ {
"name": "7.10.1", "name": "7.15.3",
"images": { "images": {
"hda_disk_image": "chr-7.10.1.img" "hda_disk_image": "chr-7.15.3.img"
} }
}, },
{ {
"name": "7.7", "name": "7.14.3",
"images": { "images": {
"hda_disk_image": "chr-7.7.img" "hda_disk_image": "chr-7.14.3.img"
} }
}, },
{ {
"name": "7.6", "name": "6.49.17",
"images": { "images": {
"hda_disk_image": "chr-7.6.img" "hda_disk_image": "chr-6.49.17.img"
} }
}, },
{ {
"name": "7.3.1", "name": "6.49.13",
"images": { "images": {
"hda_disk_image": "chr-7.3.1.img" "hda_disk_image": "chr-6.49.13.img"
}
},
{
"name": "7.1.5",
"images": {
"hda_disk_image": "chr-7.1.5.img"
}
},
{
"name": "6.49.10",
"images": {
"hda_disk_image": "chr-6.49.10.img"
}
},
{
"name": "6.49.6",
"images": {
"hda_disk_image": "chr-6.49.6.img"
}
},
{
"name": "6.48.6",
"images": {
"hda_disk_image": "chr-6.48.6.img"
} }
} }
] ]

View File

@ -25,6 +25,13 @@
"kvm": "require" "kvm": "require"
}, },
"images": [ "images": [
{
"filename": "OPNsense-24.7-nano-amd64.img",
"version": "24.7",
"md5sum": "4f75dc2c948b907cbf73763b1539677c",
"filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/24.7/"
},
{ {
"filename": "OPNsense-24.1-nano-amd64.img", "filename": "OPNsense-24.1-nano-amd64.img",
"version": "24.1", "version": "24.1",
@ -69,6 +76,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "24.7",
"images": {
"hda_disk_image": "OPNsense-24.7-nano-amd64.img"
}
},
{ {
"name": "24.1", "name": "24.1",
"images": { "images": {

View File

@ -24,17 +24,17 @@
}, },
"images": [ "images": [
{ {
"filename": "ReactOS-0.4.14-release-15-gb6088a6.iso", "filename": "ReactOS-0.4.14-release-21-g1302c1b.iso",
"version": "Installer-0.4.14-release-15", "version": "Installer-0.4.14-release-21",
"md5sum": "af4be6b27463446905f155f14232d2b4", "md5sum": "a30bc9143788c76ed584ffd5d25fddfe",
"filesize": 140509184, "filesize": 140509184,
"download_url": "https://reactos.org/download", "download_url": "https://reactos.org/download",
"direct_download_url": "https://sourceforge.net/projects/reactos/files/ReactOS/0.4.14/ReactOS-0.4.14-release-21-g1302c1b-iso.zip/download" "direct_download_url": "https://sourceforge.net/projects/reactos/files/ReactOS/0.4.14/ReactOS-0.4.14-release-21-g1302c1b-iso.zip/download"
}, },
{ {
"filename": "ReactOS-0.4.14-release-15-gb6088a6-Live.iso", "filename": "ReactOS-0.4.14-release-21-g1302c1b-Live.iso",
"version": "Live-0.4.14-release-15", "version": "Live-0.4.14-release-21",
"md5sum": "73c1a0169a9a3b8a4feb91f4d00f5e97", "md5sum": "fc362820069adeea088b3a48dcfa3f9e",
"filesize": 267386880, "filesize": 267386880,
"download_url": "https://reactos.org/download", "download_url": "https://reactos.org/download",
"direct_download_url": "https://sourceforge.net/projects/reactos/files/ReactOS/0.4.14/ReactOS-0.4.14-release-21-g1302c1b-live.zip/download" "direct_download_url": "https://sourceforge.net/projects/reactos/files/ReactOS/0.4.14/ReactOS-0.4.14-release-21-g1302c1b-live.zip/download"
@ -50,17 +50,17 @@
], ],
"versions": [ "versions": [
{ {
"name": "Installer-0.4.14-release-15", "name": "Installer-0.4.14-release-21",
"images": { "images": {
"hda_disk_image": "empty30G.qcow2", "hda_disk_image": "empty30G.qcow2",
"cdrom_image": "ReactOS-0.4.14-release-15-gb6088a6.iso" "cdrom_image": "ReactOS-0.4.14-release-21-g1302c1b.iso"
} }
}, },
{ {
"name": "Live-0.4.14-release-15", "name": "Live-0.4.14-release-21",
"images": { "images": {
"hda_disk_image": "empty30G.qcow2", "hda_disk_image": "empty30G.qcow2",
"cdrom_image": "ReactOS-0.4.14-release-15-gb6088a6-Live.iso" "cdrom_image": "ReactOS-0.4.14-release-21-g1302c1b-Live.iso"
} }
} }
] ]

View File

@ -0,0 +1,104 @@
{
"appliance_id": "8c19ccaa-a1d0-4473-94a2-a93b64924d88",
"name": "TrueNAS",
"category": "guest",
"description": "TrueNAS is a family of network-attached storage (NAS) products produced by iXsystems, incorporating both FOSS, as well as commercial offerings. Based on the OpenZFS file system, TrueNAS runs on FreeBSD as well as Linux and is available under the BSD License It is compatible with x86-64 hardware and is also available as turnkey appliances from iXsystems.",
"vendor_name": "iXsystems",
"vendor_url": "https://www.truenas.com/",
"documentation_url": "https://www.truenas.com/docs/",
"product_name": "TrueNAS",
"product_url": "https://www.truenas.com/",
"registry_version": 4,
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "To install TrueNAS SCALE you may have to select the Legacy BIOS option.",
"port_name_format": "eth{0}",
"qemu": {
"adapter_type": "e1000",
"adapters": 1,
"ram": 8192,
"hda_disk_interface": "ide",
"hdb_disk_interface": "ide",
"arch": "x86_64",
"console_type": "vnc",
"boot_priority": "cd",
"kvm": "require"
},
"images": [
{
"filename": "TrueNAS-13.0-U6.2.iso",
"version": "CORE 13.0 U6.2",
"md5sum": "8b2882b53af5e9f3ca905c6acdee1690",
"filesize": 1049112576,
"download_url": "https://www.truenas.com/download-truenas-core/",
"direct_download_url": "https://download-core.sys.truenas.net/13.0/STABLE/U6.2/x64/TrueNAS-13.0-U6.2.iso"
},
{
"filename": "TrueNAS-13.3-RELEASE.iso",
"version": "CORE 13.3 RELEASE",
"md5sum": "8bb16cfb06f3f1374a27cf6aebb14ed3",
"filesize": 995567616,
"download_url": "https://www.truenas.com/download-truenas-core/",
"direct_download_url": "https://download-core.sys.truenas.net/13.3/STABLE/RELEASE/x64/TrueNAS-13.3-RELEASE.iso"
},
{
"filename": "TrueNAS-SCALE-24.04.2.2.iso",
"version": "SCALE 24.04.2.2",
"md5sum": "47d9026254a0775800bb2b8ab6d874fd",
"filesize": 1630355456,
"download_url": "https://www.truenas.com/download-truenas-scale/",
"direct_download_url": "https://download.sys.truenas.net/TrueNAS-SCALE-Dragonfish/24.04.2.2/TrueNAS-SCALE-24.04.2.2.iso"
},
{
"filename": "TrueNAS-SCALE-24.10-BETA.1.iso",
"version": "SCALE 24.10-BETA.1",
"md5sum": "cc3d5758d1db3d55ae9c8716f5d43b88",
"filesize": 1491378176,
"download_url": "https://www.truenas.com/download-truenas-scale/",
"direct_download_url": "https://download.sys.truenas.net/TrueNAS-SCALE-ElectricEel-BETA/24.10-BETA.1/TrueNAS-SCALE-24.10-BETA.1.iso"
},
{
"filename": "empty30G.qcow2",
"version": "1.0",
"md5sum": "3411a599e822f2ac6be560a26405821a",
"filesize": 197120,
"download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty30G.qcow2/download"
}
],
"versions": [
{
"name": "CORE 13.0 U6.2",
"images": {
"hda_disk_image": "empty30G.qcow2",
"hdb_disk_image": "empty30G.qcow2",
"cdrom_image": "TrueNAS-13.0-U6.2.iso"
}
},
{
"name": "CORE 13.3 RELEASE",
"images": {
"hda_disk_image": "empty30G.qcow2",
"hdb_disk_image": "empty30G.qcow2",
"cdrom_image": "TrueNAS-13.3-RELEASE.iso"
}
},
{
"name": "SCALE 24.04.2.2",
"images": {
"hda_disk_image": "empty30G.qcow2",
"hdb_disk_image": "empty30G.qcow2",
"cdrom_image": "TrueNAS-SCALE-24.04.2.2.iso"
}
},
{
"name": "SCALE 24.10-BETA.1",
"images": {
"hda_disk_image": "empty30G.qcow2",
"hdb_disk_image": "empty30G.qcow2",
"cdrom_image": "TrueNAS-SCALE-24.10-BETA.1.iso"
}
}
]
}

View File

@ -28,36 +28,44 @@
}, },
"images": [ "images": [
{ {
"filename": "ubuntu-23.04-server-cloudimg-amd64.img", "filename": "ubuntu-24.10-server-cloudimg-amd64.img",
"version": "Ubuntu 23.04 (Lunar Lobster)", "version": "Ubuntu 24.10 (Oracular Oriole)",
"md5sum": "369e3b1f68416c39245a8014172406dd", "md5sum": "f2960f8743efedd0a4968bfcd9451782",
"filesize": 756678656, "filesize": 627360256,
"download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/", "download_url": "https://cloud-images.ubuntu.com/releases/oracular/release-20241009/",
"direct_download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/ubuntu-23.04-server-cloudimg-amd64.img" "direct_download_url": "https://cloud-images.ubuntu.com/releases/oracular/release-20241009/ubuntu-24.10-server-cloudimg-amd64.img"
},
{
"filename": "ubuntu-24.04-server-cloudimg-amd64.img",
"version": "Ubuntu 24.04 LTS (Noble Numbat)",
"md5sum": "a1c8a01953578ad432cbef03db2f3161",
"filesize": 587241984,
"download_url": "https://cloud-images.ubuntu.com/releases/noble/release-20241004/",
"direct_download_url": "https://cloud-images.ubuntu.com/releases/noble/release-20241004/ubuntu-24.04-server-cloudimg-amd64.img"
}, },
{ {
"filename": "ubuntu-22.04-server-cloudimg-amd64.img", "filename": "ubuntu-22.04-server-cloudimg-amd64.img",
"version": "Ubuntu 22.04 LTS (Jammy Jellyfish)", "version": "Ubuntu 22.04 LTS (Jammy Jellyfish)",
"md5sum": "3ce0b84f9592482fb645e8253b979827", "md5sum": "8f9a70435dc5b0b86cf5d0d4716b6091",
"filesize": 686096384, "filesize": 653668352,
"download_url": "https://cloud-images.ubuntu.com/releases/jammy/release", "download_url": "https://cloud-images.ubuntu.com/releases/jammy/release-20241002/",
"direct_download_url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img" "direct_download_url": "https://cloud-images.ubuntu.com/releases/jammy/release-20241002/ubuntu-22.04-server-cloudimg-amd64.img"
}, },
{ {
"filename": "ubuntu-20.04-server-cloudimg-amd64.img", "filename": "ubuntu-20.04-server-cloudimg-amd64.img",
"version": "Ubuntu 20.04 LTS (Focal Fossa)", "version": "Ubuntu 20.04 LTS (Focal Fossa)",
"md5sum": "044bc979b2238192ee3edb44e2bb6405", "md5sum": "1dff90e16acb0167c27ff82e4ac1813a",
"filesize": 552337408, "filesize": 627310592,
"download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20210119.1/", "download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20240821/",
"direct_download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20210119.1/ubuntu-20.04-server-cloudimg-amd64.img" "direct_download_url": "https://cloud-images.ubuntu.com/releases/focal/release-20240821/ubuntu-20.04-server-cloudimg-amd64.img"
}, },
{ {
"filename": "ubuntu-18.04-server-cloudimg-amd64.img", "filename": "ubuntu-18.04-server-cloudimg-amd64.img",
"version": "Ubuntu 18.04 LTS (Bionic Beaver)", "version": "Ubuntu 18.04 LTS (Bionic Beaver)",
"md5sum": "f4134e7fa16d7fa766c7467cbe25c949", "md5sum": "62fa110eeb0459c1ff166f897aeb9f78",
"filesize": 336134144, "filesize": 405667840,
"download_url": "https://cloud-images.ubuntu.com/releases/18.04/release-20180426.2/", "download_url": "https://cloud-images.ubuntu.com/releases/bionic/release-20230607/",
"direct_download_url": "https://cloud-images.ubuntu.com/releases/18.04/release-20180426.2/ubuntu-18.04-server-cloudimg-amd64.img" "direct_download_url": "https://cloud-images.ubuntu.com/releases/bionic/release-20230607/ubuntu-18.04-server-cloudimg-amd64.img"
}, },
{ {
"filename": "ubuntu-cloud-init-data.iso", "filename": "ubuntu-cloud-init-data.iso",
@ -70,9 +78,16 @@
], ],
"versions": [ "versions": [
{ {
"name": "Ubuntu 23.04 (Lunar Lobster)", "name": "Ubuntu 24.10 (Oracular Oriole)",
"images": { "images": {
"hda_disk_image": "ubuntu-23.04-server-cloudimg-amd64.img", "hda_disk_image": "ubuntu-24.10-server-cloudimg-amd64.img",
"cdrom_image": "ubuntu-cloud-init-data.iso"
}
},
{
"name": "Ubuntu 24.04 LTS (Noble Numbat)",
"images": {
"hda_disk_image": "ubuntu-24.04-server-cloudimg-amd64.img",
"cdrom_image": "ubuntu-cloud-init-data.iso" "cdrom_image": "ubuntu-cloud-init-data.iso"
} }
}, },

View File

@ -27,6 +27,13 @@
"options": "-vga qxl" "options": "-vga qxl"
}, },
"images": [ "images": [
{
"filename": "Ubuntu 24.04 (64bit).vmdk",
"version": "24.04",
"md5sum": "7709a5a1cf888c7644d245c42d217918",
"filesize": 6432555008,
"download_url": "https://www.osboxes.org/ubuntu/"
},
{ {
"filename": "Ubuntu 22.04 (64bit).vmdk", "filename": "Ubuntu 22.04 (64bit).vmdk",
"version": "22.04", "version": "22.04",
@ -57,6 +64,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "24.04",
"images": {
"hda_disk_image": "Ubuntu 24.04 (64bit).vmdk"
}
},
{ {
"name": "22.04", "name": "22.04",
"images": { "images": {

View File

@ -1,181 +1,178 @@
{ {
"appliance_id": "f82b74c4-0f30-456f-a582-63daca528502", "appliance_id": "f82b74c4-0f30-456f-a582-63daca528502",
"name": "VyOS", "name": "VyOS Universal Router",
"category": "router", "category": "router",
"description": "VyOS is a community fork of Vyatta, a Linux-based network operating system that provides software-based network routing, firewall, and VPN functionality.", "description": "VyOS is an open-source network operating system that provides a comprehensive suite of features for routing, firewalling, and VPN functionality. VyOS offers a robust and flexible solution for both small-scale and large-scale network environments. It is designed to support enterprise-grade networking with the added benefits of community-driven development and continuous updates.\n\nThe VyOS Universal Router, when used in GNS3, brings the power and versatility of VyOS to network simulation and emulation. GNS3 users can deploy the VyOS Universal Router to create and test complex network topologies in a virtual environment. This appliance provides a rich set of features, including dynamic routing protocols, stateful firewall capabilities, various VPNs, as well as high availability configurations.\n\nThe seamless integration with GNS3 allows network engineers and architects to validate network designs, perform testing and troubleshooting, and enhance their skill sets in a controlled, risk-free environment.",
"vendor_name": "Linux", "vendor_name": "VyOS Inc.",
"vendor_url": "https://vyos.net/", "vendor_url": "https://vyos.io/",
"vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/VyOS.png",
"documentation_url": "https://docs.vyos.io/", "documentation_url": "https://docs.vyos.io/",
"product_name": "VyOS", "product_name": "VyOS Universal Router",
"product_url": "https://vyos.net/", "product_url": "https://vyos.io/vyos-universal-router",
"registry_version": 4, "registry_version": 4,
"status": "stable", "status": "stable",
"maintainer": "GNS3 Team", "availability": "service-contract",
"maintainer_email": "developers@gns3.net", "maintainer": "VyOS Inc.",
"usage": "Default username/password is vyos/vyos.\n\nThe -KVM versions are ready to use, no installation is required.\nThe other images will start the router from the CDROM on initial boot. Login and then type \"install image\" and follow the instructions.", "maintainer_email": "support@vyos.io",
"usage": "\nDefault credentials:\nUser: vyos\nPassword: vyos",
"symbol": "vyos.svg", "symbol": "vyos.svg",
"port_name_format": "eth{0}", "port_name_format": "eth{0}",
"qemu": { "qemu": {
"adapter_type": "e1000", "adapter_type": "virtio-net-pci",
"adapters": 3, "adapters": 10,
"ram": 512, "ram": 2048,
"hda_disk_interface": "scsi", "cpus": 4,
"hda_disk_interface": "virtio",
"arch": "x86_64", "arch": "x86_64",
"console_type": "telnet", "console_type": "telnet",
"boot_priority": "cd", "boot_priority": "c",
"kvm": "allow" "kvm": "require",
"on_close": "shutdown_signal"
}, },
"images": [ "images": [
{ {
"filename": "vyos-1.3.2-amd64.iso", "filename": "vyos-1.4.0-kvm-amd64.qcow2",
"version": "1.4.0",
"md5sum": "a130e446bc5bf87391981f183ee3694b",
"filesize": 468320256,
"download_url": "https://support.vyos.io/"
},
{
"filename": "vyos-1.3.7-qemu-amd64.qcow2",
"version": "1.3.7",
"md5sum": "f4663b1e2df115bfa5c7ec17584514d6",
"filesize": 359792640,
"download_url": "https://support.vyos.io/"
},
{
"filename": "vyos-1.3.2-10G-qemu.qcow2",
"version": "1.3.2", "version": "1.3.2",
"md5sum": "070743faac800f9e5197058a8b6b3ba1", "md5sum": "68ad3fb530213189ac9ed496d5fe7897",
"filesize": 334495744, "filesize": 326893568,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-3-2-generic-iso-image" "download_url": "https://support.vyos.io/"
}, },
{ {
"filename": "vyos-1.3.1-S1-amd64.iso", "filename": "vyos-1.3.1-S1-10G-qemu.qcow2",
"version": "1.3.1-S1", "version": "1.3.1-S1",
"md5sum": "781f345e8a4ab9eb9e075ce5c87c8817", "md5sum": "d8ed9f82a983295b94b07f8e37c48ed0",
"filesize": 351272960, "filesize": 343801856,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-3-1-s1-generic-iso-image" "download_url": "https://support.vyos.io/"
}, },
{ {
"filename": "vyos-1.3.1-amd64.iso", "filename": "vyos-1.3.1-10G-qemu.qcow2",
"version": "1.3.1", "version": "1.3.1",
"md5sum": "b6f57bd0cf9b60cdafa337b08ba4f2bc", "md5sum": "482367c833990fb2b9350e3708d33dc9",
"filesize": 350224384, "filesize": 342556672,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-3-1-generic-iso-image" "download_url": "https://support.vyos.io/"
}, },
{ {
"filename": "vyos-1.3.0-amd64.iso", "filename": "vyos-1.3.0-10G-qemu.qcow2",
"version": "1.3.0", "version": "1.3.0",
"md5sum": "2019bd9c5efa6194e2761de678d0073f", "md5sum": "086e95e992e9b4d014c5f154cd01a6e6",
"filesize": 338690048, "filesize": 330956800,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-3-0-generic-iso-image" "download_url": "https://support.vyos.io/"
},
{
"filename": "vyos-1.2.9-S1-amd64.iso",
"version": "1.2.9-S1",
"md5sum": "3fece6363f9766f862e26d292d0ed5a3",
"filesize": 430964736,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-9-s1-generic-iso-image",
"direct_download_url": "https://legacy-lts-images.vyos.io/1.2.9-S1/vyos-1.2.9-S1-amd64.iso"
}, },
{ {
"filename": "vyos-1.2.9-S1-10G-qemu.qcow2", "filename": "vyos-1.2.9-S1-10G-qemu.qcow2",
"version": "1.2.9-S1-KVM", "version": "1.2.9-S1",
"md5sum": "0a70d78b80a3716d42487c02ef44f41f", "md5sum": "0a70d78b80a3716d42487c02ef44f41f",
"filesize": 426967040, "filesize": 426967040,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-9-s1-for-kvm", "download_url": "https://support.vyos.io/"
"direct_download_url": "https://legacy-lts-images.vyos.io/1.2.9-S1/vyos-1.2.9-S1-10G-qemu.qcow2"
}, },
{ {
"filename": "vyos-1.2.9-amd64.iso", "filename": "vyos-1.2.8-10G-qemu.qcow2",
"version": "1.2.9",
"md5sum": "586be23b6256173e174c82d8f1f699a1",
"filesize": 430964736,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-9-generic-iso-image",
"direct_download_url": "https://legacy-lts-images.vyos.io/1.2.9/vyos-1.2.9-amd64.iso"
},
{
"filename": "vyos-1.2.9-10G-qemu.qcow2",
"version": "1.2.9-KVM",
"md5sum": "76871c7b248c32f75177c419128257ac",
"filesize": 427360256,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-9-10g-qemu-qcow2",
"direct_download_url": "https://legacy-lts-images.vyos.io/1.2.9/vyos-1.2.9-10G-qemu.qcow2"
},
{
"filename": "vyos-1.2.8-amd64.iso",
"version": "1.2.8", "version": "1.2.8",
"md5sum": "0ad879db903efdbf1c39dc945e165931", "md5sum": "96c76f619d0f8ea11dc8a3a18ed67b98",
"filesize": 429916160, "filesize": 425852928,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-8-generic-iso-image" "download_url": "https://support.vyos.io/"
}, },
{ {
"filename": "vyos-1.1.8-amd64.iso", "filename": "vyos-1.2.7-qemu.qcow2",
"version": "1.1.8", "version": "1.2.7",
"md5sum": "95a141d4b592b81c803cdf7e9b11d8ea", "md5sum": "1be4674c970fcdd65067e504baea5d74",
"filesize": 241172480, "filesize": 424607744,
"direct_download_url": "https://legacy-lts-images.vyos.io/vyos-1.1.8-amd64.iso" "download_url": "https://support.vyos.io/"
}, },
{ {
"filename": "empty8G.qcow2", "filename": "vyos-1.2.6-qemu.qcow2",
"version": "1.0", "version": "1.2.6",
"md5sum": "f1d2c25b6990f99bd05b433ab603bdb4", "md5sum": "d8010d79889ca0ba5cb2634665e548e3",
"filesize": 197120, "filesize": 424607744,
"download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/", "download_url": "https://support.vyos.io/"
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty8G.qcow2/download" },
{
"filename": "vyos-1.2.5-amd64.qcow2",
"version": "1.2.5",
"md5sum": "110c22309ec480600446fd2fb4f27a0d",
"filesize": 411500544 ,
"download_url": "https://support.vyos.io/"
} }
], ],
"versions": [ "versions": [
{
"name": "1.4.0",
"images": {
"hda_disk_image": "vyos-1.4.0-kvm-amd64.qcow2"
}
},
{
"name": "1.3.7",
"images": {
"hda_disk_image": "vyos-1.3.7-qemu-amd64.qcow2"
}
},
{ {
"name": "1.3.2", "name": "1.3.2",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "vyos-1.3.2-10G-qemu.qcow2"
"cdrom_image": "vyos-1.3.2-amd64.iso"
} }
}, },
{ {
"name": "1.3.1-S1", "name": "1.3.1-S1",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "vyos-1.3.1-S1-10G-qemu.qcow2"
"cdrom_image": "vyos-1.3.1-S1-amd64.iso"
} }
}, },
{ {
"name": "1.3.1", "name": "1.3.1",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "vyos-1.3.1-10G-qemu.qcow2"
"cdrom_image": "vyos-1.3.1-amd64.iso"
} }
}, },
{ {
"name": "1.3.0", "name": "1.3.0",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "vyos-1.3.0-10G-qemu.qcow2"
"cdrom_image": "vyos-1.3.0-amd64.iso"
} }
}, },
{ {
"name": "1.2.9-S1", "name": "1.2.9-S1",
"images": {
"hda_disk_image": "empty8G.qcow2",
"cdrom_image": "vyos-1.2.9-S1-amd64.iso"
}
},
{
"name": "1.2.9-S1-KVM",
"images": { "images": {
"hda_disk_image": "vyos-1.2.9-S1-10G-qemu.qcow2" "hda_disk_image": "vyos-1.2.9-S1-10G-qemu.qcow2"
} }
}, },
{
"name": "1.2.9",
"images": {
"hda_disk_image": "empty8G.qcow2",
"cdrom_image": "vyos-1.2.9-amd64.iso"
}
},
{
"name": "1.2.9-KVM",
"images": {
"hda_disk_image": "vyos-1.2.9-10G-qemu.qcow2"
}
},
{ {
"name": "1.2.8", "name": "1.2.8",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "vyos-1.2.8-10G-qemu.qcow2"
"cdrom_image": "vyos-1.2.8-amd64.iso"
} }
}, },
{ {
"name": "1.1.8", "name": "1.2.7",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "vyos-1.2.7-qemu.qcow2"
"cdrom_image": "vyos-1.1.8-amd64.iso" }
},
{
"name": "1.2.6",
"images": {
"hda_disk_image": "vyos-1.2.6-qemu.qcow2"
}
},
{
"name": "1.2.5",
"images": {
"hda_disk_image": "vyos-1.2.5-amd64.qcow2"
} }
} }
] ]

View File

@ -33,7 +33,7 @@ from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
from gns3server.utils.asyncio.raw_command_server import AsyncioRawCommandServer from gns3server.utils.asyncio.raw_command_server import AsyncioRawCommandServer
from gns3server.utils.asyncio import wait_for_file_creation from gns3server.utils.asyncio import wait_for_file_creation
from gns3server.utils.asyncio import monitor_process from gns3server.utils.asyncio import monitor_process
from gns3server.utils.get_resource import get_resource from gns3server.utils import macaddress_to_int, int_to_macaddress
from gns3server.ubridge.ubridge_error import UbridgeError, UbridgeNamespaceError from gns3server.ubridge.ubridge_error import UbridgeError, UbridgeNamespaceError
from ..base_node import BaseNode from ..base_node import BaseNode
@ -83,6 +83,7 @@ class DockerVM(BaseNode):
self._environment = environment self._environment = environment
self._cid = None self._cid = None
self._ethernet_adapters = [] self._ethernet_adapters = []
self._mac_address = ""
self._temporary_directory = None self._temporary_directory = None
self._telnet_servers = [] self._telnet_servers = []
self._vnc_process = None self._vnc_process = None
@ -106,6 +107,8 @@ class DockerVM(BaseNode):
else: else:
self.adapters = adapters self.adapters = adapters
self.mac_address = "" # this will generate a MAC address
log.debug("{module}: {name} [{image}] initialized.".format(module=self.manager.module_name, log.debug("{module}: {name} [{image}] initialized.".format(module=self.manager.module_name,
name=self.name, name=self.name,
image=self._image)) image=self._image))
@ -119,6 +122,7 @@ class DockerVM(BaseNode):
"project_id": self._project.id, "project_id": self._project.id,
"image": self._image, "image": self._image,
"adapters": self.adapters, "adapters": self.adapters,
"mac_address": self.mac_address,
"console": self.console, "console": self.console,
"console_type": self.console_type, "console_type": self.console_type,
"console_resolution": self.console_resolution, "console_resolution": self.console_resolution,
@ -149,6 +153,36 @@ class DockerVM(BaseNode):
def ethernet_adapters(self): def ethernet_adapters(self):
return self._ethernet_adapters return self._ethernet_adapters
@property
def mac_address(self):
"""
Returns the MAC address for this Docker container.
:returns: adapter type (string)
"""
return self._mac_address
@mac_address.setter
def mac_address(self, mac_address):
"""
Sets the MAC address for this Docker container.
:param mac_address: MAC address
"""
if not mac_address:
# use the node UUID to generate a random MAC address
self._mac_address = "02:42:%s:%s:%s:00" % (self.id[2:4], self.id[4:6], self.id[6:8])
else:
self._mac_address = mac_address
log.info('Docker container "{name}" [{id}]: MAC address changed to {mac_addr}'.format(
name=self._name,
id=self._id,
mac_addr=self._mac_address)
)
@property @property
def start_command(self): def start_command(self):
return self._start_command return self._start_command
@ -350,6 +384,7 @@ class DockerVM(BaseNode):
"Privileged": True, "Privileged": True,
"Binds": self._mount_binds(image_infos), "Binds": self._mount_binds(image_infos),
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"Env": ["container=docker"], # Systemd compliant: https://github.com/GNS3/gns3-server/issues/573 "Env": ["container=docker"], # Systemd compliant: https://github.com/GNS3/gns3-server/issues/573
"Cmd": [], "Cmd": [],
@ -914,15 +949,33 @@ class DockerVM(BaseNode):
bridge_name = 'bridge{}'.format(adapter_number) bridge_name = 'bridge{}'.format(adapter_number)
await self._ubridge_send('bridge create {}'.format(bridge_name)) await self._ubridge_send('bridge create {}'.format(bridge_name))
self._bridges.add(bridge_name) self._bridges.add(bridge_name)
await self._ubridge_send('bridge add_nio_tap bridge{adapter_number} {hostif}'.format(adapter_number=adapter_number, await self._ubridge_send('bridge add_nio_tap bridge{adapter_number} {hostif}'.format(
hostif=adapter.host_ifc)) adapter_number=adapter_number,
hostif=adapter.host_ifc)
)
mac_address = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
custom_adapter = self._get_custom_adapter_settings(adapter_number)
custom_mac_address = custom_adapter.get("mac_address")
if custom_mac_address:
mac_address = custom_mac_address
try:
await self._ubridge_send('docker set_mac_addr {ifc} {mac}'.format(ifc=adapter.host_ifc, mac=mac_address))
except UbridgeError:
log.warning("Could not set MAC address %s on interface %s", mac_address, adapter.host_ifc)
log.debug("Move container %s adapter %s to namespace %s", self.name, adapter.host_ifc, self._namespace) log.debug("Move container %s adapter %s to namespace %s", self.name, adapter.host_ifc, self._namespace)
try: try:
await self._ubridge_send('docker move_to_ns {ifc} {ns} eth{adapter}'.format(ifc=adapter.host_ifc, await self._ubridge_send('docker move_to_ns {ifc} {ns} eth{adapter}'.format(
ifc=adapter.host_ifc,
ns=self._namespace, ns=self._namespace,
adapter=adapter_number)) adapter=adapter_number)
)
except UbridgeError as e: except UbridgeError as e:
raise UbridgeNamespaceError(e) raise UbridgeNamespaceError(e)
else:
log.info("Created adapter %s with MAC address %s in namespace %s", adapter_number, mac_address, self._namespace)
if nio: if nio:
await self._connect_nio(adapter_number, nio) await self._connect_nio(adapter_number, nio)

View File

@ -25,7 +25,10 @@ PATH=/gns3/bin:/tmp/gns3/bin:/sbin:$PATH
# bootstrap busybox commands # bootstrap busybox commands
if [ ! -d /tmp/gns3/bin ]; then if [ ! -d /tmp/gns3/bin ]; then
busybox mkdir -p /tmp/gns3/bin busybox mkdir -p /tmp/gns3/bin
/gns3/bin/busybox --install -s /tmp/gns3/bin for applet in `busybox --list-full`
do
ln -s /gns3/bin/busybox "/tmp/gns3/bin/$applet"
done
fi fi
# Restore file permission and mount volumes # Restore file permission and mount volumes
@ -75,7 +78,7 @@ ip link set dev lo up
while true while true
do do
grep $GNS3_MAX_ETHERNET /proc/net/dev > /dev/null && break grep $GNS3_MAX_ETHERNET /proc/net/dev > /dev/null && break
sleep 0.5 usleep 500000 # wait 0.5 seconds
done done
# activate eth interfaces # activate eth interfaces

View File

@ -70,8 +70,9 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
files = [f for f in files if _is_exportable(os.path.join(root, f), include_snapshots)] files = [f for f in files if _is_exportable(os.path.join(root, f), include_snapshots)]
for file in files: for file in files:
path = os.path.join(root, file) path = os.path.join(root, file)
# check if we can export the file if not os.path.islink(path):
try: try:
# check if we can export the file
open(path).close() open(path).close()
except OSError as e: except OSError as e:
msg = "Could not export file {}: {}".format(path, e) msg = "Could not export file {}: {}".format(path, e)
@ -128,7 +129,7 @@ def _patch_mtime(path):
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
# only UNIX type platforms # only UNIX type platforms
return return
st = os.stat(path) st = os.stat(path, follow_symlinks=False)
file_date = datetime.fromtimestamp(st.st_mtime) file_date = datetime.fromtimestamp(st.st_mtime)
if file_date.year < 1980: if file_date.year < 1980:
new_mtime = file_date.replace(year=1980).timestamp() new_mtime = file_date.replace(year=1980).timestamp()
@ -144,10 +145,6 @@ def _is_exportable(path, include_snapshots=False):
if include_snapshots is False and path.endswith("snapshots"): if include_snapshots is False and path.endswith("snapshots"):
return False return False
# do not export symlinks
if os.path.islink(path):
return False
# do not export directories of snapshots # do not export directories of snapshots
if include_snapshots is False and "{sep}snapshots{sep}".format(sep=os.path.sep) in path: if include_snapshots is False and "{sep}snapshots{sep}".format(sep=os.path.sep) in path:
return False return False
@ -200,13 +197,16 @@ async def _patch_project_file(project, path, zstream, include_images, keep_compu
if not keep_compute_ids: if not keep_compute_ids:
node["compute_id"] = "local" # To make project portable all node by default run on local node["compute_id"] = "local" # To make project portable all node by default run on local
if "properties" in node and node["node_type"] != "docker": if "properties" in node:
for prop, value in node["properties"].items(): for prop, value in node["properties"].items():
# reset the MAC address # reset the MAC address
if reset_mac_addresses and prop in ("mac_addr", "mac_address"): if reset_mac_addresses and prop in ("mac_addr", "mac_address"):
node["properties"][prop] = None node["properties"][prop] = None
if node["node_type"] == "docker":
continue
if node["node_type"] == "iou": if node["node_type"] == "iou":
if not prop == "path": if not prop == "path":
continue continue

View File

@ -17,6 +17,7 @@
import os import os
import sys import sys
import stat
import json import json
import uuid import uuid
import shutil import shutil
@ -93,6 +94,7 @@ async def import_project(controller, project_id, stream, location=None, name=Non
try: try:
with zipfile.ZipFile(stream) as zip_file: with zipfile.ZipFile(stream) as zip_file:
await wait_run_in_executor(zip_file.extractall, path) await wait_run_in_executor(zip_file.extractall, path)
_create_symbolic_links(zip_file, path)
except zipfile.BadZipFile: except zipfile.BadZipFile:
raise aiohttp.web.HTTPConflict(text="Cannot extract files from GNS3 project (invalid zip)") raise aiohttp.web.HTTPConflict(text="Cannot extract files from GNS3 project (invalid zip)")
@ -174,6 +176,24 @@ async def import_project(controller, project_id, stream, location=None, name=Non
project = await controller.load_project(dot_gns3_path, load=False) project = await controller.load_project(dot_gns3_path, load=False)
return project return project
def _create_symbolic_links(zip_file, path):
"""
Manually create symbolic links (if any) because ZipFile does not support it.
:param zip_file: ZipFile instance
:param path: project location
"""
for zip_info in zip_file.infolist():
if stat.S_ISLNK(zip_info.external_attr >> 16):
symlink_target = zip_file.read(zip_info.filename).decode()
symlink_path = os.path.join(path, zip_info.filename)
try:
# remove the regular file and replace it by a symbolic link
os.remove(symlink_path)
os.symlink(symlink_target, symlink_path)
except OSError as e:
raise aiohttp.web.HTTPConflict(text=f"Cannot create symbolic link: {e}")
def _move_node_file(path, old_id, new_id): def _move_node_file(path, old_id, new_id):
""" """
@ -257,6 +277,7 @@ async def _import_snapshots(snapshots_path, project_name, project_id):
with open(snapshot_path, "rb") as f: with open(snapshot_path, "rb") as f:
with zipfile.ZipFile(f) as zip_file: with zipfile.ZipFile(f) as zip_file:
await wait_run_in_executor(zip_file.extractall, tmpdir) await wait_run_in_executor(zip_file.extractall, tmpdir)
_create_symbolic_links(zip_file, tmpdir)
except OSError as e: except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot open snapshot '{}': {}".format(os.path.basename(snapshot), e)) raise aiohttp.web.HTTPConflict(text="Cannot open snapshot '{}': {}".format(os.path.basename(snapshot), e))
except zipfile.BadZipFile: except zipfile.BadZipFile:

View File

@ -26,8 +26,8 @@ import os
from .compute import ComputeConflict, ComputeError from .compute import ComputeConflict, ComputeError
from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory
from ..utils.images import images_directories from ..utils.images import images_directories
from ..utils import macaddress_to_int, int_to_macaddress
from ..config import Config from ..config import Config
from ..utils.qt import qt_font_to_style
import logging import logging
@ -663,7 +663,13 @@ class Node:
break break
port_name = "eth{}".format(adapter_number) port_name = "eth{}".format(adapter_number)
port_name = custom_adapter_settings.get("port_name", port_name) port_name = custom_adapter_settings.get("port_name", port_name)
self._ports.append(PortFactory(port_name, 0, adapter_number, 0, "ethernet", short_name=port_name)) mac_address = custom_adapter_settings.get("mac_address")
if not mac_address and "mac_address" in self._properties:
mac_address = int_to_macaddress(macaddress_to_int(self._properties["mac_address"]) + adapter_number)
port = PortFactory(port_name, 0, adapter_number, 0, "ethernet", short_name=port_name)
port.mac_address = mac_address
self._ports.append(port)
elif self._node_type in ("ethernet_switch", "ethernet_hub"): elif self._node_type in ("ethernet_switch", "ethernet_hub"):
# Basic node we don't want to have adapter number # Basic node we don't want to have adapter number
port_number = 0 port_number = 0

View File

@ -15,6 +15,7 @@
# 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 sys
import re import re
import os import os
import json import json
@ -27,6 +28,7 @@ import aiohttp
import aiofiles import aiofiles
import tempfile import tempfile
import zipfile import zipfile
import pathlib
from uuid import UUID, uuid4 from uuid import UUID, uuid4
@ -42,8 +44,9 @@ from ..utils.application_id import get_next_application_id
from ..utils.asyncio.pool import Pool from ..utils.asyncio.pool import Pool
from ..utils.asyncio import locking from ..utils.asyncio import locking
from ..utils.asyncio import aiozipstream from ..utils.asyncio import aiozipstream
from ..utils.asyncio import wait_run_in_executor
from .export_project import export_project from .export_project import export_project
from .import_project import import_project from .import_project import import_project, _move_node_file
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -1037,14 +1040,16 @@ class Project:
""" """
Duplicate a project Duplicate a project
It's the save as feature of the 1.X. It's implemented on top of the Implemented on top of the export / import features. It will generate a gns3p and reimport it.
export / import features. It will generate a gns3p and reimport it.
It's a little slower but we have only one implementation to maintain. NEW: fast duplication is used if possible (when there are no remote computes).
If not, the project is exported and reimported as explained above.
:param name: Name of the new project. A new one will be generated in case of conflicts :param name: Name of the new project. A new one will be generated in case of conflicts
:param location: Parent directory of the new project :param location: Parent directory of the new project
:param reset_mac_addresses: Reset MAC addresses for the new project :param reset_mac_addresses: Reset MAC addresses for the new project
""" """
# If the project was not open we open it temporary # If the project was not open we open it temporary
previous_status = self._status previous_status = self._status
if self._status == "closed": if self._status == "closed":
@ -1052,6 +1057,18 @@ class Project:
self.dump() self.dump()
assert self._status != "closed" assert self._status != "closed"
try:
proj = await self._fast_duplication(name, location, reset_mac_addresses)
if proj:
if previous_status == "closed":
await self.close()
return proj
else:
log.info("Fast duplication failed, fallback to normal duplication")
except Exception as e:
raise aiohttp.web.HTTPConflict(text="Cannot duplicate project: {}".format(str(e)))
try: try:
begin = time.time() begin = time.time()
@ -1237,3 +1254,70 @@ class Project:
def __repr__(self): def __repr__(self):
return "<gns3server.controller.Project {} {}>".format(self._name, self._id) return "<gns3server.controller.Project {} {}>".format(self._name, self._id)
async def _fast_duplication(self, name=None, location=None, reset_mac_addresses=True):
"""
Fast duplication of a project.
Copy the project files directly rather than in an import-export fashion.
:param name: Name of the new project. A new one will be generated in case of conflicts
:param location: Parent directory of the new project
:param reset_mac_addresses: Reset MAC addresses for the new project
"""
# remote replication is not supported with remote computes
for compute in self.computes:
if compute.id != "local":
log.warning("Fast duplication is not supported with remote compute: '{}'".format(compute.id))
return None
# work dir
p_work = pathlib.Path(location or self.path).parent.absolute()
t0 = time.time()
new_project_id = str(uuid.uuid4())
new_project_path = p_work.joinpath(new_project_id)
# copy dir
await wait_run_in_executor(shutil.copytree, self.path, new_project_path.as_posix(), symlinks=True, ignore_dangling_symlinks=True)
log.info("Project content copied from '{}' to '{}' in {}s".format(self.path, new_project_path, time.time() - t0))
topology = json.loads(new_project_path.joinpath('{}.gns3'.format(self.name)).read_bytes())
project_name = name or topology["name"]
# If the project name is already used we generate a new one
project_name = self.controller.get_free_project_name(project_name)
topology["name"] = project_name
# To avoid unexpected behavior (project start without manual operations just after import)
topology["auto_start"] = False
topology["auto_open"] = False
topology["auto_close"] = False
# change node ID
node_old_to_new = {}
for node in topology["topology"]["nodes"]:
new_node_id = str(uuid.uuid4())
if "node_id" in node:
node_old_to_new[node["node_id"]] = new_node_id
_move_node_file(new_project_path, node["node_id"], new_node_id)
node["node_id"] = new_node_id
if reset_mac_addresses:
if "properties" in node:
for prop, value in node["properties"].items():
# reset the MAC address
if prop in ("mac_addr", "mac_address"):
node["properties"][prop] = None
# change link ID
for link in topology["topology"]["links"]:
link["link_id"] = str(uuid.uuid4())
for node in link["nodes"]:
node["node_id"] = node_old_to_new[node["node_id"]]
# Generate new drawings id
for drawing in topology["topology"]["drawings"]:
drawing["drawing_id"] = str(uuid.uuid4())
# And we dump the updated.gns3
dot_gns3_path = new_project_path.joinpath('{}.gns3'.format(project_name))
topology["project_id"] = new_project_id
with open(dot_gns3_path, "w+") as f:
json.dump(topology, f, indent=4)
os.remove(new_project_path.joinpath('{}.gns3'.format(self.name)))
project = await self.controller.load_project(dot_gns3_path, load=False)
log.info("Project '{}' fast duplicated in {:.4f} seconds".format(project.name, time.time() - t0))
return project

View File

@ -188,7 +188,7 @@ def load_topology(path):
try: try:
_check_topology_schema(topo) _check_topology_schema(topo)
except aiohttp.web.HTTPConflict as e: except aiohttp.web.HTTPConflict as e:
log.error("Can't load the topology %s", path) log.error("Can't load the topology %s, please check using the debug mode...", path)
raise e raise e
if changed: if changed:

View File

@ -57,7 +57,7 @@ class CrashReport:
Report crash to a third party service Report crash to a third party service
""" """
DSN = "https://46838dcfc8a27b89afc8d5587c0f6230@o19455.ingest.us.sentry.io/38482" DSN = "https://decb06a5d7bda07bef342a41c5c9d6e1@o19455.ingest.us.sentry.io/38482"
_instance = None _instance = None
def __init__(self): def __init__(self):

View File

@ -317,7 +317,7 @@ class DockerHandler:
props = [ props = [
"name", "console", "aux", "console_type", "console_resolution", "name", "console", "aux", "console_type", "console_resolution",
"console_http_port", "console_http_path", "start_command", "console_http_port", "console_http_path", "start_command",
"environment", "adapters", "extra_hosts", "extra_volumes" "environment", "adapters", "mac_address", "custom_adapters", "extra_hosts", "extra_volumes"
] ]
changed = False changed = False

View File

@ -85,6 +85,12 @@ DOCKER_CREATE_SCHEMA = {
"minimum": 0, "minimum": 0,
"maximum": 99, "maximum": 99,
}, },
"mac_address": {
"description": "Docker container base MAC address",
"type": ["string", "null"],
"minLength": 1,
"pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"
},
"environment": { "environment": {
"description": "Docker environment variables", "description": "Docker environment variables",
"type": ["string", "null"], "type": ["string", "null"],
@ -187,6 +193,12 @@ DOCKER_OBJECT_SCHEMA = {
"minimum": 0, "minimum": 0,
"maximum": 99, "maximum": 99,
}, },
"mac_address": {
"description": "Docker container base MAC address",
"type": ["string", "null"],
"minLength": 1,
"pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"
},
"usage": { "usage": {
"description": "How to use the Docker container", "description": "How to use the Docker container",
"type": "string", "type": "string",

View File

@ -38,6 +38,15 @@ DOCKER_TEMPLATE_PROPERTIES = {
"maximum": 99, "maximum": 99,
"default": 1 "default": 1
}, },
"mac_address": {
"description": "Docker container base MAC address",
"type": ["string", "null"],
"anyOf": [
{"pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"},
{"pattern": "^$"}
],
"default": "",
},
"start_command": { "start_command": {
"description": "Docker CMD entry", "description": "Docker CMD entry",
"type": "string", "type": "string",

View File

@ -46,6 +46,6 @@
gtag('config', 'G-0BT7QQV1W1'); gtag('config', 'G-0BT7QQV1W1');
</script> </script>
<script src="runtime.415291667f70565cd8ef.js" defer></script><script src="polyfills-es5.865074f5cd9a121111a2.js" nomodule defer></script><script src="polyfills.2f91a039d848e57ff02e.js" defer></script><script src="main.99a70a8d013fc974361c.js" defer></script> <script src="runtime.415291667f70565cd8ef.js" defer></script><script src="polyfills-es5.865074f5cd9a121111a2.js" nomodule defer></script><script src="polyfills.2f91a039d848e57ff02e.js" defer></script><script src="main.df8c319a3da6fb0e3629.js" defer></script>
</body></html> </body></html>

View File

@ -161,8 +161,11 @@ class ZipFile(zipfile.ZipFile):
self._comment = comment self._comment = comment
self._didModify = True self._didModify = True
async def data_generator(self, path): async def data_generator(self, path, islink=False):
if islink:
yield os.readlink(path).encode()
else:
async with aiofiles.open(path, "rb") as f: async with aiofiles.open(path, "rb") as f:
while True: while True:
part = await f.read(self._chunksize) part = await f.read(self._chunksize)
@ -224,12 +227,13 @@ class ZipFile(zipfile.ZipFile):
raise ValueError("either (exclusively) filename or iterable shall be not None") raise ValueError("either (exclusively) filename or iterable shall be not None")
if filename: if filename:
st = os.stat(filename) st = os.stat(filename, follow_symlinks=False)
isdir = stat.S_ISDIR(st.st_mode) isdir = stat.S_ISDIR(st.st_mode)
islink = stat.S_ISLNK(st.st_mode)
mtime = time.localtime(st.st_mtime) mtime = time.localtime(st.st_mtime)
date_time = mtime[0:6] date_time = mtime[0:6]
else: else:
st, isdir, date_time = None, False, time.localtime()[0:6] st, isdir, islink, date_time = None, False, False, time.localtime()[0:6]
# Create ZipInfo instance to store file information # Create ZipInfo instance to store file information
if arcname is None: if arcname is None:
arcname = filename arcname = filename
@ -282,7 +286,7 @@ class ZipFile(zipfile.ZipFile):
file_size = 0 file_size = 0
if filename: if filename:
async for buf in self.data_generator(filename): async for buf in self.data_generator(filename, islink):
file_size = file_size + len(buf) file_size = file_size + len(buf)
CRC = zipfile.crc32(buf, CRC) & 0xffffffff CRC = zipfile.crc32(buf, CRC) & 0xffffffff
if cmpr: if cmpr:

View File

@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version # or negative for a release candidate or beta (after the base version
# number has been incremented) # number has been incremented)
__version__ = "2.2.49" __version__ = "2.2.50"
__version_info__ = (2, 2, 49, 0) __version_info__ = (2, 2, 50, 0)
if "dev" in __version__: if "dev" in __version__:
try: try:

View File

@ -1,9 +1,9 @@
jsonschema>=4.23,<4.24 jsonschema>=4.23,<4.24
aiohttp>=3.9.5,<3.10 aiohttp>=3.10.3,<3.11
aiohttp-cors>=0.7.0,<0.8 aiohttp-cors>=0.7.0,<0.8
aiofiles>=24.1.0,<25.0 aiofiles>=24.1.0,<25.0
Jinja2>=3.1.4,<3.2 Jinja2>=3.1.4,<3.2
sentry-sdk==2.12,<2.13 sentry-sdk==2.12,<2.13 # optional dependency
psutil==6.0.0 psutil==6.0.0
async-timeout>=4.0.3,<4.1 async-timeout>=4.0.3,<4.1
distro>=1.9.0 distro>=1.9.0

View File

@ -48,6 +48,7 @@ async def vm(compute_project, manager):
vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest")
vm._cid = "e90e34656842" vm._cid = "e90e34656842"
vm.allocate_aux = False vm.allocate_aux = False
vm.mac_address = '02:42:3d:b7:93:00'
return vm return vm
@ -60,6 +61,7 @@ def test_json(vm, compute_project):
'project_id': compute_project.id, 'project_id': compute_project.id,
'node_id': vm.id, 'node_id': vm.id,
'adapters': 1, 'adapters': 1,
'mac_address': '02:42:3d:b7:93:00',
'console': vm.console, 'console': vm.console,
'console_type': 'telnet', 'console_type': 'telnet',
'console_resolution': '1024x768', 'console_resolution': '1024x768',
@ -107,6 +109,7 @@ async def test_create(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -145,6 +148,7 @@ async def test_create_with_tag(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -187,6 +191,7 @@ async def test_create_vnc(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -316,6 +321,7 @@ async def test_create_start_cmd(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"Entrypoint": ["/gns3/init.sh"], "Entrypoint": ["/gns3/init.sh"],
"Cmd": ["/bin/ls"], "Cmd": ["/bin/ls"],
@ -414,6 +420,7 @@ async def test_create_image_not_available(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -457,6 +464,7 @@ async def test_create_with_user(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -540,6 +548,7 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -579,6 +588,7 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -618,6 +628,7 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -657,6 +668,7 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project,
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -695,6 +707,7 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -733,6 +746,7 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -779,6 +793,7 @@ async def test_create_with_extra_volumes(compute_project, manager):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -1027,6 +1042,7 @@ async def test_update(vm):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -1095,6 +1111,7 @@ async def test_update_running(vm):
], ],
"Privileged": True "Privileged": True
}, },
"UsernsMode": "host",
"Volumes": {}, "Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Hostname": "test", "Hostname": "test",
@ -1185,7 +1202,37 @@ async def test_add_ubridge_connection(vm):
call.send('bridge start bridge0') call.send('bridge start bridge0')
] ]
assert 'bridge0' in vm._bridges assert 'bridge0' in vm._bridges
# We need to check any_order ortherwise mock is confused by asyncio # We need to check any_order otherwise mock is confused by asyncio
vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True)
async def test_add_ubridge_connections_with_base_mac_address(vm):
vm._ubridge_hypervisor = MagicMock()
vm._namespace = 42
vm.adapters = 2
vm.mac_address = "02:42:42:42:42:00"
nio_params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"}
nio = vm.manager.create_nio(nio_params)
await vm._add_ubridge_connection(nio, 0)
nio = vm.manager.create_nio(nio_params)
await vm._add_ubridge_connection(nio, 1)
calls = [
call.send('bridge create bridge0'),
call.send('bridge create bridge1'),
call.send('docker set_mac_addr tap-gns3-e0 02:42:42:42:42:00'),
call.send('docker set_mac_addr tap-gns3-e0 02:42:42:42:42:01')
]
# We need to check any_order otherwise mock is confused by asyncio
vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True) vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True)

View File

@ -21,6 +21,7 @@ import json
import pytest import pytest
import aiohttp import aiohttp
import zipfile import zipfile
import stat
from pathlib import Path from pathlib import Path
from unittest.mock import patch from unittest.mock import patch
@ -116,6 +117,8 @@ async def test_export(tmpdir, project):
with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f: with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f:
f.write("WORLD") f.write("WORLD")
os.symlink("/tmp/anywhere", os.path.join(path, "vm-1", "dynamips", "symlink"))
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
await export_project(z, project, str(tmpdir), include_images=False) await export_project(z, project, str(tmpdir), include_images=False)
@ -131,9 +134,12 @@ async def test_export(tmpdir, project):
assert 'vm-1/dynamips/empty-dir/' in myzip.namelist() assert 'vm-1/dynamips/empty-dir/' in myzip.namelist()
assert 'project-files/snapshots/test' not in myzip.namelist() assert 'project-files/snapshots/test' not in myzip.namelist()
assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist()
assert 'images/IOS/test.image' not in myzip.namelist() assert 'images/IOS/test.image' not in myzip.namelist()
assert 'vm-1/dynamips/symlink' in myzip.namelist()
zip_info = myzip.getinfo('vm-1/dynamips/symlink')
assert stat.S_ISLNK(zip_info.external_attr >> 16)
with myzip.open("project.gns3") as myfile: with myzip.open("project.gns3") as myfile:
topo = json.loads(myfile.read().decode())["topology"] topo = json.loads(myfile.read().decode())["topology"]
assert topo["nodes"][0]["compute_id"] == "local" # All node should have compute_id local after export assert topo["nodes"][0]["compute_id"] == "local" # All node should have compute_id local after export

View File

@ -21,7 +21,11 @@ import json
import zipfile import zipfile
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest.mock import patch, MagicMock
from gns3server.utils.asyncio import aiozipstream
from gns3server.controller.project import Project
from gns3server.controller.export_project import export_project
from gns3server.controller.import_project import import_project, _move_files_to_compute from gns3server.controller.import_project import import_project, _move_files_to_compute
from gns3server.version import __version__ from gns3server.version import __version__
@ -106,6 +110,54 @@ async def test_import_project_override(tmpdir, controller):
assert project.name == "test" assert project.name == "test"
async def write_file(path, z):
with open(path, 'wb') as f:
async for chunk in z:
f.write(chunk)
async def test_import_project_containing_symlink(tmpdir, controller):
project = Project(controller=controller, name="test")
project.dump = MagicMock()
path = project.path
project_id = str(uuid.uuid4())
topology = {
"project_id": str(uuid.uuid4()),
"name": "test",
"auto_open": True,
"auto_start": True,
"topology": {
},
"version": "2.0.0"
}
with open(os.path.join(path, "project.gns3"), 'w+') as f:
json.dump(topology, f)
os.makedirs(os.path.join(path, "vm1", "dynamips"))
symlink_path = os.path.join(project.path, "vm1", "dynamips", "symlink")
symlink_target = "/tmp/anywhere"
os.symlink(symlink_target, symlink_path)
zip_path = str(tmpdir / "project.zip")
with aiozipstream.ZipFile() as z:
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
await export_project(z, project, str(tmpdir), include_images=False)
await write_file(zip_path, z)
with open(zip_path, "rb") as f:
project = await import_project(controller, project_id, f)
assert project.name == "test"
assert project.id == project_id
symlink_path = os.path.join(project.path, "vm1", "dynamips", "symlink")
assert os.path.islink(symlink_path)
assert os.readlink(symlink_path) == symlink_target
async def test_import_upgrade(tmpdir, controller): async def test_import_upgrade(tmpdir, controller):
""" """
Topology made for previous GNS3 version are upgraded during the process Topology made for previous GNS3 version are upgraded during the process