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.

Files changed (49) hide show
  1. {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/METADATA +19 -18
  2. {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/RECORD +48 -42
  3. {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/WHEEL +1 -1
  4. gns3server/api/routes/controller/images.py +58 -21
  5. gns3server/api/routes/controller/projects.py +20 -4
  6. gns3server/api/routes/controller/templates.py +23 -2
  7. gns3server/api/routes/index.py +3 -3
  8. gns3server/api/server.py +38 -3
  9. gns3server/appliances/arista-veos.gns3a +20 -514
  10. gns3server/appliances/cisco-7200.gns3a +26 -0
  11. gns3server/appliances/cisco-asav.gns3a +14 -1
  12. gns3server/appliances/cisco-iou-l2.gns3a +16 -4
  13. gns3server/appliances/cisco-iou-l3.gns3a +16 -4
  14. gns3server/appliances/innovaphone-app.gns3a +50 -0
  15. gns3server/appliances/innovaphone-ipva.gns3a +78 -0
  16. gns3server/appliances/pfsense.gns3a +14 -0
  17. gns3server/compute/docker/__init__.py +1 -1
  18. gns3server/compute/iou/iou_vm.py +22 -12
  19. gns3server/controller/__init__.py +48 -38
  20. gns3server/controller/appliance_manager.py +2 -2
  21. gns3server/controller/compute.py +8 -3
  22. gns3server/controller/node.py +2 -6
  23. gns3server/controller/project.py +1 -2
  24. gns3server/crash_report.py +1 -1
  25. gns3server/db/repositories/images.py +22 -3
  26. gns3server/db/repositories/pools.py +1 -1
  27. gns3server/db/repositories/templates.py +11 -0
  28. gns3server/db/tasks.py +120 -79
  29. gns3server/disks/empty100G.qcow2 +0 -0
  30. gns3server/disks/empty200G.qcow2 +0 -0
  31. gns3server/disks/empty30G.qcow2 +0 -0
  32. gns3server/disks/empty8G.qcow2 +0 -0
  33. gns3server/schemas/config.py +1 -1
  34. gns3server/server.py +3 -3
  35. gns3server/services/authentication.py +9 -6
  36. gns3server/static/favicon.ico +0 -0
  37. gns3server/static/redoc.standalone.js +1782 -0
  38. gns3server/static/swagger-ui-bundle.js +2 -0
  39. gns3server/static/swagger-ui.css +3 -0
  40. gns3server/static/web-ui/index.html +1 -1
  41. gns3server/static/web-ui/main.62c99707e4709a56.js +1 -0
  42. gns3server/utils/asyncio/__init__.py +4 -12
  43. gns3server/utils/asyncio/pool.py +1 -4
  44. gns3server/utils/images.py +62 -39
  45. gns3server/version.py +2 -2
  46. gns3server/static/web-ui/main.ed82697b58d803e7.js +0 -1
  47. {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/LICENSE +0 -0
  48. {gns3_server-3.0.0rc2.dist-info → gns3_server-3.0.2.dist-info}/entry_points.txt +0 -0
  49. {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": 128,
17
- "ram": 256,
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.bin",
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.bin"
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": 128,
17
- "ram": 256,
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.bin",
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.bin"
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}")
@@ -436,14 +436,16 @@ class IOUVM(BaseNode):
436
436
  )
437
437
  )
438
438
 
439
- async def _check_iou_licence(self):
439
+ def _is_iou_license_check_enabled(self):
440
440
  """
441
- Checks for a valid IOU key in the iourc file (paranoid mode).
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 = self.iourc_path
563
- if not iourc_path:
564
- raise IOUError("Could not find an iourc file (IOU license), please configure an IOU license")
565
- if not os.path.isfile(iourc_path):
566
- raise IOUError(f"The iourc path '{iourc_path}' is not a regular file")
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 a environment variable pointing to the iourc file.
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": True}
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
- await self._project_auto_open()
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
- if iou_config.iourc_path:
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 iou_config.iourc_path:
258
- iourc_path = iou_config.iourc_path
259
- else:
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
- self._iou_license_settings["license_check"] = iou_config.license_check
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
- def should_copy(src, dst, upgrade_resources):
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
- return md5sum(src) != md5sum(dst)
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(dst_path, entry.name))
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
- Controller.install_resource_files(dst_path, "configs", upgrade_resources=False)
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", upgrade_resources=False)
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
- for project in self._projects.values():
604
- if project.auto_open:
605
- await project.open()
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