gns3-server 3.0.0b1__py3-none-any.whl → 3.0.0b3__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 (95) hide show
  1. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b3.dist-info}/METADATA +20 -20
  2. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b3.dist-info}/RECORD +93 -75
  3. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b3.dist-info}/WHEEL +1 -1
  4. gns3server/api/routes/compute/dynamips_nodes.py +2 -2
  5. gns3server/api/routes/compute/notifications.py +1 -5
  6. gns3server/api/routes/controller/controller.py +0 -5
  7. gns3server/api/routes/controller/images.py +4 -2
  8. gns3server/api/routes/controller/projects.py +0 -4
  9. gns3server/appliances/arista-veos.gns3a +178 -2
  10. gns3server/appliances/asterfusion-vAsterNOS.gns3a +49 -0
  11. gns3server/appliances/bird.gns3a +1 -1
  12. gns3server/appliances/bird2.gns3a +14 -0
  13. gns3server/appliances/cisco-asav.gns3a +13 -0
  14. gns3server/appliances/cisco-c8000v.gns3a +26 -0
  15. gns3server/appliances/cisco-cat9k.gns3a +16 -3
  16. gns3server/appliances/cisco-csr1000v.gns3a +13 -0
  17. gns3server/appliances/cisco-iosv.gns3a +14 -0
  18. gns3server/appliances/cisco-iosxrv9k.gns3a +13 -0
  19. gns3server/appliances/cisco-iou-l2.gns3a +12 -0
  20. gns3server/appliances/cisco-iou-l3.gns3a +12 -0
  21. gns3server/appliances/cisco-nxosv9k.gns3a +70 -0
  22. gns3server/appliances/clavister-netsheild.gns3a +15 -2
  23. gns3server/appliances/clavister-netwall.gns3a +15 -2
  24. gns3server/appliances/debian.gns3a +7 -7
  25. gns3server/appliances/dell-ftos.gns3a +21 -4
  26. gns3server/appliances/exos.gns3a +13 -151
  27. gns3server/appliances/fedora-cloud.gns3a +12 -42
  28. gns3server/appliances/fortianalyzer.gns3a +42 -0
  29. gns3server/appliances/fortigate.gns3a +56 -0
  30. gns3server/appliances/fortimanager.gns3a +43 -1
  31. gns3server/appliances/freebsd.gns3a +17 -2
  32. gns3server/appliances/huawei-ne40e.gns3a +51 -2
  33. gns3server/appliances/mikrotik-chr.gns3a +9 -0
  34. gns3server/appliances/mikrotik-rb1100ahx4-dude-edition.gns3a +1 -1
  35. gns3server/appliances/mikrotik-rb450g.gns3a +1 -1
  36. gns3server/appliances/mikrotik-rb450gx4.gns3a +1 -1
  37. gns3server/appliances/open-media-vault.gns3a +16 -0
  38. gns3server/appliances/openvswitch-management.gns3a +1 -1
  39. gns3server/appliances/opnsense.gns3a +13 -0
  40. gns3server/appliances/ostinato.gns3a +13 -0
  41. gns3server/appliances/rhel.gns3a +17 -3
  42. gns3server/appliances/rockylinux.gns3a +30 -0
  43. gns3server/appliances/ubuntu-cloud.gns3a +5 -5
  44. gns3server/compute/docker/__init__.py +3 -3
  45. gns3server/compute/docker/docker_vm.py +12 -3
  46. gns3server/compute/dynamips/__init__.py +7 -0
  47. gns3server/compute/iou/iou_vm.py +2 -1
  48. gns3server/compute/qemu/__init__.py +10 -3
  49. gns3server/compute/qemu/qemu_vm.py +7 -1
  50. gns3server/compute/vpcs/vpcs_vm.py +3 -0
  51. gns3server/controller/__init__.py +6 -6
  52. gns3server/controller/export_project.py +5 -0
  53. gns3server/controller/project.py +19 -5
  54. gns3server/controller/snapshot.py +3 -3
  55. gns3server/controller/symbol_themes.py +7 -0
  56. gns3server/controller/udp_link.py +1 -1
  57. gns3server/crash_report.py +1 -1
  58. gns3server/db/models/templates.py +2 -2
  59. gns3server/db_migrations/README +1 -0
  60. gns3server/main.py +5 -1
  61. gns3server/schemas/compute/docker_nodes.py +1 -1
  62. gns3server/schemas/controller/templates/__init__.py +0 -1
  63. gns3server/schemas/controller/templates/docker_templates.py +1 -1
  64. gns3server/server.py +3 -4
  65. gns3server/services/authentication.py +7 -7
  66. gns3server/services/templates.py +1 -1
  67. gns3server/static/web-ui/3rdpartylicenses.txt +23 -0
  68. gns3server/static/web-ui/index.html +1 -1
  69. gns3server/static/web-ui/main.f3840f9b1c0240e6.js +1 -0
  70. gns3server/static/web-ui/runtime.24fa95b7061d7056.js +1 -0
  71. gns3server/symbols/affinity/circle/blue/nat.svg +60 -0
  72. gns3server/symbols/affinity/circle/blue/nat2.svg +55 -0
  73. gns3server/symbols/affinity/circle/gray/nat.svg +60 -0
  74. gns3server/symbols/affinity/circle/gray/nat2.svg +55 -0
  75. gns3server/symbols/affinity/circle/green/nat.svg +60 -0
  76. gns3server/symbols/affinity/circle/green/nat2.svg +55 -0
  77. gns3server/symbols/affinity/circle/red/nat.svg +60 -0
  78. gns3server/symbols/affinity/circle/red/nat2.svg +55 -0
  79. gns3server/symbols/affinity/square/blue/nat.svg +58 -0
  80. gns3server/symbols/affinity/square/blue/nat2.svg +74 -0
  81. gns3server/symbols/affinity/square/gray/nat.svg +58 -0
  82. gns3server/symbols/affinity/square/gray/nat2.svg +74 -0
  83. gns3server/symbols/affinity/square/green/nat.svg +58 -0
  84. gns3server/symbols/affinity/square/green/nat2.svg +74 -0
  85. gns3server/symbols/affinity/square/red/nat.svg +58 -0
  86. gns3server/symbols/affinity/square/red/nat2.svg +74 -0
  87. gns3server/symbols/classic/nat.svg +207 -0
  88. gns3server/utils/asyncio/telnet_server.py +17 -4
  89. gns3server/utils/images.py +3 -5
  90. gns3server/version.py +1 -1
  91. gns3server/static/web-ui/main.fa00b32e7c90ea1c.js +0 -1
  92. gns3server/static/web-ui/runtime.53e0b4d68251ad21.js +0 -1
  93. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b3.dist-info}/LICENSE +0 -0
  94. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b3.dist-info}/entry_points.txt +0 -0
  95. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b3.dist-info}/top_level.txt +0 -0
