gns3-server 3.0.0rc2__py3-none-any.whl → 3.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of gns3-server might be problematic. Click here for more details.
- {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/METADATA +19 -18
- {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/RECORD +48 -42
- {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/WHEEL +1 -1
- gns3server/api/routes/controller/images.py +58 -21
- gns3server/api/routes/controller/projects.py +20 -4
- gns3server/api/routes/controller/templates.py +23 -2
- gns3server/api/routes/index.py +3 -3
- gns3server/api/server.py +38 -3
- gns3server/appliances/arista-veos.gns3a +20 -514
- gns3server/appliances/cisco-7200.gns3a +26 -0
- gns3server/appliances/cisco-asav.gns3a +14 -1
- gns3server/appliances/cisco-iou-l2.gns3a +16 -4
- gns3server/appliances/cisco-iou-l3.gns3a +16 -4
- gns3server/appliances/innovaphone-app.gns3a +50 -0
- gns3server/appliances/innovaphone-ipva.gns3a +78 -0
- gns3server/appliances/pfsense.gns3a +14 -0
- gns3server/compute/docker/__init__.py +1 -1
- gns3server/compute/iou/iou_vm.py +22 -12
- gns3server/controller/__init__.py +48 -38
- gns3server/controller/appliance_manager.py +2 -2
- gns3server/controller/compute.py +8 -3
- gns3server/controller/node.py +2 -6
- gns3server/controller/project.py +1 -2
- gns3server/crash_report.py +1 -1
- gns3server/db/repositories/images.py +22 -3
- gns3server/db/repositories/pools.py +1 -1
- gns3server/db/repositories/templates.py +11 -0
- gns3server/db/tasks.py +120 -79
- gns3server/disks/empty100G.qcow2 +0 -0
- gns3server/disks/empty200G.qcow2 +0 -0
- gns3server/disks/empty30G.qcow2 +0 -0
- gns3server/disks/empty8G.qcow2 +0 -0
- gns3server/schemas/config.py +1 -1
- gns3server/server.py +3 -3
- gns3server/services/authentication.py +9 -6
- gns3server/static/favicon.ico +0 -0
- gns3server/static/redoc.standalone.js +1782 -0
- gns3server/static/swagger-ui-bundle.js +2 -0
- gns3server/static/swagger-ui.css +3 -0
- gns3server/static/web-ui/index.html +1 -1
- gns3server/static/web-ui/main.62c99707e4709a56.js +1 -0
- gns3server/utils/asyncio/__init__.py +4 -12
- gns3server/utils/asyncio/pool.py +1 -4
- gns3server/utils/images.py +62 -39
- gns3server/version.py +2 -2
- gns3server/static/web-ui/main.ed82697b58d803e7.js +0 -1
- {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/LICENSE +0 -0
- {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/entry_points.txt +0 -0
- {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/top_level.txt +0 -0
|
@@ -33,11 +33,23 @@
|
|
|
33
33
|
"md5sum": "cbbbea66a253f1dac0fcf81274dc778d",
|
|
34
34
|
"filesize": 87756936
|
|
35
35
|
},
|
|
36
|
+
{
|
|
37
|
+
"filename": "c7200-adventerprisek9-mz.152-4.M11.image",
|
|
38
|
+
"version": "152-4.M11",
|
|
39
|
+
"md5sum": "9a2005ad09ce1ec6fe7cf9af1e9b099e",
|
|
40
|
+
"filesize": 128487680
|
|
41
|
+
},
|
|
36
42
|
{
|
|
37
43
|
"filename": "c7200-adventerprisek9-mz.124-24.T5.image",
|
|
38
44
|
"version": "124-24.T5",
|
|
39
45
|
"md5sum": "6b89d0d804e1f2bb5b8bda66b5692047",
|
|
40
46
|
"filesize": 102345240
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"filename": "c7200-a3jk9s-mz.124-25g.image",
|
|
50
|
+
"version": "124-25G",
|
|
51
|
+
"md5sum": "9c7cc9b3f3b3571411a7f62faaa2c036",
|
|
52
|
+
"filesize": 71528984
|
|
41
53
|
}
|
|
42
54
|
],
|
|
43
55
|
"versions": [
|
|
@@ -55,12 +67,26 @@
|
|
|
55
67
|
"image": "c7200-advipservicesk9-mz.152-4.S5.image"
|
|
56
68
|
}
|
|
57
69
|
},
|
|
70
|
+
{
|
|
71
|
+
"name": "152-4.M11",
|
|
72
|
+
"idlepc": "0x6062e5c0",
|
|
73
|
+
"images": {
|
|
74
|
+
"image": "c7200-adventerprisek9-mz.152-4.M11.image"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
58
77
|
{
|
|
59
78
|
"name": "124-24.T5",
|
|
60
79
|
"idlepc": "0x606df838",
|
|
61
80
|
"images": {
|
|
62
81
|
"image": "c7200-adventerprisek9-mz.124-24.T5.image"
|
|
63
82
|
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "124-25G",
|
|
86
|
+
"idlepc": "0x6066a998",
|
|
87
|
+
"images": {
|
|
88
|
+
"image": "c7200-a3jk9s-mz.124-25g.image"
|
|
89
|
+
}
|
|
64
90
|
}
|
|
65
91
|
]
|
|
66
92
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"status": "stable",
|
|
13
13
|
"maintainer": "GNS3 Team",
|
|
14
14
|
"maintainer_email": "developers@gns3.net",
|
|
15
|
-
"usage": "There is no default password and enable password. A default configuration is present. ASAv goes through a double-boot before becoming active. This is normal and expected.",
|
|
15
|
+
"usage": "There is no default password and enable password. A default configuration is present. ASAv goes through a double-boot before becoming active. This is normal and expected. Switch to the Telnet console type after the first boot.",
|
|
16
16
|
"symbol": ":/symbols/asa.svg",
|
|
17
17
|
"first_port_name": "Management0/0",
|
|
18
18
|
"port_name_format": "Gi0/{0}",
|
|
@@ -26,6 +26,13 @@
|
|
|
26
26
|
"kvm": "require"
|
|
27
27
|
},
|
|
28
28
|
"images": [
|
|
29
|
+
{
|
|
30
|
+
"filename": "asav9-22-1-1.qcow2",
|
|
31
|
+
"version": "9.22.1.1 CML",
|
|
32
|
+
"md5sum": "250a924cdc2370208eaac9d1dc8dc9e3",
|
|
33
|
+
"filesize": 379518976,
|
|
34
|
+
"download_url": "https://learningnetworkstore.cisco.com/cisco-modeling-labs-personal/cisco-modeling-labs-personal/CML-PERSONAL.html"
|
|
35
|
+
},
|
|
29
36
|
{
|
|
30
37
|
"filename": "asav9-18-2.qcow2",
|
|
31
38
|
"version": "9.18.2 CML",
|
|
@@ -126,6 +133,12 @@
|
|
|
126
133
|
}
|
|
127
134
|
],
|
|
128
135
|
"versions": [
|
|
136
|
+
{
|
|
137
|
+
"name": "9.22.1.1 CML",
|
|
138
|
+
"images": {
|
|
139
|
+
"hda_disk_image": "asav9-22-1-1.qcow2"
|
|
140
|
+
}
|
|
141
|
+
},
|
|
129
142
|
{
|
|
130
143
|
"name": "9.18.2 CML",
|
|
131
144
|
"images": {
|
|
@@ -13,13 +13,19 @@
|
|
|
13
13
|
"iou": {
|
|
14
14
|
"ethernet_adapters": 4,
|
|
15
15
|
"serial_adapters": 0,
|
|
16
|
-
"nvram":
|
|
17
|
-
"ram":
|
|
16
|
+
"nvram": 512,
|
|
17
|
+
"ram": 512,
|
|
18
18
|
"startup_config": "iou_l2_base_startup-config.txt"
|
|
19
19
|
},
|
|
20
20
|
"images": [
|
|
21
21
|
{
|
|
22
|
-
"filename": "x86_64_crb_linux_l2-adventerprisek9-ms.
|
|
22
|
+
"filename": "x86_64_crb_linux_l2-adventerprisek9-ms.iol",
|
|
23
|
+
"version": "17.15.1",
|
|
24
|
+
"md5sum": "6c587cdfd5056078e70b3f6c26800d66",
|
|
25
|
+
"filesize": 243251976
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"filename": "x86_64_crb_linux_l2-adventerprisek9-ms",
|
|
23
29
|
"version": "17.12.1",
|
|
24
30
|
"md5sum": "2b5055e4cef8fd257416d74a94adb626",
|
|
25
31
|
"filesize": 240355720
|
|
@@ -44,10 +50,16 @@
|
|
|
44
50
|
}
|
|
45
51
|
],
|
|
46
52
|
"versions": [
|
|
53
|
+
{
|
|
54
|
+
"name": "17.15.1",
|
|
55
|
+
"images": {
|
|
56
|
+
"image": "x86_64_crb_linux_l2-adventerprisek9-ms.iol"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
47
59
|
{
|
|
48
60
|
"name": "17.12.1",
|
|
49
61
|
"images": {
|
|
50
|
-
"image": "x86_64_crb_linux_l2-adventerprisek9-ms
|
|
62
|
+
"image": "x86_64_crb_linux_l2-adventerprisek9-ms"
|
|
51
63
|
}
|
|
52
64
|
},
|
|
53
65
|
{
|
|
@@ -13,13 +13,19 @@
|
|
|
13
13
|
"iou": {
|
|
14
14
|
"ethernet_adapters": 2,
|
|
15
15
|
"serial_adapters": 2,
|
|
16
|
-
"nvram":
|
|
17
|
-
"ram":
|
|
16
|
+
"nvram": 512,
|
|
17
|
+
"ram": 512,
|
|
18
18
|
"startup_config": "iou_l3_base_startup-config.txt"
|
|
19
19
|
},
|
|
20
20
|
"images": [
|
|
21
21
|
{
|
|
22
|
-
"filename": "x86_64_crb_linux-adventerprisek9-ms.
|
|
22
|
+
"filename": "x86_64_crb_linux-adventerprisek9-ms.iol",
|
|
23
|
+
"version": "17.15.1",
|
|
24
|
+
"md5sum": "5d584f6cfbeaadc87d55f613da1049ed",
|
|
25
|
+
"filesize": 292001512
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"filename": "x86_64_crb_linux-adventerprisek9-ms",
|
|
23
29
|
"version": "17.12.1",
|
|
24
30
|
"md5sum": "4a2fce8de21d1831fbceffd155e41ae7",
|
|
25
31
|
"filesize": 288947184
|
|
@@ -44,10 +50,16 @@
|
|
|
44
50
|
}
|
|
45
51
|
],
|
|
46
52
|
"versions": [
|
|
53
|
+
{
|
|
54
|
+
"name": "17.15.1",
|
|
55
|
+
"images": {
|
|
56
|
+
"image": "x86_64_crb_linux-adventerprisek9-ms.iol"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
47
59
|
{
|
|
48
60
|
"name": "17.12.1",
|
|
49
61
|
"images": {
|
|
50
|
-
"image": "x86_64_crb_linux-adventerprisek9-ms
|
|
62
|
+
"image": "x86_64_crb_linux-adventerprisek9-ms"
|
|
51
63
|
}
|
|
52
64
|
},
|
|
53
65
|
{
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"appliance_id": "92dbd0e9-144e-4c59-a4a8-97b6a1661818",
|
|
3
|
+
"name": "Innovaphone App-Platform",
|
|
4
|
+
"category": "guest",
|
|
5
|
+
"description": "In addition to telephony, apps for Video Telephony, Chat, Conferencing, Application Sharing and many other functions have become indispensable UCC tools in the area of business communication. Based on the myApps platform and its various components, innovaphone provides a collaborative work and communication platform for enhanced corporate communications \u2013 regardless of the location and the device being used. The innovaphone platform myApps consists of many independent components that work well individually, yet unfold their remarkable performance when combined.",
|
|
6
|
+
"vendor_name": "Innovaphone",
|
|
7
|
+
"vendor_url": "https://www.innovaphone.com",
|
|
8
|
+
"vendor_logo_url": "https://www.innovaphone.com/content/downloads/innovaphone-myapps-logo-short-without-background-screen.png",
|
|
9
|
+
"documentation_url": "https://wiki.innovaphone.com/index.php?title=Reference14r2:Concept_App_Platform",
|
|
10
|
+
"product_name": "App-Platform",
|
|
11
|
+
"product_url": "https://www.innovaphone.com/en/products/myapps/myapps-platform.html",
|
|
12
|
+
"registry_version": 4,
|
|
13
|
+
"status": "experimental",
|
|
14
|
+
"availability": "free-to-try",
|
|
15
|
+
"maintainer": "Thomas Marchsteiner",
|
|
16
|
+
"maintainer_email": "thomas.marchsteiner@acp.at",
|
|
17
|
+
"usage": "Default users console:root/iplinux , ssh:admin/ipapps , Webinterface:pwd \nAfter first boot wait for automatic reboot.\nA static ip can be set via the setip utility. \nLoading another keymap can be done via the loadkeys command. \nThe app-platform-disk1.vmdk file is contained within an ova file. \nIt can be extraced with the tar utility, 7Zip or any other tool which can handle tar files.",
|
|
18
|
+
"symbol": "innovaphone-ap-icon.jpg",
|
|
19
|
+
"first_port_name": "eth0",
|
|
20
|
+
"qemu": {
|
|
21
|
+
"adapter_type": "vmxnet3",
|
|
22
|
+
"adapters": 1,
|
|
23
|
+
"ram": 512,
|
|
24
|
+
"cpus": 1,
|
|
25
|
+
"hda_disk_interface": "scsi",
|
|
26
|
+
"arch": "x86_64",
|
|
27
|
+
"console_type": "vnc",
|
|
28
|
+
"boot_priority": "d",
|
|
29
|
+
"kvm": "allow",
|
|
30
|
+
"on_close": "power_off",
|
|
31
|
+
"process_priority": "normal"
|
|
32
|
+
},
|
|
33
|
+
"images": [
|
|
34
|
+
{
|
|
35
|
+
"filename": "app-platform-disk1_120010.vmdk",
|
|
36
|
+
"version": "12.0010",
|
|
37
|
+
"md5sum": "d5a5a77f682c2c988b0810935d79a787",
|
|
38
|
+
"filesize": 129474560,
|
|
39
|
+
"download_url": "https://store.innovaphone.com/"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"versions": [
|
|
43
|
+
{
|
|
44
|
+
"images": {
|
|
45
|
+
"hda_disk_image": "app-platform-disk1_120010.vmdk"
|
|
46
|
+
},
|
|
47
|
+
"name": "12.0010"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"appliance_id": "ddf8f7a4-60c0-4c9d-849c-ffc3c9d1d082",
|
|
3
|
+
"name": "Innovaphone IPVA",
|
|
4
|
+
"category": "guest",
|
|
5
|
+
"description": "The innovaphone PBX is a professional IP telephone system. The IPVA is a software-only solution. It appears as and performs as an innovaphone 'hard-box' excluding DSP-, ISDN-/AB-resources.",
|
|
6
|
+
"vendor_name": "Innovaphone",
|
|
7
|
+
"vendor_url": "https://www.innovaphone.com/",
|
|
8
|
+
"vendor_logo_url": "https://www.innovaphone.com/content/downloads/innovaphone-myapps-logo-short-without-background-screen.png",
|
|
9
|
+
"documentation_url": "https://wiki.innovaphone.com/index.php?title=Reference15r1:Concept_Innovaphone_Virtual_Appliance_(IPVA)",
|
|
10
|
+
"product_name": "IPVA",
|
|
11
|
+
"product_url": "https://www.innovaphone.com/en/products/innovaphone-pbx.html",
|
|
12
|
+
"registry_version": 4,
|
|
13
|
+
"status": "experimental",
|
|
14
|
+
"availability": "free-to-try",
|
|
15
|
+
"maintainer": "Thomas Marchsteiner",
|
|
16
|
+
"maintainer_email": "thomas.marchsteiner@acp.at",
|
|
17
|
+
"usage": "Default user admin/ipva \nDefault network configuration: DHCP client on eth0 with fallback to static address 192.168.0.1/24 after timeout. Static address 192.168.1.1/24 on eth1\n The ova in the zip file contains the disk images to run this appliance. Disableing the dhcp client and setting a static IP is possible with the following commands:\n> config change IP0 ETH0 /addr 192.168.0.1 /mask 255.255.255.0 \n> config change DHCP0 /mode off \n> config write \n> config activate \n> reset",
|
|
18
|
+
"symbol": "innovaphone-pbx-green.png",
|
|
19
|
+
"first_port_name": "eth0",
|
|
20
|
+
"port_name_format": "eth{port1}",
|
|
21
|
+
"qemu": {
|
|
22
|
+
"adapter_type": "vmxnet3",
|
|
23
|
+
"adapters": 2,
|
|
24
|
+
"ram": 256,
|
|
25
|
+
"cpus": 1,
|
|
26
|
+
"hda_disk_interface": "ide",
|
|
27
|
+
"hdb_disk_interface": "ide",
|
|
28
|
+
"hdc_disk_interface": "ide",
|
|
29
|
+
"hdd_disk_interface": "ide",
|
|
30
|
+
"arch": "x86_64",
|
|
31
|
+
"console_type": "vnc",
|
|
32
|
+
"boot_priority": "d",
|
|
33
|
+
"kvm": "allow",
|
|
34
|
+
"on_close": "power_off",
|
|
35
|
+
"process_priority": "normal"
|
|
36
|
+
},
|
|
37
|
+
"images": [
|
|
38
|
+
{
|
|
39
|
+
"filename": "ipva-qemu-disk1-14r2.vmdk",
|
|
40
|
+
"version": "14r2",
|
|
41
|
+
"md5sum": "aaa1c3885eee30ca6ffa3827619e8643",
|
|
42
|
+
"filesize": 6269952,
|
|
43
|
+
"download_url": "https://store.innovaphone.com/"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"filename": "ipva-qemu-disk2-14r2.vmdk",
|
|
47
|
+
"version": "14r2",
|
|
48
|
+
"md5sum": "008a8fc6b0b1e5f11a3e7fd6f22ba349",
|
|
49
|
+
"filesize": 72192,
|
|
50
|
+
"download_url": "https://store.innovaphone.com/"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"filename": "ipva-qemu-disk3-14r2.vmdk",
|
|
54
|
+
"version": "14r2",
|
|
55
|
+
"md5sum": "20516731c480e2112b3fb4a4d7f514f2",
|
|
56
|
+
"filesize": 68096,
|
|
57
|
+
"download_url": "https://store.innovaphone.com/"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"filename": "ipva-qemu-disk4-14r2.vmdk",
|
|
61
|
+
"version": "14r2",
|
|
62
|
+
"md5sum": "15d7d79ef8c28bd29b2eceac8405f964",
|
|
63
|
+
"filesize": 68096,
|
|
64
|
+
"download_url": "https://store.innovaphone.com/"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"versions": [
|
|
68
|
+
{
|
|
69
|
+
"images": {
|
|
70
|
+
"hda_disk_image": "ipva-qemu-disk1-14r2.vmdk",
|
|
71
|
+
"hdb_disk_image": "ipva-qemu-disk2-14r2.vmdk",
|
|
72
|
+
"hdc_disk_image": "ipva-qemu-disk3-14r2.vmdk",
|
|
73
|
+
"hdd_disk_image": "ipva-qemu-disk4-14r2.vmdk"
|
|
74
|
+
},
|
|
75
|
+
"name": "14r2"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
@@ -24,6 +24,13 @@
|
|
|
24
24
|
"process_priority": "normal"
|
|
25
25
|
},
|
|
26
26
|
"images": [
|
|
27
|
+
{
|
|
28
|
+
"filename": "pfSense-CE-2.7.2-RELEASE-amd64.iso",
|
|
29
|
+
"version": "2.7.2",
|
|
30
|
+
"md5sum": "50c3e723d68ec74d038041a34fa846f8",
|
|
31
|
+
"filesize": 874672128,
|
|
32
|
+
"download_url": "https://www.pfsense.org/download/mirror.php?section=downloads"
|
|
33
|
+
},
|
|
27
34
|
{
|
|
28
35
|
"filename": "pfSense-CE-2.7.0-RELEASE-amd64.iso",
|
|
29
36
|
"version": "2.7.0",
|
|
@@ -76,6 +83,13 @@
|
|
|
76
83
|
}
|
|
77
84
|
],
|
|
78
85
|
"versions": [
|
|
86
|
+
{
|
|
87
|
+
"name": "2.7.2",
|
|
88
|
+
"images": {
|
|
89
|
+
"hda_disk_image": "empty100G.qcow2",
|
|
90
|
+
"cdrom_image": "pfSense-CE-2.7.2-RELEASE-amd64.iso"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
79
93
|
{
|
|
80
94
|
"name": "2.7.0",
|
|
81
95
|
"images": {
|
|
@@ -115,7 +115,7 @@ class Docker(BaseManager):
|
|
|
115
115
|
dst_path = self.resources_path()
|
|
116
116
|
log.info(f"Installing Docker resources in '{dst_path}'")
|
|
117
117
|
from gns3server.controller import Controller
|
|
118
|
-
Controller.instance().install_resource_files(dst_path, "compute/docker/resources")
|
|
118
|
+
await Controller.instance().install_resource_files(dst_path, "compute/docker/resources")
|
|
119
119
|
await self.install_busybox(dst_path)
|
|
120
120
|
except OSError as e:
|
|
121
121
|
raise DockerError(f"Could not install Docker resources to {dst_path}: {e}")
|
gns3server/compute/iou/iou_vm.py
CHANGED
|
@@ -436,14 +436,16 @@ class IOUVM(BaseNode):
|
|
|
436
436
|
)
|
|
437
437
|
)
|
|
438
438
|
|
|
439
|
-
|
|
439
|
+
def _is_iou_license_check_enabled(self):
|
|
440
440
|
"""
|
|
441
|
-
|
|
441
|
+
Returns if IOU license check is enabled.
|
|
442
|
+
|
|
443
|
+
:return: boolean
|
|
442
444
|
"""
|
|
443
445
|
|
|
444
446
|
# license check is sent by the controller
|
|
445
447
|
if self.license_check is False:
|
|
446
|
-
return
|
|
448
|
+
return False
|
|
447
449
|
|
|
448
450
|
try:
|
|
449
451
|
# we allow license check to be disabled server wide
|
|
@@ -453,7 +455,14 @@ class IOUVM(BaseNode):
|
|
|
453
455
|
|
|
454
456
|
if server_wide_license_check is False:
|
|
455
457
|
log.warning("License check is explicitly disabled on this server")
|
|
456
|
-
return
|
|
458
|
+
return False
|
|
459
|
+
|
|
460
|
+
return True
|
|
461
|
+
|
|
462
|
+
async def _check_iou_license(self):
|
|
463
|
+
"""
|
|
464
|
+
Checks for a valid IOU key in the iourc file (paranoid mode).
|
|
465
|
+
"""
|
|
457
466
|
|
|
458
467
|
config = configparser.ConfigParser()
|
|
459
468
|
try:
|
|
@@ -559,15 +568,16 @@ class IOUVM(BaseNode):
|
|
|
559
568
|
except OSError as e:
|
|
560
569
|
raise IOUError(f"Could not rename nvram files: {e}")
|
|
561
570
|
|
|
562
|
-
iourc_path =
|
|
563
|
-
if
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
571
|
+
iourc_path = None
|
|
572
|
+
if self._is_iou_license_check_enabled():
|
|
573
|
+
iourc_path = self.iourc_path
|
|
574
|
+
if not iourc_path:
|
|
575
|
+
raise IOUError("Could not find an iourc file (IOU license), please configure an IOU license")
|
|
576
|
+
if not os.path.isfile(iourc_path):
|
|
577
|
+
raise IOUError(f"The iourc path '{iourc_path}' is not a regular file")
|
|
578
|
+
await self._check_iou_license()
|
|
567
579
|
|
|
568
|
-
await self._check_iou_licence()
|
|
569
580
|
await self._start_ubridge()
|
|
570
|
-
|
|
571
581
|
self._create_netmap_config()
|
|
572
582
|
if self.use_default_iou_values:
|
|
573
583
|
# make sure we have the default nvram amount to correctly push the configs
|
|
@@ -579,7 +589,7 @@ class IOUVM(BaseNode):
|
|
|
579
589
|
|
|
580
590
|
self._nvram_watcher = FileWatcher(self._nvram_file(), self._nvram_changed, delay=2)
|
|
581
591
|
|
|
582
|
-
# created
|
|
592
|
+
# created an environment variable pointing to the iourc file.
|
|
583
593
|
env = os.environ.copy()
|
|
584
594
|
if "IOURC" not in os.environ and iourc_path:
|
|
585
595
|
env["IOURC"] = iourc_path
|
|
@@ -28,10 +28,10 @@ try:
|
|
|
28
28
|
except ImportError:
|
|
29
29
|
from importlib import resources as importlib_resources
|
|
30
30
|
|
|
31
|
-
|
|
32
31
|
from ..config import Config
|
|
33
32
|
from ..utils import parse_version, md5sum
|
|
34
33
|
from ..utils.images import default_images_directory
|
|
34
|
+
from ..utils.asyncio import wait_run_in_executor
|
|
35
35
|
|
|
36
36
|
from .project import Project
|
|
37
37
|
from .appliance import Appliance
|
|
@@ -43,6 +43,7 @@ from .topology import load_topology
|
|
|
43
43
|
from .gns3vm import GNS3VM
|
|
44
44
|
from .gns3vm.gns3_vm_error import GNS3VMError
|
|
45
45
|
from .controller_error import ControllerError, ControllerNotFoundError
|
|
46
|
+
from ..db.tasks import update_disk_checksums
|
|
46
47
|
from ..version import __version__
|
|
47
48
|
|
|
48
49
|
import logging
|
|
@@ -64,7 +65,7 @@ class Controller:
|
|
|
64
65
|
self.gns3vm = GNS3VM(self)
|
|
65
66
|
self.symbols = Symbols()
|
|
66
67
|
self._appliance_manager = ApplianceManager()
|
|
67
|
-
self._iou_license_settings = {"iourc_content": "", "license_check":
|
|
68
|
+
self._iou_license_settings = {"iourc_content": "", "license_check": False}
|
|
68
69
|
self._vars_loaded = False
|
|
69
70
|
self._vars_file = Config.instance().controller_vars
|
|
70
71
|
log.info(f'Loading controller vars file "{self._vars_file}"')
|
|
@@ -72,8 +73,11 @@ class Controller:
|
|
|
72
73
|
async def start(self, computes=None):
|
|
73
74
|
|
|
74
75
|
log.info("Controller is starting")
|
|
75
|
-
self._install_base_configs()
|
|
76
|
-
self._install_builtin_disks()
|
|
76
|
+
await self._install_base_configs()
|
|
77
|
+
installed_disks = await self._install_builtin_disks()
|
|
78
|
+
if installed_disks:
|
|
79
|
+
await update_disk_checksums(installed_disks)
|
|
80
|
+
|
|
77
81
|
server_config = Config.instance().settings.Server
|
|
78
82
|
Config.instance().listen_for_config_changes(self._update_config)
|
|
79
83
|
name = server_config.name
|
|
@@ -86,7 +90,7 @@ class Controller:
|
|
|
86
90
|
if host == "0.0.0.0":
|
|
87
91
|
host = "127.0.0.1"
|
|
88
92
|
|
|
89
|
-
self._load_controller_vars()
|
|
93
|
+
await self._load_controller_vars()
|
|
90
94
|
|
|
91
95
|
if server_config.enable_ssl:
|
|
92
96
|
self._ssl_context = self._create_ssl_context(server_config)
|
|
@@ -134,7 +138,9 @@ class Controller:
|
|
|
134
138
|
log.warning(str(e))
|
|
135
139
|
|
|
136
140
|
await self.load_projects()
|
|
137
|
-
|
|
141
|
+
|
|
142
|
+
# start to auto open projects (if configured) 5 seconds after the controller has started
|
|
143
|
+
asyncio.get_event_loop().call_later(5, asyncio.create_task, self._project_auto_open())
|
|
138
144
|
|
|
139
145
|
def _create_ssl_context(self, server_config):
|
|
140
146
|
|
|
@@ -188,7 +194,7 @@ class Controller:
|
|
|
188
194
|
async def reload(self):
|
|
189
195
|
|
|
190
196
|
log.info("Controller is reloading")
|
|
191
|
-
self._load_controller_vars()
|
|
197
|
+
await self._load_controller_vars()
|
|
192
198
|
|
|
193
199
|
# remove all projects deleted from disk.
|
|
194
200
|
for project in self._projects.copy().values():
|
|
@@ -208,19 +214,15 @@ class Controller:
|
|
|
208
214
|
if self._vars_loaded:
|
|
209
215
|
controller_vars = {
|
|
210
216
|
"appliances_etag": self._appliance_manager.appliances_etag,
|
|
217
|
+
"iou_license_check": self._iou_license_settings["license_check"],
|
|
211
218
|
"version": __version__
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
if self._iou_license_settings["iourc_content"]:
|
|
215
222
|
|
|
216
|
-
iou_config = Config.instance().settings.IOU
|
|
217
223
|
server_config = Config.instance().settings.Server
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
iourc_path = iou_config.iourc_path
|
|
221
|
-
else:
|
|
222
|
-
os.makedirs(server_config.secrets_dir, exist_ok=True)
|
|
223
|
-
iourc_path = os.path.join(server_config.secrets_dir, "gns3_iourc_license")
|
|
224
|
+
os.makedirs(server_config.secrets_dir, exist_ok=True)
|
|
225
|
+
iourc_path = os.path.join(server_config.secrets_dir, "iou_license")
|
|
224
226
|
|
|
225
227
|
try:
|
|
226
228
|
with open(iourc_path, "w+") as f:
|
|
@@ -236,7 +238,7 @@ class Controller:
|
|
|
236
238
|
except OSError as e:
|
|
237
239
|
log.error(f"Cannot write controller vars file '{self._vars_file}': {e}")
|
|
238
240
|
|
|
239
|
-
def _load_controller_vars(self):
|
|
241
|
+
async def _load_controller_vars(self):
|
|
240
242
|
"""
|
|
241
243
|
Reload the controller vars from disk
|
|
242
244
|
"""
|
|
@@ -251,15 +253,11 @@ class Controller:
|
|
|
251
253
|
return []
|
|
252
254
|
|
|
253
255
|
# load the IOU license settings
|
|
254
|
-
iou_config = Config.instance().settings.IOU
|
|
255
256
|
server_config = Config.instance().settings.Server
|
|
256
257
|
|
|
257
|
-
if
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if not server_config.secrets_dir:
|
|
261
|
-
server_config.secrets_dir = os.path.dirname(Config.instance().server_config)
|
|
262
|
-
iourc_path = os.path.join(server_config.secrets_dir, "gns3_iourc_license")
|
|
258
|
+
if not server_config.secrets_dir:
|
|
259
|
+
server_config.secrets_dir = os.path.dirname(Config.instance().server_config)
|
|
260
|
+
iourc_path = os.path.join(server_config.secrets_dir, "iou_license")
|
|
263
261
|
|
|
264
262
|
if os.path.exists(iourc_path):
|
|
265
263
|
try:
|
|
@@ -268,7 +266,10 @@ class Controller:
|
|
|
268
266
|
log.info(f"iourc file '{iourc_path}' loaded")
|
|
269
267
|
except OSError as e:
|
|
270
268
|
log.error(f"Cannot read IOU license file '{iourc_path}': {e}")
|
|
271
|
-
|
|
269
|
+
|
|
270
|
+
# IOU license check is disabled by default
|
|
271
|
+
self._iou_license_settings["license_check"] = controller_vars.get("iou_license_check", False)
|
|
272
|
+
log.info("IOU license check is {} on the controller".format("enabled" if self._iou_license_settings["license_check"] else "disabled"))
|
|
272
273
|
|
|
273
274
|
# install the built-in appliances if needed
|
|
274
275
|
if Config.instance().settings.Server.install_builtin_appliances:
|
|
@@ -277,9 +278,9 @@ class Controller:
|
|
|
277
278
|
builtin_appliances_path = self._appliance_manager.builtin_appliances_path()
|
|
278
279
|
if not previous_version or \
|
|
279
280
|
parse_version(__version__.split("+")[0]) > parse_version(previous_version.split("+")[0]):
|
|
280
|
-
self._appliance_manager.install_builtin_appliances()
|
|
281
|
+
await self._appliance_manager.install_builtin_appliances()
|
|
281
282
|
elif not os.listdir(builtin_appliances_path):
|
|
282
|
-
self._appliance_manager.install_builtin_appliances()
|
|
283
|
+
await self._appliance_manager.install_builtin_appliances()
|
|
283
284
|
else:
|
|
284
285
|
log.info(f"Built-in appliances are installed in '{builtin_appliances_path}'")
|
|
285
286
|
|
|
@@ -310,18 +311,21 @@ class Controller:
|
|
|
310
311
|
|
|
311
312
|
|
|
312
313
|
@staticmethod
|
|
313
|
-
def install_resource_files(dst_path, resource_name, upgrade_resources=True):
|
|
314
|
+
async def install_resource_files(dst_path, resource_name, upgrade_resources=True):
|
|
314
315
|
"""
|
|
315
316
|
Install files from resources to user's file system
|
|
316
317
|
"""
|
|
317
318
|
|
|
318
|
-
|
|
319
|
+
installed_resources = []
|
|
320
|
+
async def should_copy(src, dst, upgrade_resources):
|
|
319
321
|
if not os.path.exists(dst):
|
|
320
322
|
return True
|
|
321
323
|
if upgrade_resources is False:
|
|
322
324
|
return False
|
|
323
325
|
# copy the resource if it is different
|
|
324
|
-
|
|
326
|
+
src_md5 = await wait_run_in_executor(md5sum, src)
|
|
327
|
+
dst_md5 = await wait_run_in_executor(md5sum, dst)
|
|
328
|
+
return src_md5 != dst_md5
|
|
325
329
|
|
|
326
330
|
if hasattr(sys, "frozen") and sys.platform.startswith("win"):
|
|
327
331
|
resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name))
|
|
@@ -331,14 +335,16 @@ class Controller:
|
|
|
331
335
|
else:
|
|
332
336
|
for entry in importlib_resources.files('gns3server').joinpath(resource_name).iterdir():
|
|
333
337
|
full_path = os.path.join(dst_path, entry.name)
|
|
334
|
-
if entry.is_file() and should_copy(str(entry), full_path, upgrade_resources):
|
|
338
|
+
if entry.is_file() and await should_copy(str(entry), full_path, upgrade_resources):
|
|
335
339
|
log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
|
|
336
|
-
shutil.copy(str(entry), os.path.join(
|
|
340
|
+
shutil.copy(str(entry), os.path.join(full_path))
|
|
341
|
+
installed_resources.append(full_path)
|
|
337
342
|
elif entry.is_dir():
|
|
338
343
|
os.makedirs(full_path, exist_ok=True)
|
|
339
|
-
Controller.install_resource_files(full_path, os.path.join(resource_name, entry.name))
|
|
344
|
+
await Controller.install_resource_files(full_path, os.path.join(resource_name, entry.name))
|
|
345
|
+
return installed_resources
|
|
340
346
|
|
|
341
|
-
def _install_base_configs(self):
|
|
347
|
+
async def _install_base_configs(self):
|
|
342
348
|
"""
|
|
343
349
|
At startup we copy base configs to the user location to allow
|
|
344
350
|
them to customize it
|
|
@@ -347,11 +353,12 @@ class Controller:
|
|
|
347
353
|
dst_path = self.configs_path()
|
|
348
354
|
log.info(f"Installing base configs in '{dst_path}'")
|
|
349
355
|
try:
|
|
350
|
-
|
|
356
|
+
# do not overwrite base configs because they may have been customized by the user
|
|
357
|
+
await Controller.install_resource_files(dst_path, "configs", upgrade_resources=False)
|
|
351
358
|
except OSError as e:
|
|
352
359
|
log.error(f"Could not install base config files to {dst_path}: {e}")
|
|
353
360
|
|
|
354
|
-
def _install_builtin_disks(self):
|
|
361
|
+
async def _install_builtin_disks(self):
|
|
355
362
|
"""
|
|
356
363
|
At startup we copy built-in Qemu disks to the user location to allow
|
|
357
364
|
them to use with appliances
|
|
@@ -360,7 +367,7 @@ class Controller:
|
|
|
360
367
|
dst_path = self.disks_path()
|
|
361
368
|
log.info(f"Installing built-in disks in '{dst_path}'")
|
|
362
369
|
try:
|
|
363
|
-
Controller.install_resource_files(dst_path, "disks"
|
|
370
|
+
return await Controller.install_resource_files(dst_path, "disks")
|
|
364
371
|
except OSError as e:
|
|
365
372
|
log.error(f"Could not install disk files to {dst_path}: {e}")
|
|
366
373
|
|
|
@@ -600,9 +607,12 @@ class Controller:
|
|
|
600
607
|
Auto open the project with auto open enable
|
|
601
608
|
"""
|
|
602
609
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
610
|
+
try:
|
|
611
|
+
for project in self._projects.values():
|
|
612
|
+
if project.auto_open:
|
|
613
|
+
await project.open()
|
|
614
|
+
except ControllerError as e:
|
|
615
|
+
log.error(f"Could not auto open projects: {e}")
|
|
606
616
|
|
|
607
617
|
def get_free_project_name(self, base_name):
|
|
608
618
|
"""
|
|
@@ -110,7 +110,7 @@ class ApplianceManager:
|
|
|
110
110
|
os.makedirs(appliances_dir, exist_ok=True)
|
|
111
111
|
return appliances_dir
|
|
112
112
|
|
|
113
|
-
def install_builtin_appliances(self):
|
|
113
|
+
async def install_builtin_appliances(self):
|
|
114
114
|
"""
|
|
115
115
|
At startup we copy the built-in appliances files.
|
|
116
116
|
"""
|
|
@@ -119,7 +119,7 @@ class ApplianceManager:
|
|
|
119
119
|
log.info(f"Installing built-in appliances in '{dst_path}'")
|
|
120
120
|
from . import Controller
|
|
121
121
|
try:
|
|
122
|
-
Controller.instance().install_resource_files(dst_path, "appliances")
|
|
122
|
+
await Controller.instance().install_resource_files(dst_path, "appliances")
|
|
123
123
|
except OSError as e:
|
|
124
124
|
log.error(f"Could not install built-in appliance files to {dst_path}: {e}")
|
|
125
125
|
|