gns3-server 3.0.0b1__py3-none-any.whl → 3.0.0b2__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 (67) hide show
  1. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b2.dist-info}/METADATA +17 -17
  2. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b2.dist-info}/RECORD +65 -64
  3. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b2.dist-info}/WHEEL +1 -1
  4. gns3server/api/routes/compute/notifications.py +1 -5
  5. gns3server/api/routes/controller/controller.py +0 -5
  6. gns3server/api/routes/controller/projects.py +0 -4
  7. gns3server/appliances/asterfusion-vAsterNOS.gns3a +49 -0
  8. gns3server/appliances/bird.gns3a +1 -1
  9. gns3server/appliances/bird2.gns3a +14 -0
  10. gns3server/appliances/cisco-asav.gns3a +13 -0
  11. gns3server/appliances/cisco-c8000v.gns3a +13 -0
  12. gns3server/appliances/cisco-cat9k.gns3a +16 -3
  13. gns3server/appliances/cisco-csr1000v.gns3a +13 -0
  14. gns3server/appliances/cisco-iosv.gns3a +14 -0
  15. gns3server/appliances/cisco-iosxrv9k.gns3a +13 -0
  16. gns3server/appliances/cisco-iou-l2.gns3a +12 -0
  17. gns3server/appliances/cisco-iou-l3.gns3a +12 -0
  18. gns3server/appliances/cisco-nxosv9k.gns3a +70 -0
  19. gns3server/appliances/clavister-netsheild.gns3a +15 -2
  20. gns3server/appliances/clavister-netwall.gns3a +15 -2
  21. gns3server/appliances/debian.gns3a +7 -7
  22. gns3server/appliances/dell-ftos.gns3a +21 -4
  23. gns3server/appliances/exos.gns3a +13 -151
  24. gns3server/appliances/fedora-cloud.gns3a +12 -42
  25. gns3server/appliances/fortianalyzer.gns3a +42 -0
  26. gns3server/appliances/fortigate.gns3a +56 -0
  27. gns3server/appliances/fortimanager.gns3a +43 -1
  28. gns3server/appliances/freebsd.gns3a +17 -2
  29. gns3server/appliances/huawei-ne40e.gns3a +51 -2
  30. gns3server/appliances/mikrotik-chr.gns3a +9 -0
  31. gns3server/appliances/mikrotik-rb1100ahx4-dude-edition.gns3a +1 -1
  32. gns3server/appliances/mikrotik-rb450g.gns3a +1 -1
  33. gns3server/appliances/mikrotik-rb450gx4.gns3a +1 -1
  34. gns3server/appliances/open-media-vault.gns3a +16 -0
  35. gns3server/appliances/opnsense.gns3a +13 -0
  36. gns3server/appliances/ostinato.gns3a +13 -0
  37. gns3server/appliances/rhel.gns3a +17 -3
  38. gns3server/appliances/rockylinux.gns3a +30 -0
  39. gns3server/appliances/ubuntu-cloud.gns3a +5 -5
  40. gns3server/compute/docker/__init__.py +2 -2
  41. gns3server/compute/docker/docker_vm.py +12 -3
  42. gns3server/compute/dynamips/__init__.py +3 -0
  43. gns3server/compute/qemu/__init__.py +10 -3
  44. gns3server/compute/qemu/qemu_vm.py +7 -1
  45. gns3server/compute/vpcs/vpcs_vm.py +3 -0
  46. gns3server/controller/__init__.py +6 -6
  47. gns3server/controller/export_project.py +5 -0
  48. gns3server/controller/project.py +19 -5
  49. gns3server/crash_report.py +1 -1
  50. gns3server/db/models/templates.py +2 -2
  51. gns3server/db_migrations/README +1 -0
  52. gns3server/main.py +5 -1
  53. gns3server/schemas/compute/docker_nodes.py +1 -1
  54. gns3server/schemas/controller/templates/docker_templates.py +1 -1
  55. gns3server/server.py +3 -4
  56. gns3server/services/authentication.py +5 -5
  57. gns3server/static/web-ui/3rdpartylicenses.txt +23 -0
  58. gns3server/static/web-ui/index.html +1 -1
  59. gns3server/static/web-ui/main.8e8e3d55e78d84fc.js +1 -0
  60. gns3server/static/web-ui/runtime.24fa95b7061d7056.js +1 -0
  61. gns3server/utils/asyncio/telnet_server.py +17 -4
  62. gns3server/version.py +1 -1
  63. gns3server/static/web-ui/main.fa00b32e7c90ea1c.js +0 -1
  64. gns3server/static/web-ui/runtime.53e0b4d68251ad21.js +0 -1
  65. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b2.dist-info}/LICENSE +0 -0
  66. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b2.dist-info}/entry_points.txt +0 -0
  67. {gns3_server-3.0.0b1.dist-info → gns3_server-3.0.0b2.dist-info}/top_level.txt +0 -0
