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
@@ -97,18 +97,10 @@ async def wait_for_process_termination(process, timeout=10):
97
97
  :param timeout: Timeout in seconds
98
98
  """
99
99
 
100
- if sys.version_info >= (3, 5):
101
- try:
102
- await asyncio.wait_for(process.wait(), timeout=timeout)
103
- except ProcessLookupError:
104
- return
105
- else:
106
- while timeout > 0:
107
- if process.returncode is not None:
108
- return
109
- await asyncio.sleep(0.1)
110
- timeout -= 0.1
111
- raise asyncio.TimeoutError()
100
+ try:
101
+ await asyncio.wait_for(process.wait(), timeout=timeout)
102
+ except ProcessLookupError:
103
+ return
112
104
 
113
105
 
114
106
  async def _check_process(process, termination_callback):
@@ -40,10 +40,7 @@ class Pool:
40
40
  while len(self._tasks) > 0 or len(pending) > 0:
41
41
  while len(self._tasks) > 0 and len(pending) < self._concurrency:
42
42
  task, args, kwargs = self._tasks.pop(0)
43
- if sys.version_info >= (3, 7):
44
- t = asyncio.create_task(task(*args, **kwargs))
45
- else:
46
- t = asyncio.get_event_loop().create_task(task(*args, **kwargs))
43
+ t = asyncio.create_task(task(*args, **kwargs))
47
44
  pending.add(t)
48
45
  (done, pending) = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
49
46
  for task in done:
@@ -20,6 +20,11 @@ import stat
20
20
  import aiofiles
21
21
  import shutil
22
22
 
23
+ try:
24
+ import importlib_resources
25
+ except ImportError:
26
+ from importlib import resources as importlib_resources
27
+
23
28
  from typing import List, AsyncGenerator
24
29
  from ..config import Config
25
30
  from . import force_unix_path
@@ -62,45 +67,63 @@ async def list_images(image_type):
62
67
  directory = os.path.normpath(directory)
63
68
  for root, _, filenames in _os_walk(directory, recurse=recurse):
64
69
  for filename in filenames:
65
- if filename not in files:
66
- if filename.endswith(".md5sum") or filename.startswith("."):
70
+ if filename in files:
71
+ log.debug("File {} has already been found, skipping...".format(filename))
72
+ continue
73
+ if filename.endswith(".md5sum") or filename.startswith("."):
74
+ continue
75
+
76
+ files.add(filename)
77
+
78
+ # It the image is located in the standard directory the path is relative
79
+ if os.path.commonprefix([root, default_directory]) != default_directory:
80
+ path = os.path.join(root, filename)
81
+ else:
82
+ path = os.path.relpath(os.path.join(root, filename), default_directory)
83
+
84
+ filesize = os.stat(os.path.join(root, filename)).st_size
85
+ if filesize < 7:
86
+ log.debug(f"File {filename} is too small to be an image, skipping...")
87
+ continue
88
+
89
+ try:
90
+ with open(os.path.join(root, filename), "rb") as f:
91
+ # read the first 7 bytes of the file.
92
+ elf_header_start = f.read(7)
93
+ if image_type == "dynamips" and elf_header_start != b'\x7fELF\x01\x02\x01':
94
+ # IOS images must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1
95
+ log.warning(f"IOS image {filename} does not start with a valid ELF magic number, skipping...")
96
+ continue
97
+ elif image_type == "iou" and elf_header_start != b'\x7fELF\x02\x01\x01' and elf_header_start != b'\x7fELF\x01\x01\x01':
98
+ # IOU images must start with the ELF magic number, be 32-bit or 64-bit, little endian and have an ELF version of 1
99
+ log.warning(f"IOU image {filename} does not start with a valid ELF magic number, skipping...")
100
+ continue
101
+ elif image_type == "qemu" and elf_header_start[:4] == b'\x7fELF':
102
+ # QEMU images should not start with an ELF magic number
103
+ log.warning(f"QEMU image {filename} starts with an ELF magic number, skipping...")
67
104
  continue
68
- elif (
69
- ((filename.endswith(".image") or filename.endswith(".bin")) and image_type == "dynamips")
70
- or ((filename.endswith(".bin") or filename.startswith("i86bi")) and image_type == "iou")
71
- or (not filename.endswith(".bin") and not filename.endswith(".image") and image_type == "qemu")
72
- ):
73
- files.add(filename)
74
-
75
- # It the image is located in the standard directory the path is relative
76
- if os.path.commonprefix([root, default_directory]) != default_directory:
77
- path = os.path.join(root, filename)
78
- else:
79
- path = os.path.relpath(os.path.join(root, filename), default_directory)
80
-
81
- try:
82
- if image_type in ["dynamips", "iou"]:
83
- with open(os.path.join(root, filename), "rb") as f:
84
- # read the first 7 bytes of the file.
85
- elf_header_start = f.read(7)
86
- # valid IOU or IOS images must start with the ELF magic number, be 32-bit or 64-bit,
87
- # little endian and have an ELF version of 1
88
- if elf_header_start != b'\x7fELF\x02\x01\x01' and elf_header_start != b'\x7fELF\x01\x01\x01':
89
- continue
90
-
91
- images.append(
92
- {
93
- "filename": filename,
94
- "path": force_unix_path(path),
95
- "md5sum": await wait_run_in_executor(md5sum, os.path.join(root, filename)),
96
- "filesize": os.stat(os.path.join(root, filename)).st_size,
97
- }
98
- )
99
- except OSError as e:
100
- log.warning(f"Can't add image {path}: {str(e)}")
105
+
106
+ images.append(
107
+ {
108
+ "filename": filename,
109
+ "path": force_unix_path(path),
110
+ "md5sum": await wait_run_in_executor(md5sum, os.path.join(root, filename)),
111
+ "filesize": filesize,
112
+ }
113
+ )
114
+ except OSError as e:
115
+ log.warning(f"Can't add image {path}: {str(e)}")
101
116
  return images
102
117
 
103
118
 
119
+ def get_builtin_disks() -> List[str]:
120
+ builtin_disks = []
121
+ for entry in importlib_resources.files('gns3server').joinpath("disks").iterdir():
122
+ if entry.is_file():
123
+ builtin_disks.append(entry.name)
124
+ return builtin_disks
125
+
126
+
104
127
  async def read_image_info(path: str, expected_image_type: str = None) -> dict:
105
128
 
106
129
  header_magic_len = 7
@@ -108,7 +131,7 @@ async def read_image_info(path: str, expected_image_type: str = None) -> dict:
108
131
  async with aiofiles.open(path, "rb") as f:
109
132
  image_header = await f.read(header_magic_len) # read the first 7 bytes of the file
110
133
  if len(image_header) >= header_magic_len:
111
- detected_image_type = check_valid_image_header(image_header)
134
+ detected_image_type = check_valid_image_header(path, image_header)
112
135
  if expected_image_type and detected_image_type != expected_image_type:
113
136
  raise InvalidImageError(f"Detected image type for '{path}' is {detected_image_type}, "
114
137
  f"expected type is {expected_image_type}")
@@ -292,7 +315,7 @@ class InvalidImageError(Exception):
292
315
  return self._message
293
316
 
294
317
 
295
- def check_valid_image_header(data: bytes, allow_raw_image: bool = False) -> str:
318
+ def check_valid_image_header(path: str, data: bytes, allow_raw_image: bool = False) -> str:
296
319
 
297
320
  if data[:7] == b'\x7fELF\x01\x02\x01':
298
321
  # for IOS images: file must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1
@@ -307,7 +330,7 @@ def check_valid_image_header(data: bytes, allow_raw_image: bool = False) -> str:
307
330
  else:
308
331
  if allow_raw_image is True:
309
332
  return "qemu"
310
- raise InvalidImageError("Could not detect image type, please make sure it is a valid image")
333
+ raise InvalidImageError(f"{path}: could not detect image type, please make sure it is a valid image")
311
334
 
312
335
 
313
336
  async def write_image(
@@ -332,7 +355,7 @@ async def write_image(
332
355
  async for chunk in stream:
333
356
  if check_image_header and len(chunk) >= header_magic_len:
334
357
  check_image_header = False
335
- image_type = check_valid_image_header(chunk, allow_raw_image)
358
+ image_type = check_valid_image_header(image_path, chunk, allow_raw_image)
336
359
  await f.write(chunk)
337
360
  checksum.update(chunk)
338
361
 
gns3server/version.py CHANGED
@@ -22,8 +22,8 @@
22
22
  # or negative for a release candidate or beta (after the base version
23
23
  # number has been incremented)
24
24
 
25
- __version__ = "3.0.0rc2"
26
- __version_info__ = (3, 0, 0, -99)
25
+ __version__ = "3.0.2"
26
+ __version_info__ = (3, 0, 2, 0)
27
27
 
28
28
  if "dev" in __version__:
29
29
  try: