gns3-server 3.0.0b2__py3-none-any.whl → 3.0.0rc1__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.0b2.dist-info → gns3_server-3.0.0rc1.dist-info}/METADATA +32 -32
- {gns3_server-3.0.0b2.dist-info → gns3_server-3.0.0rc1.dist-info}/RECORD +79 -62
- {gns3_server-3.0.0b2.dist-info → gns3_server-3.0.0rc1.dist-info}/WHEEL +1 -1
- gns3server/api/routes/compute/dynamips_nodes.py +2 -2
- gns3server/api/routes/controller/images.py +4 -2
- gns3server/api/routes/controller/projects.py +2 -0
- gns3server/appliances/arista-veos.gns3a +178 -2
- gns3server/appliances/aruba-arubaoscx.gns3a +39 -0
- gns3server/appliances/asterfusion-vAsterNOS.gns3a +2 -2
- gns3server/appliances/cisco-c8000v.gns3a +14 -1
- gns3server/appliances/cisco-iou-l2.gns3a +2 -2
- gns3server/appliances/cisco-iou-l3.gns3a +2 -2
- gns3server/appliances/debian.gns3a +28 -0
- gns3server/appliances/fortiadc.gns3a +46 -4
- gns3server/appliances/fortianalyzer.gns3a +42 -0
- gns3server/appliances/fortiauthenticator.gns3a +58 -2
- gns3server/appliances/fortigate.gns3a +42 -0
- gns3server/appliances/fortimanager.gns3a +42 -0
- gns3server/appliances/fortiweb.gns3a +56 -0
- gns3server/appliances/juniper-junos-space.gns3a +3 -2
- gns3server/appliances/juniper-vmx-legacy.gns3a +1 -1
- gns3server/appliances/juniper-vmx-vcp.gns3a +1 -1
- gns3server/appliances/juniper-vmx-vfp.gns3a +2 -1
- gns3server/appliances/juniper-vqfx-pfe.gns3a +1 -1
- gns3server/appliances/juniper-vqfx-re.gns3a +2 -1
- gns3server/appliances/juniper-vrr.gns3a +1 -1
- gns3server/appliances/juniper-vsrx.gns3a +2 -1
- gns3server/appliances/openvswitch-management.gns3a +1 -1
- gns3server/appliances/opnsense.gns3a +2 -2
- gns3server/appliances/pan-vm-fw.gns3a +26 -0
- gns3server/appliances/security-onion.gns3a +27 -3
- gns3server/appliances/ubuntu-docker.gns3a +1 -1
- gns3server/compute/docker/__init__.py +9 -3
- gns3server/compute/dynamips/__init__.py +4 -0
- gns3server/compute/iou/iou_vm.py +2 -1
- gns3server/compute/qemu/qemu_vm.py +26 -10
- gns3server/config_samples/gns3_server.conf +13 -3
- gns3server/configs/iou_l2_base_startup-config.txt +1 -1
- gns3server/configs/iou_l3_base_startup-config.txt +1 -1
- gns3server/controller/__init__.py +12 -7
- gns3server/controller/appliance_manager.py +7 -2
- gns3server/controller/export_project.py +9 -9
- gns3server/controller/import_project.py +3 -3
- gns3server/controller/project.py +8 -4
- gns3server/controller/snapshot.py +5 -10
- gns3server/controller/symbol_themes.py +7 -0
- gns3server/controller/topology.py +30 -1
- gns3server/controller/udp_link.py +1 -1
- gns3server/crash_report.py +1 -1
- gns3server/schemas/config.py +3 -0
- gns3server/schemas/controller/templates/__init__.py +0 -1
- gns3server/services/authentication.py +2 -2
- gns3server/services/templates.py +1 -1
- gns3server/static/web-ui/index.html +3 -3
- gns3server/static/web-ui/main.4185a8e61824af0d.js +1 -0
- gns3server/symbols/affinity/circle/blue/nat.svg +60 -0
- gns3server/symbols/affinity/circle/blue/nat2.svg +55 -0
- gns3server/symbols/affinity/circle/gray/nat.svg +60 -0
- gns3server/symbols/affinity/circle/gray/nat2.svg +55 -0
- gns3server/symbols/affinity/circle/green/nat.svg +60 -0
- gns3server/symbols/affinity/circle/green/nat2.svg +55 -0
- gns3server/symbols/affinity/circle/red/nat.svg +60 -0
- gns3server/symbols/affinity/circle/red/nat2.svg +55 -0
- gns3server/symbols/affinity/square/blue/nat.svg +58 -0
- gns3server/symbols/affinity/square/blue/nat2.svg +74 -0
- gns3server/symbols/affinity/square/gray/nat.svg +58 -0
- gns3server/symbols/affinity/square/gray/nat2.svg +74 -0
- gns3server/symbols/affinity/square/green/nat.svg +58 -0
- gns3server/symbols/affinity/square/green/nat2.svg +74 -0
- gns3server/symbols/affinity/square/red/nat.svg +58 -0
- gns3server/symbols/affinity/square/red/nat2.svg +74 -0
- gns3server/symbols/classic/nat.svg +207 -0
- gns3server/utils/__init__.py +20 -0
- gns3server/utils/hostname.py +53 -0
- gns3server/utils/images.py +3 -5
- gns3server/version.py +1 -1
- gns3server/static/web-ui/main.8e8e3d55e78d84fc.js +0 -1
- {gns3_server-3.0.0b2.dist-info → gns3_server-3.0.0rc1.dist-info}/LICENSE +0 -0
- {gns3_server-3.0.0b2.dist-info → gns3_server-3.0.0rc1.dist-info}/entry_points.txt +0 -0
- {gns3_server-3.0.0b2.dist-info → gns3_server-3.0.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -34,7 +34,7 @@ enable_ssl = False
|
|
|
34
34
|
certfile = /home/gns3/.config/GNS3/ssl/server.cert
|
|
35
35
|
certkey = /home/gns3/.config/GNS3/ssl/server.key
|
|
36
36
|
|
|
37
|
-
; Path where
|
|
37
|
+
; Path where binary images are stored
|
|
38
38
|
images_path = /home/gns3/GNS3/images
|
|
39
39
|
|
|
40
40
|
; Additional paths to look for images
|
|
@@ -43,15 +43,20 @@ additional_images_paths = /opt/images;/mnt/disk1/images
|
|
|
43
43
|
; Path where user projects are stored
|
|
44
44
|
projects_path = /home/gns3/GNS3/projects
|
|
45
45
|
|
|
46
|
-
; Path where user appliances are stored
|
|
46
|
+
; Path where custom user appliances are stored
|
|
47
47
|
appliances_path = /home/gns3/GNS3/appliances
|
|
48
48
|
|
|
49
|
-
; Path where custom
|
|
49
|
+
; Path where custom user symbols are stored
|
|
50
50
|
symbols_path = /home/gns3/GNS3/symbols
|
|
51
51
|
|
|
52
52
|
; Path where custom configs are stored
|
|
53
53
|
configs_path = /home/gns3/GNS3/configs
|
|
54
54
|
|
|
55
|
+
; Path where files like built-in appliances and Docker resources are stored
|
|
56
|
+
; The default path is the local user data directory
|
|
57
|
+
; (Linux: "~/.local/share/GNS3", macOS: "~/Library/Application Support/GNS3", Windows: "%APPDATA%\GNS3")
|
|
58
|
+
; resources_path = /home/gns3/GNS3/resources
|
|
59
|
+
|
|
55
60
|
; Default symbol theme
|
|
56
61
|
; Currently available themes are "Classic", Affinity-square-blue", "Affinity-square-red"
|
|
57
62
|
; "Affinity-square-gray", "Affinity-circle-blue", "Affinity-circle-red" and "Affinity-circle-gray"
|
|
@@ -102,6 +107,9 @@ default_nat_interface = vmnet10
|
|
|
102
107
|
; Enable the built-in templates
|
|
103
108
|
enable_builtin_templates = True
|
|
104
109
|
|
|
110
|
+
; Install built-in appliances
|
|
111
|
+
install_builtin_appliances = True
|
|
112
|
+
|
|
105
113
|
; check if hardware virtualization is used by other emulators (KVM, VMware or VirtualBox)
|
|
106
114
|
hardware_virtualization_check = True
|
|
107
115
|
|
|
@@ -148,3 +156,5 @@ monitor_host = 127.0.0.1
|
|
|
148
156
|
enable_hardware_acceleration = True
|
|
149
157
|
; Require hardware acceleration in order to start VMs
|
|
150
158
|
require_hardware_acceleration = False
|
|
159
|
+
; Allow unsafe additional command line options
|
|
160
|
+
allow_unsafe_options = False
|
|
@@ -270,13 +270,18 @@ class Controller:
|
|
|
270
270
|
log.error(f"Cannot read IOU license file '{iourc_path}': {e}")
|
|
271
271
|
self._iou_license_settings["license_check"] = iou_config.license_check
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
self._appliance_manager.
|
|
278
|
-
|
|
279
|
-
|
|
273
|
+
# install the built-in appliances if needed
|
|
274
|
+
if Config.instance().settings.Server.install_builtin_appliances:
|
|
275
|
+
previous_version = controller_vars.get("version")
|
|
276
|
+
log.info("Comparing controller version {} with config version {}".format(__version__, previous_version))
|
|
277
|
+
builtin_appliances_path = self._appliance_manager.builtin_appliances_path()
|
|
278
|
+
if not previous_version or \
|
|
279
|
+
parse_version(__version__.split("+")[0]) > parse_version(previous_version.split("+")[0]):
|
|
280
|
+
self._appliance_manager.install_builtin_appliances()
|
|
281
|
+
elif not os.listdir(builtin_appliances_path):
|
|
282
|
+
self._appliance_manager.install_builtin_appliances()
|
|
283
|
+
else:
|
|
284
|
+
log.info(f"Built-in appliances are installed in '{builtin_appliances_path}'")
|
|
280
285
|
|
|
281
286
|
self._appliance_manager.appliances_etag = controller_vars.get("appliances_etag")
|
|
282
287
|
self._appliance_manager.load_appliances()
|
|
@@ -100,8 +100,13 @@ class ApplianceManager:
|
|
|
100
100
|
Get the built-in appliance storage directory
|
|
101
101
|
"""
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
resources_path = Config.instance().settings.Server.resources_path
|
|
104
|
+
if not resources_path:
|
|
105
|
+
appname = vendor = "GNS3"
|
|
106
|
+
resources_path = platformdirs.user_data_dir(appname, vendor, roaming=True)
|
|
107
|
+
else:
|
|
108
|
+
resources_path = os.path.expanduser(resources_path)
|
|
109
|
+
appliances_dir = os.path.join(resources_path, "appliances")
|
|
105
110
|
if delete_first:
|
|
106
111
|
shutil.rmtree(appliances_dir, ignore_errors=True)
|
|
107
112
|
os.makedirs(appliances_dir, exist_ok=True)
|
|
@@ -39,7 +39,7 @@ async def export_project(
|
|
|
39
39
|
temporary_dir,
|
|
40
40
|
include_images=False,
|
|
41
41
|
include_snapshots=False,
|
|
42
|
-
|
|
42
|
+
keep_compute_ids=False,
|
|
43
43
|
allow_all_nodes=False,
|
|
44
44
|
reset_mac_addresses=False,
|
|
45
45
|
):
|
|
@@ -54,9 +54,9 @@ async def export_project(
|
|
|
54
54
|
:param temporary_dir: A temporary dir where to store intermediate data
|
|
55
55
|
:param include_images: save OS images to the zip file
|
|
56
56
|
:param include_snapshots: save snapshots to the zip file
|
|
57
|
-
:param
|
|
58
|
-
:param allow_all_nodes: Allow all nodes type to be
|
|
59
|
-
:param reset_mac_addresses: Reset MAC addresses for
|
|
57
|
+
:param keep_compute_ids: If false replace all compute IDs by local (standard behavior for .gns3project to make it portable)
|
|
58
|
+
:param allow_all_nodes: Allow all nodes type to be included in the zip even if not portable
|
|
59
|
+
:param reset_mac_addresses: Reset MAC addresses for each node.
|
|
60
60
|
"""
|
|
61
61
|
|
|
62
62
|
# To avoid issue with data not saved we disallow the export of a running project
|
|
@@ -77,7 +77,7 @@ async def export_project(
|
|
|
77
77
|
os.path.join(project._path, file),
|
|
78
78
|
zstream,
|
|
79
79
|
include_images,
|
|
80
|
-
|
|
80
|
+
keep_compute_ids,
|
|
81
81
|
allow_all_nodes,
|
|
82
82
|
temporary_dir,
|
|
83
83
|
reset_mac_addresses,
|
|
@@ -193,7 +193,7 @@ def _is_exportable(path, include_snapshots=False):
|
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
async def _patch_project_file(
|
|
196
|
-
project, path, zstream, include_images,
|
|
196
|
+
project, path, zstream, include_images, keep_compute_ids, allow_all_nodes, temporary_dir, reset_mac_addresses
|
|
197
197
|
):
|
|
198
198
|
"""
|
|
199
199
|
Patch a project file (.gns3) to export a project.
|
|
@@ -225,7 +225,7 @@ async def _patch_project_file(
|
|
|
225
225
|
if not allow_all_nodes and node["node_type"] in ["virtualbox", "vmware"]:
|
|
226
226
|
raise ControllerError("Projects with a {} node cannot be exported".format(node["node_type"]))
|
|
227
227
|
|
|
228
|
-
if not
|
|
228
|
+
if not keep_compute_ids:
|
|
229
229
|
node["compute_id"] = "local" # To make project portable all node by default run on local
|
|
230
230
|
|
|
231
231
|
if "properties" in node and node["node_type"] != "docker":
|
|
@@ -243,13 +243,13 @@ async def _patch_project_file(
|
|
|
243
243
|
if value is None or value.strip() == "":
|
|
244
244
|
continue
|
|
245
245
|
|
|
246
|
-
if not
|
|
246
|
+
if not keep_compute_ids: # If we keep the original compute we can keep the image path
|
|
247
247
|
node["properties"][prop] = os.path.basename(value)
|
|
248
248
|
|
|
249
249
|
if include_images is True:
|
|
250
250
|
images.append({"compute_id": compute_id, "image": value, "image_type": node["node_type"]})
|
|
251
251
|
|
|
252
|
-
if not
|
|
252
|
+
if not keep_compute_ids:
|
|
253
253
|
topology["topology"][
|
|
254
254
|
"computes"
|
|
255
255
|
] = [] # Strip compute information because could contain secret info like password
|
|
@@ -40,7 +40,7 @@ Handle the import of project from a .gns3project
|
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
async def import_project(controller, project_id, stream, location=None, name=None,
|
|
43
|
+
async def import_project(controller, project_id, stream, location=None, name=None, keep_compute_ids=False,
|
|
44
44
|
auto_start=False, auto_open=False, auto_close=True):
|
|
45
45
|
"""
|
|
46
46
|
Import a project contain in a zip file
|
|
@@ -52,7 +52,7 @@ async def import_project(controller, project_id, stream, location=None, name=Non
|
|
|
52
52
|
:param stream: A io.BytesIO of the zipfile
|
|
53
53
|
:param location: Directory for the project if None put in the default directory
|
|
54
54
|
:param name: Wanted project name, generate one from the .gns3 if None
|
|
55
|
-
:param
|
|
55
|
+
:param keep_compute_ids: keep compute IDs unchanged
|
|
56
56
|
|
|
57
57
|
:returns: Project
|
|
58
58
|
"""
|
|
@@ -126,7 +126,7 @@ async def import_project(controller, project_id, stream, location=None, name=Non
|
|
|
126
126
|
drawing["drawing_id"] = str(uuid.uuid4())
|
|
127
127
|
|
|
128
128
|
# Modify the compute id of the node depending of compute capacity
|
|
129
|
-
if not
|
|
129
|
+
if not keep_compute_ids:
|
|
130
130
|
# For some VM type we move them to the GNS3 VM if possible
|
|
131
131
|
# unless it's a linux host without GNS3 VM
|
|
132
132
|
if not sys.platform.startswith("linux") or controller.has_compute("vm"):
|
gns3server/controller/project.py
CHANGED
|
@@ -210,7 +210,11 @@ class Project:
|
|
|
210
210
|
if os.path.exists(snapshot_dir):
|
|
211
211
|
for snap in os.listdir(snapshot_dir):
|
|
212
212
|
if snap.endswith(".gns3project"):
|
|
213
|
-
|
|
213
|
+
try:
|
|
214
|
+
snapshot = Snapshot(self, filename=snap)
|
|
215
|
+
except ValueError:
|
|
216
|
+
log.error("Invalid snapshot file: {}".format(snap))
|
|
217
|
+
continue
|
|
214
218
|
self._snapshots[snapshot.id] = snapshot
|
|
215
219
|
|
|
216
220
|
# Create the project on demand on the compute node
|
|
@@ -491,7 +495,7 @@ class Project:
|
|
|
491
495
|
|
|
492
496
|
if base_name is None:
|
|
493
497
|
return None
|
|
494
|
-
base_name = re.sub(r"[ ]", "", base_name)
|
|
498
|
+
base_name = re.sub(r"[ ]", "", base_name) # remove spaces in node name
|
|
495
499
|
if base_name in self._allocated_node_names:
|
|
496
500
|
base_name = re.sub(r"[0-9]+$", "{0}", base_name)
|
|
497
501
|
|
|
@@ -1087,7 +1091,7 @@ class Project:
|
|
|
1087
1091
|
zstream,
|
|
1088
1092
|
self,
|
|
1089
1093
|
tmpdir,
|
|
1090
|
-
|
|
1094
|
+
keep_compute_ids=True,
|
|
1091
1095
|
allow_all_nodes=True,
|
|
1092
1096
|
reset_mac_addresses=reset_mac_addresses,
|
|
1093
1097
|
)
|
|
@@ -1106,7 +1110,7 @@ class Project:
|
|
|
1106
1110
|
str(uuid.uuid4()),
|
|
1107
1111
|
f,
|
|
1108
1112
|
name=name,
|
|
1109
|
-
|
|
1113
|
+
keep_compute_ids=True
|
|
1110
1114
|
)
|
|
1111
1115
|
|
|
1112
1116
|
log.info(f"Project '{project.name}' duplicated in {time.time() - begin:.4f} seconds")
|
|
@@ -51,22 +51,17 @@ class Snapshot:
|
|
|
51
51
|
self._project = project
|
|
52
52
|
if name:
|
|
53
53
|
self._name = name
|
|
54
|
-
self._created_at = datetime.now().timestamp()
|
|
54
|
+
self._created_at = datetime.now(timezone.utc).timestamp()
|
|
55
55
|
filename = (
|
|
56
56
|
self._name
|
|
57
57
|
+ "_"
|
|
58
|
-
+ datetime.
|
|
58
|
+
+ datetime.fromtimestamp(self._created_at, tz=timezone.utc).replace(tzinfo=None).strftime("%d%m%y_%H%M%S")
|
|
59
59
|
+ ".gns3project"
|
|
60
60
|
)
|
|
61
61
|
else:
|
|
62
|
-
self._name = filename.
|
|
62
|
+
self._name = filename.rsplit("_", 2)[0]
|
|
63
63
|
datestring = filename.replace(self._name + "_", "").split(".")[0]
|
|
64
|
-
|
|
65
|
-
self._created_at = (
|
|
66
|
-
datetime.strptime(datestring, "%d%m%y_%H%M%S").replace(tzinfo=timezone.utc).timestamp()
|
|
67
|
-
)
|
|
68
|
-
except ValueError:
|
|
69
|
-
self._created_at = datetime.utcnow().timestamp()
|
|
64
|
+
self._created_at = (datetime.strptime(datestring, "%d%m%y_%H%M%S").replace(tzinfo=timezone.utc).timestamp())
|
|
70
65
|
self._path = os.path.join(project.path, "snapshots", filename)
|
|
71
66
|
|
|
72
67
|
@property
|
|
@@ -104,7 +99,7 @@ class Snapshot:
|
|
|
104
99
|
with tempfile.TemporaryDirectory(dir=snapshot_directory) as tmpdir:
|
|
105
100
|
# Do not compress the snapshots
|
|
106
101
|
with aiozipstream.ZipFile(compression=zipfile.ZIP_STORED) as zstream:
|
|
107
|
-
await export_project(zstream, self._project, tmpdir,
|
|
102
|
+
await export_project(zstream, self._project, tmpdir, keep_compute_ids=True, allow_all_nodes=True)
|
|
108
103
|
async with aiofiles.open(self.path, "wb") as f:
|
|
109
104
|
async for chunk in zstream:
|
|
110
105
|
await f.write(chunk)
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
CLASSIC_SYMBOL_THEME = {
|
|
20
20
|
"cloud": ":/symbols/classic/cloud.svg",
|
|
21
|
+
"nat": ":/symbols/classic/nat.svg",
|
|
21
22
|
"ethernet_switch": ":/symbols/classic/ethernet_switch.svg",
|
|
22
23
|
"hub": ":/symbols/classic/hub.svg",
|
|
23
24
|
"frame_relay_switch": ":/symbols/classic/frame_relay_switch.svg",
|
|
@@ -35,6 +36,7 @@ CLASSIC_SYMBOL_THEME = {
|
|
|
35
36
|
|
|
36
37
|
AFFINITY_SQUARE_BLUE_SYMBOL_THEME = {
|
|
37
38
|
"cloud": ":/symbols/affinity/square/blue/cloud.svg",
|
|
39
|
+
"nat": ":/symbols/affinity/square/blue/nat.svg",
|
|
38
40
|
"ethernet_switch": ":/symbols/affinity/square/blue/switch.svg",
|
|
39
41
|
"hub": ":/symbols/affinity/square/blue/hub.svg",
|
|
40
42
|
"frame_relay_switch": ":/symbols/affinity/square/blue/isdn.svg",
|
|
@@ -52,6 +54,7 @@ AFFINITY_SQUARE_BLUE_SYMBOL_THEME = {
|
|
|
52
54
|
|
|
53
55
|
AFFINITY_SQUARE_RED_SYMBOL_THEME = {
|
|
54
56
|
"cloud": ":/symbols/affinity/square/red/cloud.svg",
|
|
57
|
+
"nat": ":/symbols/affinity/square/red/nat.svg",
|
|
55
58
|
"ethernet_switch": ":/symbols/affinity/square/red/switch.svg",
|
|
56
59
|
"hub": ":/symbols/affinity/square/red/hub.svg",
|
|
57
60
|
"frame_relay_switch": ":/symbols/affinity/square/red/isdn.svg",
|
|
@@ -69,6 +72,7 @@ AFFINITY_SQUARE_RED_SYMBOL_THEME = {
|
|
|
69
72
|
|
|
70
73
|
AFFINITY_SQUARE_GRAY_SYMBOL_THEME = {
|
|
71
74
|
"cloud": ":/symbols/affinity/square/gray/cloud.svg",
|
|
75
|
+
"nat": ":/symbols/affinity/square/gray/nat.svg",
|
|
72
76
|
"ethernet_switch": ":/symbols/affinity/square/gray/switch.svg",
|
|
73
77
|
"hub": ":/symbols/affinity/square/gray/hub.svg",
|
|
74
78
|
"frame_relay_switch": ":/symbols/affinity/square/gray/isdn.svg",
|
|
@@ -86,6 +90,7 @@ AFFINITY_SQUARE_GRAY_SYMBOL_THEME = {
|
|
|
86
90
|
|
|
87
91
|
AFFINITY_CIRCLE_BLUE_SYMBOL_THEME = {
|
|
88
92
|
"cloud": ":/symbols/affinity/circle/blue/cloud.svg",
|
|
93
|
+
"nat": ":/symbols/affinity/circle/blue/nat.svg",
|
|
89
94
|
"ethernet_switch": ":/symbols/affinity/circle/blue/switch.svg",
|
|
90
95
|
"hub": ":/symbols/affinity/circle/blue/hub.svg",
|
|
91
96
|
"frame_relay_switch": ":/symbols/affinity/circle/blue/isdn.svg",
|
|
@@ -103,6 +108,7 @@ AFFINITY_CIRCLE_BLUE_SYMBOL_THEME = {
|
|
|
103
108
|
|
|
104
109
|
AFFINITY_CIRCLE_RED_SYMBOL_THEME = {
|
|
105
110
|
"cloud": ":/symbols/affinity/circle/red/cloud.svg",
|
|
111
|
+
"nat": ":/symbols/affinity/circle/red/nat.svg",
|
|
106
112
|
"ethernet_switch": ":/symbols/affinity/circle/red/switch.svg",
|
|
107
113
|
"hub": ":/symbols/affinity/circle/red/hub.svg",
|
|
108
114
|
"frame_relay_switch": ":/symbols/affinity/circle/red/isdn.svg",
|
|
@@ -120,6 +126,7 @@ AFFINITY_CIRCLE_RED_SYMBOL_THEME = {
|
|
|
120
126
|
|
|
121
127
|
AFFINITY_CIRCLE_GRAY_SYMBOL_THEME = {
|
|
122
128
|
"cloud": ":/symbols/affinity/circle/gray/cloud.svg",
|
|
129
|
+
"nat": ":/symbols/affinity/circle/gray/nat.svg",
|
|
123
130
|
"ethernet_switch": ":/symbols/affinity/circle/gray/switch.svg",
|
|
124
131
|
"hub": ":/symbols/affinity/circle/gray/hub.svg",
|
|
125
132
|
"frame_relay_switch": ":/symbols/affinity/circle/gray/isdn.svg",
|
|
@@ -35,6 +35,7 @@ from .drawing import Drawing
|
|
|
35
35
|
from .node import Node
|
|
36
36
|
from .link import Link
|
|
37
37
|
|
|
38
|
+
from gns3server.utils.hostname import is_ios_hostname_valid, is_rfc1123_hostname_valid, to_rfc1123_hostname, to_ios_hostname
|
|
38
39
|
from gns3server.schemas.controller.topology import Topology
|
|
39
40
|
from gns3server.schemas.compute.dynamips_nodes import DynamipsCreate
|
|
40
41
|
|
|
@@ -43,7 +44,7 @@ import logging
|
|
|
43
44
|
log = logging.getLogger(__name__)
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
GNS3_FILE_FORMAT_REVISION =
|
|
47
|
+
GNS3_FILE_FORMAT_REVISION = 10
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
class DynamipsNodeValidation(DynamipsCreate):
|
|
@@ -186,6 +187,10 @@ def load_topology(path):
|
|
|
186
187
|
if variables:
|
|
187
188
|
topo["variables"] = [var for var in variables if var.get("name")]
|
|
188
189
|
|
|
190
|
+
# Version before GNS3 3.0
|
|
191
|
+
if topo["revision"] < 10:
|
|
192
|
+
topo = _convert_2_2_0(topo, path)
|
|
193
|
+
|
|
189
194
|
try:
|
|
190
195
|
_check_topology_schema(topo, path)
|
|
191
196
|
except ControllerError as e:
|
|
@@ -201,6 +206,30 @@ def load_topology(path):
|
|
|
201
206
|
return topo
|
|
202
207
|
|
|
203
208
|
|
|
209
|
+
def _convert_2_2_0(topo, topo_path):
|
|
210
|
+
"""
|
|
211
|
+
Convert topologies from GNS3 2.2.x to 3.0
|
|
212
|
+
|
|
213
|
+
Changes:
|
|
214
|
+
* Convert Qemu and Docker node names to be a valid RFC1123 hostnames.
|
|
215
|
+
* Convert Dynamips and IOU node names to be a valid IOS hostnames.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
topo["revision"] = 10
|
|
219
|
+
|
|
220
|
+
for node in topo.get("topology", {}).get("nodes", []):
|
|
221
|
+
if "properties" in node:
|
|
222
|
+
if node["node_type"] in ("qemu", "docker") and not is_rfc1123_hostname_valid(node["name"]):
|
|
223
|
+
new_name = to_rfc1123_hostname(node["name"])
|
|
224
|
+
log.info(f"Convert node name {node['name']} to {new_name} (RFC1123)")
|
|
225
|
+
node["name"] = new_name
|
|
226
|
+
if node["node_type"] in ("dynamips", "iou") and not is_ios_hostname_valid(node["name"] ):
|
|
227
|
+
new_name = to_ios_hostname(node["name"])
|
|
228
|
+
log.info(f"Convert node name {node['name']} to {new_name} (IOS)")
|
|
229
|
+
node["name"] = new_name
|
|
230
|
+
return topo
|
|
231
|
+
|
|
232
|
+
|
|
204
233
|
def _convert_2_1_0(topo, topo_path):
|
|
205
234
|
"""
|
|
206
235
|
Convert topologies from GNS3 2.1.x to 2.2
|
|
@@ -213,7 +213,7 @@ class UDPLink(Link):
|
|
|
213
213
|
:returns: Node where the capture should run
|
|
214
214
|
"""
|
|
215
215
|
|
|
216
|
-
ALWAYS_RUNNING_NODES_TYPE = ("cloud", "nat", "ethernet_switch", "ethernet_hub")
|
|
216
|
+
ALWAYS_RUNNING_NODES_TYPE = ("cloud", "nat", "ethernet_switch", "ethernet_hub", "frame_relay_switch", "atm_switch")
|
|
217
217
|
|
|
218
218
|
for node in self._nodes:
|
|
219
219
|
if (
|
gns3server/crash_report.py
CHANGED
|
@@ -58,7 +58,7 @@ class CrashReport:
|
|
|
58
58
|
Report crash to a third party service
|
|
59
59
|
"""
|
|
60
60
|
|
|
61
|
-
DSN = "https://
|
|
61
|
+
DSN = "https://1ae6f3c9d64e75bf8ad39295723da722@o19455.ingest.us.sentry.io/38482"
|
|
62
62
|
_instance = None
|
|
63
63
|
|
|
64
64
|
def __init__(self):
|
gns3server/schemas/config.py
CHANGED
|
@@ -69,6 +69,7 @@ class QemuSettings(BaseModel):
|
|
|
69
69
|
monitor_host: str = "127.0.0.1"
|
|
70
70
|
enable_hardware_acceleration: bool = True
|
|
71
71
|
require_hardware_acceleration: bool = False
|
|
72
|
+
allow_unsafe_options: bool = False
|
|
72
73
|
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True)
|
|
73
74
|
|
|
74
75
|
|
|
@@ -126,6 +127,7 @@ class ServerSettings(BaseModel):
|
|
|
126
127
|
appliances_path: str = "~/GNS3/appliances"
|
|
127
128
|
symbols_path: str = "~/GNS3/symbols"
|
|
128
129
|
configs_path: str = "~/GNS3/configs"
|
|
130
|
+
resources_path: str = None
|
|
129
131
|
default_symbol_theme: BuiltinSymbolTheme = BuiltinSymbolTheme.affinity_square_blue
|
|
130
132
|
allow_raw_images: bool = True
|
|
131
133
|
auto_discover_images: bool = True
|
|
@@ -144,6 +146,7 @@ class ServerSettings(BaseModel):
|
|
|
144
146
|
default_nat_interface: str = None
|
|
145
147
|
allow_remote_console: bool = False
|
|
146
148
|
enable_builtin_templates: bool = True
|
|
149
|
+
install_builtin_appliances: bool = True
|
|
147
150
|
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True, use_enum_values=True)
|
|
148
151
|
|
|
149
152
|
@field_validator("additional_images_paths", mode="before")
|
|
@@ -45,7 +45,6 @@ class TemplateBase(BaseModel):
|
|
|
45
45
|
category: Optional[Category] = None
|
|
46
46
|
default_name_format: Optional[str] = None
|
|
47
47
|
symbol: Optional[str] = None
|
|
48
|
-
builtin: Optional[bool] = None
|
|
49
48
|
template_type: Optional[NodeType] = None
|
|
50
49
|
compute_id: Optional[str] = None
|
|
51
50
|
usage: Optional[str] = ""
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from jose import JWTError, jwt
|
|
19
|
-
from datetime import datetime, timedelta
|
|
19
|
+
from datetime import datetime, timedelta, timezone
|
|
20
20
|
import bcrypt
|
|
21
21
|
|
|
22
22
|
from typing import Optional
|
|
@@ -48,7 +48,7 @@ class AuthService:
|
|
|
48
48
|
|
|
49
49
|
if not expires_in:
|
|
50
50
|
expires_in = Config.instance().settings.Controller.jwt_access_token_expire_minutes
|
|
51
|
-
expire = datetime.
|
|
51
|
+
expire = datetime.now(timezone.utc) + timedelta(minutes=expires_in)
|
|
52
52
|
to_encode = {"sub": username, "exp": expire}
|
|
53
53
|
if secret_key is None:
|
|
54
54
|
secret_key = Config.instance().settings.Controller.jwt_secret_key
|
gns3server/services/templates.py
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<body class="mat-app-background" oncontextmenu="return false;">
|
|
37
37
|
<app-root></app-root>
|
|
38
38
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
39
|
-
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-
|
|
39
|
+
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-0BT7QQV1W1"></script>
|
|
40
40
|
<script>
|
|
41
41
|
window.dataLayer = window.dataLayer || [];
|
|
42
42
|
function gtag() {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
}
|
|
45
45
|
gtag('js', new Date());
|
|
46
46
|
|
|
47
|
-
gtag('config', 'G-
|
|
47
|
+
gtag('config', 'G-0BT7QQV1W1');
|
|
48
48
|
</script>
|
|
49
|
-
<script src="runtime.24fa95b7061d7056.js" type="module"></script><script src="polyfills.319c79dd175e50d0.js" type="module"></script><script src="main.
|
|
49
|
+
<script src="runtime.24fa95b7061d7056.js" type="module"></script><script src="polyfills.319c79dd175e50d0.js" type="module"></script><script src="main.4185a8e61824af0d.js" type="module"></script>
|
|
50
50
|
|
|
51
51
|
</body></html>
|