@@ -252,6 +252,9 @@ class Dynamips(BaseManager):
252
252
  # look for Dynamips
253
253
  dynamips_path = self.config.settings.Dynamips.dynamips_path
254
254
  if not os.path.isabs(dynamips_path):
255
+ if sys.platform.startswith("win") and hasattr(sys, "frozen"):
256
+ dynamips_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "dynamips"))
257
+ os.environ["PATH"] = os.pathsep.join(dynamips_dir) + os.pathsep + os.environ.get("PATH", "")
255
258
  dynamips_path = shutil.which(dynamips_path)
256
259
 
257
260
  if not dynamips_path:
@@ -518,6 +521,10 @@ class Dynamips(BaseManager):
518
521
  if usage is not None and usage != vm.usage:
519
522
  vm.usage = usage
520
523
 
524
+ aux_type = settings.get("aux_type")
525
+ if aux_type is not None and aux_type != vm.aux_type:
526
+ vm.aux_type = aux_type
527
+
521
528
  # update the configs if needed
522
529
  await self.set_vm_configs(vm, settings)
523
530
 
@@ -661,7 +661,8 @@ class IOUVM(BaseNode):
661
661
  self._telnet_server.close()
662
662
  await self._telnet_server.wait_closed()
663
663
  self._telnet_server = None
664
- await self.start_console()
664
+ if self.is_running():
665
+ await self.start_console()
665
666
 
666
667
  @locking
667
668
  async def _networking(self):
@@ -149,13 +149,20 @@ class Qemu(BaseManager):
149
149
  for arch in archs:
150
150
  if f.endswith(arch) or f.endswith(f"{arch}.exe") or f.endswith(f"{arch}w.exe"):
151
151
  qemu_path = os.path.join(path, f)
152
- version = await Qemu.get_qemu_version(qemu_path)
152
+ try:
153
+ version = await Qemu.get_qemu_version(qemu_path)
154
+ except QemuError as e:
155
+ log.warning(str(e))
156
+ continue
153
157
  qemus.append({"path": qemu_path, "version": version})
154
158
  else:
155
159
  qemu_path = os.path.join(path, f)
156
- version = await Qemu.get_qemu_version(qemu_path)
160
+ try:
161
+ version = await Qemu.get_qemu_version(qemu_path)
162
+ except QemuError as e:
163
+ log.warning(str(e))
164
+ continue
157
165
  qemus.append({"path": qemu_path, "version": version})
158
-
159
166
  except OSError:
160
167
  continue
161
168
 
@@ -124,6 +124,8 @@ class QemuVM(BaseNode):
124
124
  except QemuError:
125
125
  # If the binary is not found for topologies 1.4 and later
126
126
  # search via the platform otherwise use the binary name
127
+ log.warning(f"Could not find the QEMU binary {qemu_path} on this system, "
128
+ f"trying to find one using platform {platform}")
127
129
  if platform:
128
130
  self.platform = platform
129
131
  else:
@@ -242,7 +244,7 @@ class QemuVM(BaseNode):
242
244
  if qemu_path and os.pathsep not in qemu_path:
243
245
  new_qemu_path = shutil.which(qemu_path, path=os.pathsep.join(self._manager.paths_list()))
244
246
  if new_qemu_path is None:
245
- raise QemuError(f"QEMU binary path {qemu_path} is not found in the path")
247
+ raise QemuError(f"QEMU binary '{qemu_path}' cannot be found on the system")
246
248
  qemu_path = new_qemu_path
247
249
 
248
250
  self._check_qemu_path(qemu_path)
@@ -289,6 +291,7 @@ class QemuVM(BaseNode):
289
291
  def platform(self, platform):
290
292
 
291
293
  self._platform = platform
294
+ log.info(f"QEMU VM '{self._name}' [{self._id}] has set the platform {platform}")
292
295
  self.qemu_path = f"qemu-system-{platform}"
293
296
 
294
297
  def _disk_setter(self, variable, value):
@@ -2640,6 +2643,9 @@ class QemuVM(BaseNode):
2640
2643
  command.extend(shlex.split(additional_options))
2641
2644
  except ValueError as e:
2642
2645
  raise QemuError(f"Invalid additional options: {additional_options} error {e}")
2646
+ # avoiding mouse offset (see https://github.com/GNS3/gns3-server/issues/2335)
2647
+ if self._console_type == "vnc":
2648
+ command.extend(['-machine', 'usb=on', '-device', 'usb-tablet'])
2643
2649
  return command
2644
2650
 
2645
2651
  def asdict(self):
@@ -142,6 +142,9 @@ class VPCSVM(BaseNode):
142
142
 
143
143
  vpcs_path = self._manager.config.settings.VPCS.vpcs_path
144
144
  if not os.path.isabs(vpcs_path):
145
+ if sys.platform.startswith("win") and hasattr(sys, "frozen"):
146
+ vpcs_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "vpcs"))
147
+ os.environ["PATH"] = os.pathsep.join(vpcs_dir) + os.pathsep + os.environ.get("PATH", "")
145
148
  vpcs_path = shutil.which(vpcs_path)
146
149
  return vpcs_path
147
150
 
@@ -317,12 +317,12 @@ class Controller:
317
317
  else:
318
318
  for entry in importlib_resources.files('gns3server').joinpath(resource_name).iterdir():
319
319
  full_path = os.path.join(dst_path, entry.name)
320
- if not os.path.exists(full_path):
321
- if entry.is_file():
322
- log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
323
- shutil.copy(str(entry), os.path.join(dst_path, entry.name))
324
- elif entry.is_dir():
325
- os.makedirs(full_path, exist_ok=True)
320
+ if entry.is_file() and not os.path.exists(full_path):
321
+ log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
322
+ shutil.copy(str(entry), os.path.join(dst_path, entry.name))
323
+ elif entry.is_dir():
324
+ os.makedirs(full_path, exist_ok=True)
325
+ Controller.install_resource_files(full_path, os.path.join(resource_name, entry.name))
326
326
 
327
327
  def _install_base_configs(self):
328
328
  """
@@ -102,6 +102,11 @@ async def export_project(
102
102
  continue
103
103
  _patch_mtime(path)
104
104
  zstream.write(path, os.path.relpath(path, project._path))
105
+ # save empty directories
106
+ for directory in dirs:
107
+ path = os.path.join(root, directory)
108
+ if not os.listdir(path):
109
+ zstream.write(path, os.path.relpath(path, project._path))
105
110
  except FileNotFoundError as e:
106
111
  log.warning(f"Cannot export local file: {e}")
107
112
  continue
@@ -152,16 +152,27 @@ class Project:
152
152
 
153
153
  self._iou_id_lock = asyncio.Lock()
154
154
  log.debug(f'Project "{self.name}" [{self._id}] loaded')
155
+ self.emit_controller_notification("project.created", self.asdict())
155
156
 
156
157
  def emit_notification(self, action, event):
157
158
  """
158
- Emit a notification to all clients using this project.
159
+ Emit a project notification to all clients using this project.
159
160
 
160
161
  :param action: Action name
161
162
  :param event: Event to send
162
163
  """
163
164
 
164
- self.controller.notification.project_emit(action, event, project_id=self.id)
165
+ self._controller.notification.project_emit(action, event, project_id=self.id)
166
+
167
+ def emit_controller_notification(self, action, event):
168
+ """
169
+ Emit a controller notification, all clients will see it.
170
+
171
+ :param action: Action name
172
+ :param event: Event to send
173
+ """
174
+
175
+ self._controller.notification.controller_emit(action, event)
165
176
 
166
177
  async def update(self, **kwargs):
167
178
  """
@@ -176,7 +187,7 @@ class Project:
176
187
 
177
188
  # We send notif only if object has changed
178
189
  if old_json != self.asdict():
179
- self.emit_notification("project.updated", self.asdict())
190
+ self.emit_controller_notification("project.updated", self.asdict())
180
191
  self.dump()
181
192
 
182
193
  # update on computes
@@ -812,7 +823,8 @@ class Project:
812
823
  self._clean_pictures()
813
824
  self._status = "closed"
814
825
  if not ignore_notification:
815
- self.emit_notification("project.closed", self.asdict())
826
+ self.emit_controller_notification("project.closed", self.asdict())
827
+
816
828
  self.reset()
817
829
  self._closing = False
818
830
 
@@ -868,6 +880,7 @@ class Project:
868
880
  shutil.rmtree(self.path)
869
881
  except OSError as e:
870
882
  raise ControllerError(f"Cannot delete project directory {self.path}: {str(e)}")
883
+ self.emit_controller_notification("project.deleted", self.asdict())
871
884
 
872
885
  async def delete_on_computes(self):
873
886
  """
@@ -1001,7 +1014,7 @@ class Project:
1001
1014
  await self.add_drawing(dump=False, **drawing_data)
1002
1015
 
1003
1016
  self.dump()
1004
- # We catch all error to be able to rollback the .gns3 to the previous state
1017
+ # We catch all error to be able to roll back the .gns3 to the previous state
1005
1018
  except Exception as e:
1006
1019
  for compute in list(self._project_created_on_compute):
1007
1020
  try:
@@ -1026,6 +1039,7 @@ class Project:
1026
1039
  pass
1027
1040
 
1028
1041
  self._loading = False
1042
+ self.emit_controller_notification("project.opened", self.asdict())
1029
1043
  # Should we start the nodes when project is open
1030
1044
  if self._auto_start:
1031
1045
  # Start all in the background without waiting for completion
@@ -51,11 +51,11 @@ 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.utcfromtimestamp(self._created_at).replace(tzinfo=None).strftime("%d%m%y_%H%M%S")
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:
@@ -66,7 +66,7 @@ class Snapshot:
66
66
  datetime.strptime(datestring, "%d%m%y_%H%M%S").replace(tzinfo=timezone.utc).timestamp()
67
67
  )
68
68
  except ValueError:
69
- self._created_at = datetime.utcnow().timestamp()
69
+ self._created_at = datetime.now(timezone.utc)
70
70
  self._path = os.path.join(project.path, "snapshots", filename)
71
71
 
72
72
  @property
@@ -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",
@@ -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 (
@@ -58,7 +58,7 @@ class CrashReport:
58
58
  Report crash to a third party service
59
59
  """
60
60
 
61
- DSN = "https://1608802d5d3109b6533e8b270cc8c9b6@o19455.ingest.sentry.io/38482"
61
+ DSN = "https://99870c759d1c1d62ceb091d59dbcfa78@o19455.ingest.us.sentry.io/38482"
62
62
  _instance = None
63
63
 
64
64
  def __init__(self):
@@ -16,7 +16,7 @@
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
 
19
- from sqlalchemy import Boolean, Column, String, Integer, ForeignKey, PickleType
19
+ from sqlalchemy import Boolean, Column, String, Integer, Float, ForeignKey, PickleType
20
20
  from sqlalchemy.orm import relationship
21
21
 
22
22
  from .base import BaseTable, generate_uuid, GUID
@@ -77,7 +77,7 @@ class DockerTemplate(Template):
77
77
  extra_hosts = Column(String)
78
78
  extra_volumes = Column(PickleType)
79
79
  memory = Column(Integer)
80
- cpus = Column(Integer)
80
+ cpus = Column(Float)
81
81
  custom_adapters = Column(PickleType)
82
82
 
83
83
  __mapper_args__ = {"polymorphic_identity": "docker", "polymorphic_load": "selectin"}
@@ -1,4 +1,5 @@
1
1
  Generic single-database configuration with an async dbapi.
2
2
 
3
3
  # Command to generate a revision
4
+ alembic upgrade head
4
5
  alembic revision --autogenerate -m "add fields in table"
gns3server/main.py CHANGED
@@ -29,6 +29,7 @@ import gns3server.utils.get_resource
29
29
 
30
30
  import os
31
31
  import sys
32
+ import asyncio
32
33
 
33
34
 
34
35
  def daemonize():
@@ -70,7 +71,10 @@ def main():
70
71
  daemonize()
71
72
  from gns3server.server import Server
72
73
 
73
- Server().run()
74
+ try:
75
+ asyncio.run(Server().run())
76
+ except KeyboardInterrupt:
77
+ pass
74
78
 
75
79
 
76
80
  if __name__ == "__main__":
@@ -43,7 +43,7 @@ class DockerBase(BaseModel):
43
43
  extra_hosts: Optional[str] = Field(None, description="Docker extra hosts (added to /etc/hosts)")
44
44
  extra_volumes: Optional[List[str]] = Field(None, description="Additional directories to make persistent")
45
45
  memory: Optional[int] = Field(None, description="Maximum amount of memory the container can use in MB")
46
- cpus: Optional[int] = Field(None, description="Maximum amount of CPU resources the container can use")
46
+ cpus: Optional[float] = Field(None, description="Maximum amount of CPU resources the container can use")
47
47
  custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adapters")
48
48
 
49
49
 
@@ -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] = ""
@@ -49,7 +49,7 @@ class DockerTemplate(TemplateBase):
49
49
  extra_hosts: Optional[str] = Field("", description="Docker extra hosts (added to /etc/hosts)")
50
50
  extra_volumes: Optional[List] = Field([], description="Additional directories to make persistent")
51
51
  memory: Optional[int] = Field(0, description="Maximum amount of memory the container can use in MB")
52
- cpus: Optional[int] = Field(0, description="Maximum amount of CPU resources the container can use")
52
+ cpus: Optional[float] = Field(0, description="Maximum amount of CPU resources the container can use")
53
53
  custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
54
54
 
55
55
 
gns3server/server.py CHANGED
@@ -182,7 +182,7 @@ class Server:
182
182
  asyncio.ensure_future(Controller.instance().reload())
183
183
  else:
184
184
  log.info(f"Server has got signal {signame}, exiting...")
185
- # send SIGTERM to the server PID so uvicorn can shutdown the process
185
+ # send SIGTERM to the server PID so uvicorn can shut down the process
186
186
  os.kill(os.getpid(), signal.SIGTERM)
187
187
  except asyncio.CancelledError:
188
188
  pass
@@ -239,7 +239,7 @@ class Server:
239
239
  log.critical("Can't write pid file %s: %s", path, str(e))
240
240
  sys.exit(1)
241
241
 
242
- def run(self):
242
+ async def run(self):
243
243
 
244
244
  args = self._parse_arguments(sys.argv[1:])
245
245
 
@@ -333,8 +333,7 @@ class Server:
333
333
  uvicorn_logger.propagate = False
334
334
 
335
335
  server = uvicorn.Server(config)
336
- loop = asyncio.get_event_loop()
337
- loop.run_until_complete(server.serve())
336
+ await server.serve()
338
337
 
339
338
  except Exception as e:
340
339
  log.critical(f"Critical error while running the server: {e}", exc_info=1)
@@ -16,8 +16,8 @@
16
16
 
17
17
 
18
18
  from jose import JWTError, jwt
19
- from datetime import datetime, timedelta
20
- from passlib.context import CryptContext
19
+ from datetime import datetime, timedelta, timezone
20
+ import bcrypt
21
21
 
22
22
  from typing import Optional
23
23
  from fastapi import HTTPException, status
@@ -29,8 +29,6 @@ import logging
29
29
 
30
30
  log = logging.getLogger(__name__)
31
31
 
32
- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
33
-
34
32
  DEFAULT_JWT_SECRET_KEY = "efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76cea5e33d4e"
35
33
 
36
34
 
@@ -38,17 +36,19 @@ class AuthService:
38
36
 
39
37
  def hash_password(self, password: str) -> str:
40
38
 
41
- return pwd_context.hash(password)
39
+ salt = bcrypt.gensalt()
40
+ hashed_password = bcrypt.hashpw(password=password.encode('utf-8'), salt=salt)
41
+ return hashed_password.decode('utf-8')
42
42
 
43
43
  def verify_password(self, password, hashed_password) -> bool:
44
44
 
45
- return pwd_context.verify(password, hashed_password)
45
+ return bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hashed_password.encode('utf-8'))
46
46
 
47
47
  def create_access_token(self, username, secret_key: str = None, expires_in: int = 0) -> str:
48
48
 
49
49
  if not expires_in:
50
50
  expires_in = Config.instance().settings.Controller.jwt_access_token_expire_minutes
51
- expire = datetime.utcnow() + timedelta(minutes=expires_in)
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
@@ -96,7 +96,7 @@ BUILTIN_TEMPLATES = [
96
96
  "name": "NAT",
97
97
  "default_name_format": "NAT{0}",
98
98
  "category": "guest",
99
- "symbol": "cloud",
99
+ "symbol": "nat",
100
100
  "compute_id": None,
101
101
  "builtin": True,
102
102
  },
@@ -1493,6 +1493,29 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1493
1493
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1494
1494
 
1495
1495
 
1496
+ ipaddr.js
1497
+ MIT
1498
+ Copyright (C) 2011-2017 whitequark <whitequark@whitequark.org>
1499
+
1500
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1501
+ of this software and associated documentation files (the "Software"), to deal
1502
+ in the Software without restriction, including without limitation the rights
1503
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1504
+ copies of the Software, and to permit persons to whom the Software is
1505
+ furnished to do so, subject to the following conditions:
1506
+
1507
+ The above copyright notice and this permission notice shall be included in
1508
+ all copies or substantial portions of the Software.
1509
+
1510
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1511
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1512
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1513
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1514
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1515
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1516
+ THE SOFTWARE.
1517
+
1518
+
1496
1519
  marked
1497
1520
  MIT
1498
1521
  # License information
@@ -46,6 +46,6 @@
46
46
 
47
47
  gtag('config', 'G-5D6FZL9923');
48
48
  </script>
49
- <script src="runtime.53e0b4d68251ad21.js" type="module"></script><script src="polyfills.319c79dd175e50d0.js" type="module"></script><script src="main.fa00b32e7c90ea1c.js" type="module"></script>
49
+ <script src="runtime.24fa95b7061d7056.js" type="module"></script><script src="polyfills.319c79dd175e50d0.js" type="module"></script><script src="main.f3840f9b1c0240e6.js" type="module"></script>
50
50
 
51
51
  </body></html>