@@ -26,6 +26,14 @@
26
26
  "options": "-nographic -cpu host"
27
27
  },
28
28
  "images": [
29
+ {
30
+ "filename": "Rocky-9-GenericCloud-Base-9.3-20231113.0.x86_64.qcow2",
31
+ "version": "9.3",
32
+ "md5sum": "48cdeb033364af5909e15ee48d0e144d",
33
+ "filesize": 1083965440,
34
+ "download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/",
35
+ "direct_download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base-9.3-20231113.0.x86_64.qcow2"
36
+ },
29
37
  {
30
38
  "filename": "Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2",
31
39
  "version": "9.2",
@@ -34,6 +42,14 @@
34
42
  "download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/",
35
43
  "direct_download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2"
36
44
  },
45
+ {
46
+ "filename": "Rocky-8-GenericCloud-Base-8.9-20231119.0.x86_64.qcow2",
47
+ "version": "8.9",
48
+ "md5sum": "50c44d8d9c4df43694372c13768f114c",
49
+ "filesize": 1971978240,
50
+ "download_url": "https://download.rockylinux.org/pub/rocky/8/images/x86_64/",
51
+ "direct_download_url": "https://download.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud-Base-8.9-20231119.0.x86_64.qcow2"
52
+ },
37
53
  {
38
54
  "filename": "Rocky-8-GenericCloud-Base-8.8-20230518.0.x86_64.qcow2",
39
55
  "version": "8.8",
@@ -52,6 +68,13 @@
52
68
  }
53
69
  ],
54
70
  "versions": [
71
+ {
72
+ "name": "9.3",
73
+ "images": {
74
+ "hda_disk_image": "Rocky-9-GenericCloud-Base-9.3-20231113.0.x86_64.qcow2",
75
+ "cdrom_image": "rocky-cloud-init-data.iso"
76
+ }
77
+ },
55
78
  {
56
79
  "name": "9.2",
57
80
  "images": {
@@ -59,6 +82,13 @@
59
82
  "cdrom_image": "rocky-cloud-init-data.iso"
60
83
  }
61
84
  },
85
+ {
86
+ "name": "8.9",
87
+ "images": {
88
+ "hda_disk_image": "Rocky-8-GenericCloud-Base-8.9-20231119.0.x86_64.qcow2",
89
+ "cdrom_image": "rocky-cloud-init-data.iso"
90
+ }
91
+ },
62
92
  {
63
93
  "name": "8.8",
64
94
  "images": {
@@ -28,12 +28,12 @@
28
28
  },
29
29
  "images": [
30
30
  {
31
- "filename": "ubuntu-23.04-server-cloudimg-arm64.img",
31
+ "filename": "ubuntu-23.04-server-cloudimg-amd64.img",
32
32
  "version": "Ubuntu 23.04 (Lunar Lobster)",
33
- "md5sum": "35fa3b31b65717af6f0520a769aac8c0",
34
- "filesize": 786432000,
33
+ "md5sum": "369e3b1f68416c39245a8014172406dd",
34
+ "filesize": 756678656,
35
35
  "download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/",
36
- "direct_download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/ubuntu-23.04-server-cloudimg-arm64.img"
36
+ "direct_download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/ubuntu-23.04-server-cloudimg-amd64.img"
37
37
  },
38
38
  {
39
39
  "filename": "ubuntu-22.04-server-cloudimg-amd64.img",
@@ -72,7 +72,7 @@
72
72
  {
73
73
  "name": "Ubuntu 23.04 (Lunar Lobster)",
74
74
  "images": {
75
- "hda_disk_image": "ubuntu-23.04-server-cloudimg-arm64.img",
75
+ "hda_disk_image": "ubuntu-23.04-server-cloudimg-amd64.img",
76
76
  "cdrom_image": "ubuntu-cloud-init-data.iso"
77
77
  }
78
78
  },
@@ -193,8 +193,8 @@ class Docker(BaseManager):
193
193
  if timeout is None:
194
194
  timeout = 60 * 60 * 24 * 31 # One month timeout
195
195
 
196
- if path == "version":
197
- url = "http://docker/v1.12/" + path # API of docker v1.0
196
+ if path == 'version':
197
+ url = "http://docker/v1.24/" + path
198
198
  else:
199
199
  url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
200
200
  try:
@@ -506,6 +506,10 @@ class DockerVM(BaseNode):
506
506
  result = await self.manager.query("POST", "containers/create", data=params)
507
507
  self._cid = result["Id"]
508
508
  log.info(f"Docker container '{self._name}' [{self._id}] created")
509
+ if self._cpus > 0:
510
+ log.info(f"CPU limit set to {self._cpus} CPUs")
511
+ if self._memory > 0:
512
+ log.info(f"Memory limit set to {self._memory} MB")
509
513
  return True
510
514
 
511
515
  def _format_env(self, variables, env):
@@ -571,6 +575,9 @@ class DockerVM(BaseNode):
571
575
  await self._start_vnc_process(restart=True)
572
576
  monitor_process(self._vnc_process, self._vnc_callback)
573
577
 
578
+ if self._console_websocket:
579
+ await self._console_websocket.close()
580
+ self._console_websocket = None
574
581
  await self._clean_servers()
575
582
 
576
583
  await self.manager.query("POST", f"containers/{self._cid}/start")
@@ -833,9 +840,7 @@ class DockerVM(BaseNode):
833
840
  f"containers/{self._cid}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1"
834
841
  )
835
842
  input_stream.ws = self._console_websocket
836
-
837
843
  output_stream.feed_data(self.name.encode() + b" console is now available... Press RETURN to get started.\r\n")
838
-
839
844
  asyncio.ensure_future(self._read_console_output(self._console_websocket, output_stream))
840
845
 
841
846
  async def _read_console_output(self, ws, out):
@@ -858,13 +863,14 @@ class DockerVM(BaseNode):
858
863
  out.feed_eof()
859
864
  await ws.close()
860
865
  break
861
- await self.stop()
862
866
 
863
867
  async def reset_console(self):
864
868
  """
865
869
  Reset the console.
866
870
  """
867
871
 
872
+ if self._console_websocket:
873
+ await self._console_websocket.close()
868
874
  await self._clean_servers()
869
875
  await self._start_console()
870
876
 
@@ -908,6 +914,9 @@ class DockerVM(BaseNode):
908
914
  """
909
915
 
910
916
  try:
917
+ if self._console_websocket:
918
+ await self._console_websocket.close()
919
+ self._console_websocket = None
911
920
  await self._clean_servers()
912
921
  await self._stop_ubridge()
913
922
 
@@ -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:
@@ -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
@@ -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://395af26fb5b2245d4c9810095aa11da9@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
 
@@ -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)
@@ -17,7 +17,7 @@
17
17
 
18
18
  from jose import JWTError, jwt
19
19
  from datetime import datetime, timedelta
20
- from passlib.context import CryptContext
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,11 +36,13 @@ 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
 
@@ -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.8e8e3d55e78d84fc.js" type="module"></script>
50
50
 
51
51
  </body></html>