mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-06-24 09:46:42 +00:00
Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
b5e2bb1561 | |||
3003ed1465 | |||
09774a3a56 | |||
369fcaa512 | |||
7b7d3d676f | |||
d1186b2acc | |||
bd58196817 | |||
b3a822df8f | |||
ba4e0c945d | |||
f6725a37dd | |||
8e96c4c2e8 | |||
035a104957 | |||
54c0b383cb | |||
7c0384a8d4 | |||
469bd692a0 | |||
d34c3a0ac7 | |||
fb3409c1a1 | |||
08693871ae | |||
f5ea20347e | |||
ac4c5f5985 | |||
e17a79e261 | |||
4758431c76 | |||
b472f6dbf8 | |||
b39c7541fb | |||
82779d816f | |||
ccc8974d92 | |||
0090ff3722 | |||
96c6805ace | |||
efb84b3063 | |||
0be45c7da2 | |||
a4222b4d03 | |||
5f75fc7573 | |||
5269d4386c | |||
9d6cea665a | |||
2c727c6bd2 | |||
13b0caef4f | |||
8b57fbaa0a | |||
d4a9a21af9 | |||
8a900588ca | |||
8ed1fa6ad5 | |||
a689a55937 | |||
1c0b0ae423 | |||
ad7813d04b | |||
685bf88005 | |||
a1090a3da8 | |||
c8e5b41f39 | |||
94b5d3e636 | |||
e94b55e3bf | |||
1a6a729736 | |||
77c356c82c | |||
b4819b5500 | |||
11a9451098 |
@ -18,7 +18,7 @@ jobs:
|
||||
ref: "gh-pages"
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- name: Merge changes from 3.0 branch
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
|
2
.github/workflows/testing.yml
vendored
2
.github/workflows/testing.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
#include:
|
||||
# only test with Python 3.10 on Windows
|
||||
# - os: windows-latest
|
||||
|
30
CHANGELOG
30
CHANGELOG
@ -1,5 +1,34 @@
|
||||
# Change Log
|
||||
|
||||
## 3.0.3 22/01/2025
|
||||
|
||||
* Bundle web-ui v3.0.3
|
||||
* Refactor command line arguments parsing
|
||||
* Fix capture on IOU/IOL links. Fixes #2477
|
||||
* Use Python 3.9 to publish API documentation
|
||||
* Upgrade dependencies
|
||||
|
||||
## 2.2.53 21/01/2025
|
||||
|
||||
* Bundle web-ui v2.2.53
|
||||
* Add more information when patching .vbox file. Ref https://github.com/GNS3/gns3-gui/issues/3542
|
||||
* Increase timeout to run compute HTTP queries. Fixes #2461
|
||||
* Use 'allow_methods="*"' in aiohttp_cors.ResourceOptions(). Fixes #2459
|
||||
* Upgrade dependencies
|
||||
* Update remote-install.sh to support a custom repository and the deb822 source format
|
||||
* Fix: do not use the iourc file if IOU licence check is not enabled
|
||||
|
||||
## 3.0.2 03/01/2025
|
||||
|
||||
* Bundle web-ui v3.0.2
|
||||
* Support to create templates based on image checksums.
|
||||
* Improvements for installing built-in disks.
|
||||
* Use watchdog instead of watchfiles to monitor for new images on the file system
|
||||
* Drop Python 3.8
|
||||
* Replace python-jose library by joserfc
|
||||
* Upgrade dependencies
|
||||
* Remove blocking IOU phone home call.
|
||||
|
||||
## 3.0.1 27/12/2024
|
||||
|
||||
* Bundle web-ui v3.0.1
|
||||
@ -20,7 +49,6 @@
|
||||
## 2.2.52 02/12/2024
|
||||
|
||||
* Bundle web-ui v2.2.52
|
||||
* Sync appliances
|
||||
* Remove restrictions based on file extension when listing images and fix ELF header checks
|
||||
* Fix use project name instead of ID for fast duplication when running local server. Fixes #2446
|
||||
* Overwrite user resources when the originals have changed.
|
||||
|
@ -1,7 +1,7 @@
|
||||
pytest==8.3.3
|
||||
pytest==8.3.4
|
||||
flake8==7.1.1
|
||||
pytest-timeout==2.3.1
|
||||
pytest-asyncio==0.21.2
|
||||
pytest-asyncio==0.25.2
|
||||
requests==2.32.3
|
||||
httpx==0.27.2 # version 0.24.1 is required by httpx_ws
|
||||
httpx_ws==0.6.2
|
||||
httpx==0.28.1
|
||||
httpx_ws==0.7.1
|
||||
|
@ -284,7 +284,7 @@ async def start_iou_node_capture(
|
||||
"""
|
||||
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
await node.start_capture(adapter_number, port_number, pcap_file_path)
|
||||
return {"pcap_file_path": str(pcap_file_path)}
|
||||
|
||||
|
||||
|
@ -27,11 +27,11 @@ from fastapi.encoders import jsonable_encoder
|
||||
from starlette.requests import ClientDisconnect
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound
|
||||
from typing import List, Optional
|
||||
from gns3server import schemas
|
||||
|
||||
from gns3server import schemas
|
||||
from gns3server.config import Config
|
||||
from gns3server.compute.qemu import Qemu
|
||||
from gns3server.utils.images import InvalidImageError, write_image, read_image_info, default_images_directory
|
||||
from gns3server.utils.images import InvalidImageError, write_image, read_image_info, default_images_directory, get_builtin_disks
|
||||
from gns3server.db.repositories.images import ImagesRepository
|
||||
from gns3server.db.repositories.templates import TemplatesRepository
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
@ -51,7 +51,6 @@ log = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post(
|
||||
"/qemu/{image_path:path}",
|
||||
response_model=schemas.Image,
|
||||
@ -175,6 +174,61 @@ async def upload_image(
|
||||
return image
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/prune",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(has_privilege("Image.Allocate"))]
|
||||
)
|
||||
async def prune_images(
|
||||
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
|
||||
) -> None:
|
||||
"""
|
||||
Prune images not attached to any template.
|
||||
|
||||
Required privilege: Image.Allocate
|
||||
"""
|
||||
|
||||
skip_images = get_builtin_disks()
|
||||
await images_repo.prune_images(skip_images)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/install",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(has_privilege("Image.Allocate"))]
|
||||
)
|
||||
async def install_images(
|
||||
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
|
||||
templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository))
|
||||
) -> None:
|
||||
"""
|
||||
Attempt to automatically create templates based on image checksums.
|
||||
|
||||
Required privilege: Image.Allocate
|
||||
"""
|
||||
|
||||
skip_images = get_builtin_disks()
|
||||
images = await images_repo.get_images()
|
||||
for image in images:
|
||||
if skip_images and image.filename in skip_images:
|
||||
log.debug(f"Skipping image '{image.path}' for image installation")
|
||||
continue
|
||||
templates = await images_repo.get_image_templates(image.image_id)
|
||||
if templates:
|
||||
# the image is already used by a template
|
||||
log.warning(f"Image '{image.path}' is used by one or more templates")
|
||||
continue
|
||||
await Controller.instance().appliance_manager.install_appliances_from_image(
|
||||
image.path,
|
||||
image.checksum,
|
||||
images_repo,
|
||||
templates_repo,
|
||||
None,
|
||||
None,
|
||||
os.path.dirname(image.path)
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{image_path:path}",
|
||||
response_model=schemas.Image,
|
||||
@ -218,7 +272,7 @@ async def delete_image(
|
||||
image = await images_repo.get_image(image_path)
|
||||
except MultipleResultsFound:
|
||||
raise ControllerBadRequestError(f"Image '{image_path}' matches multiple images. "
|
||||
f"Please include the relative path of the image")
|
||||
f"Please include the absolute path of the image")
|
||||
|
||||
if not image:
|
||||
raise ControllerNotFoundError(f"Image '{image_path}' not found")
|
||||
@ -236,20 +290,3 @@ async def delete_image(
|
||||
success = await images_repo.delete_image(image_path)
|
||||
if not success:
|
||||
raise ControllerError(f"Image '{image_path}' could not be deleted")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/prune",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(has_privilege("Image.Allocate"))]
|
||||
)
|
||||
async def prune_images(
|
||||
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
|
||||
) -> None:
|
||||
"""
|
||||
Prune images not attached to any template.
|
||||
|
||||
Required privilege: Image.Allocate
|
||||
"""
|
||||
|
||||
await images_repo.prune_images()
|
||||
|
@ -18,6 +18,7 @@
|
||||
API routes for templates.
|
||||
"""
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
@ -34,6 +35,8 @@ from gns3server.db.repositories.templates import TemplatesRepository
|
||||
from gns3server.services.templates import TemplatesService
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
from gns3server.db.repositories.images import ImagesRepository
|
||||
from gns3server.controller.controller_error import ControllerError
|
||||
from gns3server.utils.images import get_builtin_disks
|
||||
|
||||
from .dependencies.authentication import get_current_active_user
|
||||
from .dependencies.rbac import has_privilege
|
||||
@ -132,10 +135,28 @@ async def delete_template(
|
||||
Required privilege: Template.Allocate
|
||||
"""
|
||||
|
||||
images = await templates_repo.get_template_images(template_id)
|
||||
await TemplatesService(templates_repo).delete_template(template_id)
|
||||
await rbac_repo.delete_all_ace_starting_with_path(f"/templates/{template_id}")
|
||||
if prune_images:
|
||||
await images_repo.prune_images()
|
||||
if prune_images and images:
|
||||
skip_images = get_builtin_disks()
|
||||
for image in images:
|
||||
if image.filename in skip_images:
|
||||
continue
|
||||
templates = await images_repo.get_image_templates(image.image_id)
|
||||
if templates:
|
||||
template_names = ", ".join([template.name for template in templates])
|
||||
raise ControllerError(f"Image '{image.path}' is used by one or more templates: {template_names}")
|
||||
|
||||
try:
|
||||
os.remove(image.path)
|
||||
except OSError:
|
||||
log.warning(f"Could not delete image file {image.path}")
|
||||
|
||||
print(f"Deleting image '{image.path}'")
|
||||
success = await images_repo.delete_image(image.path)
|
||||
if not success:
|
||||
raise ControllerError(f"Image '{image.path}' could not removed from the database")
|
||||
|
||||
|
||||
@router.get(
|
||||
|
56
gns3server/appliances/alpine-cloud.gns3a
Normal file
56
gns3server/appliances/alpine-cloud.gns3a
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"appliance_id": "edbaa01e-2032-4ee2-bb9f-dd5c4d84c270",
|
||||
"name": "Alpine Cloud Guest",
|
||||
"category": "guest",
|
||||
"description": "Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.",
|
||||
"vendor_name": "Alpine Linux Development Team",
|
||||
"vendor_url": "http://alpinelinux.org",
|
||||
"vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/Alpine Linux.png",
|
||||
"documentation_url": "http://wiki.alpinelinux.org",
|
||||
"product_name": "Alpine Linux",
|
||||
"product_url": "https://www.alpinelinux.org/cloud/",
|
||||
"registry_version": 4,
|
||||
"status": "stable",
|
||||
"maintainer": "GNS3 Team",
|
||||
"maintainer_email": "developers@gns3.net",
|
||||
"usage": "\nUsername: alpine\nPassword: alpine",
|
||||
"port_name_format": "Ethernet{0}",
|
||||
"qemu": {
|
||||
"adapter_type": "virtio-net-pci",
|
||||
"adapters": 1,
|
||||
"ram": 1024,
|
||||
"hda_disk_interface": "virtio",
|
||||
"arch": "x86_64",
|
||||
"console_type": "telnet",
|
||||
"boot_priority": "c",
|
||||
"kvm": "require",
|
||||
"options": "-nographic"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"filename": "generic_alpine-3.21.2-x86_64-bios-cloudinit-r0.qcow2",
|
||||
"version": "3.21.2",
|
||||
"md5sum": "b40825dff2867e0ffaffbc4c87674462",
|
||||
"filesize": 189726720,
|
||||
"download_url": "https://www.alpinelinux.org/cloud/",
|
||||
"direct_download_url": "https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/cloud/generic_alpine-3.21.2-x86_64-bios-cloudinit-r0.qcow2"
|
||||
},
|
||||
{
|
||||
"filename": "alpine-cloud-init-data.iso",
|
||||
"version": "1.0",
|
||||
"md5sum": "b1b4b16cc3bf0250c0fa377c19c97683",
|
||||
"filesize": 374784,
|
||||
"download_url": "https://github.com/GNS3/gns3-registry/tree/master/cloud-init/alpine-cloud",
|
||||
"direct_download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/alpine-cloud/alpine-cloud-init-data.iso"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.21.2",
|
||||
"images": {
|
||||
"hda_disk_image": "generic_alpine-3.21.2-x86_64-bios-cloudinit-r0.qcow2",
|
||||
"cdrom_image": "alpine-cloud-init-data.iso"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
50
gns3server/appliances/stormshield-eva.gns3a
Normal file
50
gns3server/appliances/stormshield-eva.gns3a
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"appliance_id": "60801097-332e-4f40-a63e-8ad62047c01f",
|
||||
"name": "Stormshield EVA",
|
||||
"category": "firewall",
|
||||
"description": "Stormshield EVA (Elastic Virtual Appliance) is a french virtual firewall designed to protect network infrastructures. It offers advanced features such as filtering, intrusion prevention (IPS), VPN management (IPSec/SSL), and access control.",
|
||||
"vendor_name": "Stormshield",
|
||||
"vendor_url": "https://www.stormshield.com/",
|
||||
"vendor_logo_url": "https://www.stormshield.com/wp-content/uploads/stormshield-logo.png",
|
||||
"documentation_url": "https://www.stormshield.com/fr/ressourcescenter/network-security-elastic-virtual-appliances/",
|
||||
"product_name": "Stormshield EVA",
|
||||
"product_url": "https://www.stormshield.com/fr/produits-et-services/produits/protection-des-reseaux/nos-produits/appliances-virtuelles/",
|
||||
"registry_version": 4,
|
||||
"status": "stable",
|
||||
"availability": "service-contract",
|
||||
"maintainer": "Samy SCANNA",
|
||||
"maintainer_email": "samy.scanna@outlook.com",
|
||||
"usage": "After the first boot, the appliance automatically runs the configuration script to set up the password, and network interfaces.",
|
||||
"symbol": "stormshield.png",
|
||||
"port_name_format": "port{port1}",
|
||||
"qemu": {
|
||||
"adapter_type": "vmxnet3",
|
||||
"adapters": 8,
|
||||
"ram": 2048,
|
||||
"cpus": 1,
|
||||
"hda_disk_interface": "scsi",
|
||||
"arch": "x86_64",
|
||||
"console_type": "telnet",
|
||||
"kvm": "allow",
|
||||
"options": "-serial stdio",
|
||||
"on_close": "shutdown_signal",
|
||||
"process_priority": "normal"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"filename": "utm-SNS-EVA-4.3.33-kvm.qcow2",
|
||||
"version": "4.3.33",
|
||||
"md5sum": "21d94d0e20f2e270f06c5853fd750d5b",
|
||||
"filesize": 284360704,
|
||||
"download_url": "https://mystormshield.eu/product/download/"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"images": {
|
||||
"hda_disk_image": "utm-SNS-EVA-4.3.33-kvm.qcow2"
|
||||
},
|
||||
"name": "4.3.33"
|
||||
}
|
||||
]
|
||||
}
|
@ -115,7 +115,7 @@ class Docker(BaseManager):
|
||||
dst_path = self.resources_path()
|
||||
log.info(f"Installing Docker resources in '{dst_path}'")
|
||||
from gns3server.controller import Controller
|
||||
Controller.instance().install_resource_files(dst_path, "compute/docker/resources")
|
||||
await Controller.instance().install_resource_files(dst_path, "compute/docker/resources")
|
||||
await self.install_busybox(dst_path)
|
||||
except OSError as e:
|
||||
raise DockerError(f"Could not install Docker resources to {dst_path}: {e}")
|
||||
|
@ -233,32 +233,36 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
Fix the VM uuid in the case of linked clone
|
||||
"""
|
||||
if os.path.exists(self._linked_vbox_file()):
|
||||
try:
|
||||
tree = ET.parse(self._linked_vbox_file())
|
||||
except ET.ParseError:
|
||||
raise VirtualBoxError(
|
||||
"Cannot modify VirtualBox linked nodes file. "
|
||||
"File {} is corrupted.".format(self._linked_vbox_file())
|
||||
|
||||
linked_vbox_file = self._linked_vbox_file()
|
||||
if not os.path.exists(linked_vbox_file):
|
||||
raise VirtualBoxError("Cannot find VirtualBox linked node file: {}".format(linked_vbox_file))
|
||||
|
||||
try:
|
||||
tree = ET.parse(linked_vbox_file)
|
||||
except ET.ParseError:
|
||||
raise VirtualBoxError(f"Cannot modify VirtualBox linked node file. "
|
||||
"File {linked_vbox_file} is corrupted.")
|
||||
except OSError as e:
|
||||
raise VirtualBoxError(f"Cannot modify VirtualBox linked nodes file '{self._linked_vbox_file()}': {e}")
|
||||
|
||||
machine = tree.getroot().find("{http://www.virtualbox.org/}Machine")
|
||||
if machine is not None and machine.get("uuid") != "{" + self.id + "}":
|
||||
|
||||
for image in tree.getroot().findall("{http://www.virtualbox.org/}Image"):
|
||||
currentSnapshot = machine.get("currentSnapshot")
|
||||
if currentSnapshot:
|
||||
newSnapshot = re.sub(r"\{.*\}", "{" + str(uuid.uuid4()) + "}", currentSnapshot)
|
||||
shutil.move(
|
||||
os.path.join(self.working_dir, self._vmname, "Snapshots", currentSnapshot) + ".vdi",
|
||||
os.path.join(self.working_dir, self._vmname, "Snapshots", newSnapshot) + ".vdi"
|
||||
)
|
||||
except OSError as e:
|
||||
raise VirtualBoxError(f"Cannot modify VirtualBox linked nodes file '{self._linked_vbox_file()}': {e}")
|
||||
log.info(f"VirtualBox VM '{self.name}' [{self.id}] snapshot file moved from '{currentSnapshot}' to '{newSnapshot}'")
|
||||
image.set("uuid", newSnapshot)
|
||||
|
||||
machine = tree.getroot().find("{http://www.virtualbox.org/}Machine")
|
||||
if machine is not None and machine.get("uuid") != "{" + self.id + "}":
|
||||
|
||||
for image in tree.getroot().findall("{http://www.virtualbox.org/}Image"):
|
||||
currentSnapshot = machine.get("currentSnapshot")
|
||||
if currentSnapshot:
|
||||
newSnapshot = re.sub(r"\{.*\}", "{" + str(uuid.uuid4()) + "}", currentSnapshot)
|
||||
shutil.move(
|
||||
os.path.join(self.working_dir, self._vmname, "Snapshots", currentSnapshot) + ".vdi",
|
||||
os.path.join(self.working_dir, self._vmname, "Snapshots", newSnapshot) + ".vdi",
|
||||
)
|
||||
image.set("uuid", newSnapshot)
|
||||
|
||||
machine.set("uuid", "{" + self.id + "}")
|
||||
tree.write(self._linked_vbox_file())
|
||||
log.info(f"VirtualBox VM '{self.name}' [{self.id}] '{linked_vbox_file}' has been patched")
|
||||
machine.set("uuid", "{" + self.id + "}")
|
||||
tree.write(linked_vbox_file)
|
||||
|
||||
async def check_hw_virtualization(self):
|
||||
"""
|
||||
@ -488,7 +492,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
async def save_linked_hdds_info(self):
|
||||
"""
|
||||
Save linked cloned hard disks information.
|
||||
Save linked cloned hard disk information.
|
||||
|
||||
:returns: disk table information
|
||||
"""
|
||||
|
@ -28,10 +28,10 @@ try:
|
||||
except ImportError:
|
||||
from importlib import resources as importlib_resources
|
||||
|
||||
|
||||
from ..config import Config
|
||||
from ..utils import parse_version, md5sum
|
||||
from ..utils.images import default_images_directory
|
||||
from ..utils.asyncio import wait_run_in_executor
|
||||
|
||||
from .project import Project
|
||||
from .appliance import Appliance
|
||||
@ -43,6 +43,7 @@ from .topology import load_topology
|
||||
from .gns3vm import GNS3VM
|
||||
from .gns3vm.gns3_vm_error import GNS3VMError
|
||||
from .controller_error import ControllerError, ControllerNotFoundError
|
||||
from ..db.tasks import update_disk_checksums
|
||||
from ..version import __version__
|
||||
|
||||
import logging
|
||||
@ -72,8 +73,11 @@ class Controller:
|
||||
async def start(self, computes=None):
|
||||
|
||||
log.info("Controller is starting")
|
||||
self._install_base_configs()
|
||||
self._install_builtin_disks()
|
||||
await self._install_base_configs()
|
||||
installed_disks = await self._install_builtin_disks()
|
||||
if installed_disks:
|
||||
await update_disk_checksums(installed_disks)
|
||||
|
||||
server_config = Config.instance().settings.Server
|
||||
Config.instance().listen_for_config_changes(self._update_config)
|
||||
name = server_config.name
|
||||
@ -86,7 +90,7 @@ class Controller:
|
||||
if host == "0.0.0.0":
|
||||
host = "127.0.0.1"
|
||||
|
||||
self._load_controller_vars()
|
||||
await self._load_controller_vars()
|
||||
|
||||
if server_config.enable_ssl:
|
||||
self._ssl_context = self._create_ssl_context(server_config)
|
||||
@ -190,7 +194,7 @@ class Controller:
|
||||
async def reload(self):
|
||||
|
||||
log.info("Controller is reloading")
|
||||
self._load_controller_vars()
|
||||
await self._load_controller_vars()
|
||||
|
||||
# remove all projects deleted from disk.
|
||||
for project in self._projects.copy().values():
|
||||
@ -234,7 +238,7 @@ class Controller:
|
||||
except OSError as e:
|
||||
log.error(f"Cannot write controller vars file '{self._vars_file}': {e}")
|
||||
|
||||
def _load_controller_vars(self):
|
||||
async def _load_controller_vars(self):
|
||||
"""
|
||||
Reload the controller vars from disk
|
||||
"""
|
||||
@ -274,9 +278,9 @@ class Controller:
|
||||
builtin_appliances_path = self._appliance_manager.builtin_appliances_path()
|
||||
if not previous_version or \
|
||||
parse_version(__version__.split("+")[0]) > parse_version(previous_version.split("+")[0]):
|
||||
self._appliance_manager.install_builtin_appliances()
|
||||
await self._appliance_manager.install_builtin_appliances()
|
||||
elif not os.listdir(builtin_appliances_path):
|
||||
self._appliance_manager.install_builtin_appliances()
|
||||
await self._appliance_manager.install_builtin_appliances()
|
||||
else:
|
||||
log.info(f"Built-in appliances are installed in '{builtin_appliances_path}'")
|
||||
|
||||
@ -307,18 +311,21 @@ class Controller:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def install_resource_files(dst_path, resource_name, upgrade_resources=True):
|
||||
async def install_resource_files(dst_path, resource_name, upgrade_resources=True):
|
||||
"""
|
||||
Install files from resources to user's file system
|
||||
"""
|
||||
|
||||
def should_copy(src, dst, upgrade_resources):
|
||||
installed_resources = []
|
||||
async def should_copy(src, dst, upgrade_resources):
|
||||
if not os.path.exists(dst):
|
||||
return True
|
||||
if upgrade_resources is False:
|
||||
return False
|
||||
# copy the resource if it is different
|
||||
return md5sum(src) != md5sum(dst)
|
||||
src_md5 = await wait_run_in_executor(md5sum, src)
|
||||
dst_md5 = await wait_run_in_executor(md5sum, dst)
|
||||
return src_md5 != dst_md5
|
||||
|
||||
if hasattr(sys, "frozen") and sys.platform.startswith("win"):
|
||||
resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name))
|
||||
@ -328,14 +335,16 @@ class Controller:
|
||||
else:
|
||||
for entry in importlib_resources.files('gns3server').joinpath(resource_name).iterdir():
|
||||
full_path = os.path.join(dst_path, entry.name)
|
||||
if entry.is_file() and should_copy(str(entry), full_path, upgrade_resources):
|
||||
if entry.is_file() and await should_copy(str(entry), full_path, upgrade_resources):
|
||||
log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
|
||||
shutil.copy(str(entry), os.path.join(dst_path, entry.name))
|
||||
shutil.copy(str(entry), os.path.join(full_path))
|
||||
installed_resources.append(full_path)
|
||||
elif entry.is_dir():
|
||||
os.makedirs(full_path, exist_ok=True)
|
||||
Controller.install_resource_files(full_path, os.path.join(resource_name, entry.name))
|
||||
await Controller.install_resource_files(full_path, os.path.join(resource_name, entry.name))
|
||||
return installed_resources
|
||||
|
||||
def _install_base_configs(self):
|
||||
async def _install_base_configs(self):
|
||||
"""
|
||||
At startup we copy base configs to the user location to allow
|
||||
them to customize it
|
||||
@ -345,11 +354,11 @@ class Controller:
|
||||
log.info(f"Installing base configs in '{dst_path}'")
|
||||
try:
|
||||
# do not overwrite base configs because they may have been customized by the user
|
||||
Controller.install_resource_files(dst_path, "configs", upgrade_resources=False)
|
||||
await Controller.install_resource_files(dst_path, "configs", upgrade_resources=False)
|
||||
except OSError as e:
|
||||
log.error(f"Could not install base config files to {dst_path}: {e}")
|
||||
|
||||
def _install_builtin_disks(self):
|
||||
async def _install_builtin_disks(self):
|
||||
"""
|
||||
At startup we copy built-in Qemu disks to the user location to allow
|
||||
them to use with appliances
|
||||
@ -358,7 +367,7 @@ class Controller:
|
||||
dst_path = self.disks_path()
|
||||
log.info(f"Installing built-in disks in '{dst_path}'")
|
||||
try:
|
||||
Controller.install_resource_files(dst_path, "disks")
|
||||
return await Controller.install_resource_files(dst_path, "disks")
|
||||
except OSError as e:
|
||||
log.error(f"Could not install disk files to {dst_path}: {e}")
|
||||
|
||||
|
@ -110,7 +110,7 @@ class ApplianceManager:
|
||||
os.makedirs(appliances_dir, exist_ok=True)
|
||||
return appliances_dir
|
||||
|
||||
def install_builtin_appliances(self):
|
||||
async def install_builtin_appliances(self):
|
||||
"""
|
||||
At startup we copy the built-in appliances files.
|
||||
"""
|
||||
@ -119,7 +119,7 @@ class ApplianceManager:
|
||||
log.info(f"Installing built-in appliances in '{dst_path}'")
|
||||
from . import Controller
|
||||
try:
|
||||
Controller.instance().install_resource_files(dst_path, "appliances")
|
||||
await Controller.instance().install_resource_files(dst_path, "appliances")
|
||||
except OSError as e:
|
||||
log.error(f"Could not install built-in appliance files to {dst_path}: {e}")
|
||||
|
||||
|
@ -18,14 +18,19 @@
|
||||
import ipaddress
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import async_timeout
|
||||
import socket
|
||||
import json
|
||||
import sys
|
||||
import io
|
||||
|
||||
from fastapi import HTTPException
|
||||
from aiohttp import web
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from asyncio import timeout as asynctimeout
|
||||
else:
|
||||
from async_timeout import timeout as asynctimeout
|
||||
|
||||
from ..utils import parse_version
|
||||
from ..utils.asyncio import locking
|
||||
from ..controller.controller_error import (
|
||||
@ -503,7 +508,7 @@ class Compute:
|
||||
return self._getUrl(path)
|
||||
|
||||
async def _run_http_query(self, method, path, data=None, timeout=120, raw=False):
|
||||
async with async_timeout.timeout(delay=timeout):
|
||||
async with asynctimeout(delay=timeout):
|
||||
url = self._getUrl(path)
|
||||
headers = {"content-type": "application/json"}
|
||||
chunked = None
|
||||
|
@ -58,7 +58,7 @@ class CrashReport:
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "https://847198b87dbd50ef8962901641918a08@o19455.ingest.us.sentry.io/38482"
|
||||
DSN = "https://2c96fa0280f82c48108f122b87cd902c@o19455.ingest.us.sentry.io/38482"
|
||||
_instance = None
|
||||
|
||||
def __init__(self):
|
||||
|
@ -18,7 +18,7 @@
|
||||
import os
|
||||
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import select, delete
|
||||
from sqlalchemy import select, delete, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from .base import BaseRepository
|
||||
@ -103,6 +103,22 @@ class ImagesRepository(BaseRepository):
|
||||
await self._db_session.refresh(db_image)
|
||||
return db_image
|
||||
|
||||
async def update_image(self, image_path: str, checksum: str, checksum_algorithm: str) -> models.Image:
|
||||
"""
|
||||
Update an image.
|
||||
"""
|
||||
|
||||
query = update(models.Image).\
|
||||
where(models.Image.path == image_path).\
|
||||
values(checksum=checksum, checksum_algorithm=checksum_algorithm)
|
||||
|
||||
await self._db_session.execute(query)
|
||||
await self._db_session.commit()
|
||||
image_db = await self.get_image_by_checksum(checksum)
|
||||
if image_db:
|
||||
await self._db_session.refresh(image_db) # force refresh of updated_at value
|
||||
return image_db
|
||||
|
||||
async def delete_image(self, image_path: str) -> bool:
|
||||
"""
|
||||
Delete an image.
|
||||
@ -119,7 +135,7 @@ class ImagesRepository(BaseRepository):
|
||||
await self._db_session.commit()
|
||||
return result.rowcount > 0
|
||||
|
||||
async def prune_images(self) -> int:
|
||||
async def prune_images(self, skip_images: list[str] = None) -> int:
|
||||
"""
|
||||
Prune images not attached to any template.
|
||||
"""
|
||||
@ -130,12 +146,15 @@ class ImagesRepository(BaseRepository):
|
||||
images = result.scalars().all()
|
||||
images_deleted = 0
|
||||
for image in images:
|
||||
if skip_images and image.filename in skip_images:
|
||||
log.debug(f"Skipping image '{image.path}' for pruning")
|
||||
continue
|
||||
try:
|
||||
log.debug(f"Deleting image '{image.path}'")
|
||||
os.remove(image.path)
|
||||
except OSError:
|
||||
log.warning(f"Could not delete image file {image.path}")
|
||||
if await self.delete_image(image.filename):
|
||||
if await self.delete_image(image.path):
|
||||
images_deleted += 1
|
||||
log.info(f"{images_deleted} image(s) have been deleted")
|
||||
return images_deleted
|
||||
|
@ -170,3 +170,14 @@ class TemplatesRepository(BaseRepository):
|
||||
await self._db_session.commit()
|
||||
await self._db_session.refresh(template_in_db)
|
||||
return template_in_db
|
||||
|
||||
async def get_template_images(self, template_id: UUID) -> List[models.Image]:
|
||||
"""
|
||||
Return all images attached to a template.
|
||||
"""
|
||||
|
||||
query = select(models.Image).\
|
||||
join(models.Image.templates).\
|
||||
filter(models.Template.template_id == template_id)
|
||||
result = await self._db_session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
@ -16,13 +16,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import asyncio
|
||||
import signal
|
||||
import time
|
||||
import os
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import ValidationError
|
||||
from watchfiles import awatch, Change
|
||||
|
||||
from typing import List
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.engine import Engine
|
||||
@ -32,10 +30,13 @@ from alembic import command, config
|
||||
from alembic.script import ScriptDirectory
|
||||
from alembic.runtime.migration import MigrationContext
|
||||
from alembic.util.exc import CommandError
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEvent, PatternMatchingEventHandler
|
||||
|
||||
from gns3server.db.repositories.computes import ComputesRepository
|
||||
from gns3server.db.repositories.images import ImagesRepository
|
||||
from gns3server.utils.images import discover_images, check_valid_image_header, read_image_info, default_images_directory, InvalidImageError
|
||||
from gns3server.utils.images import md5sum, discover_images, read_image_info, InvalidImageError
|
||||
from gns3server.utils.asyncio import wait_run_in_executor
|
||||
from gns3server import schemas
|
||||
|
||||
from .models import Base
|
||||
@ -130,81 +131,7 @@ async def get_computes(app: FastAPI) -> List[dict]:
|
||||
return computes
|
||||
|
||||
|
||||
def image_filter(change: Change, path: str) -> bool:
|
||||
|
||||
if change == Change.added and os.path.isfile(path):
|
||||
if path.endswith(".tmp") or path.endswith(".md5sum") or path.startswith("."):
|
||||
return False
|
||||
if "/lib/" in path or "/lib64/" in path:
|
||||
# ignore custom IOU libraries
|
||||
return False
|
||||
header_magic_len = 7
|
||||
with open(path, "rb") as f:
|
||||
image_header = f.read(header_magic_len) # read the first 7 bytes of the file
|
||||
if len(image_header) >= header_magic_len:
|
||||
try:
|
||||
check_valid_image_header(image_header)
|
||||
except InvalidImageError as e:
|
||||
log.debug(f"New image '{path}': {e}")
|
||||
return False
|
||||
else:
|
||||
log.debug(f"New image '{path}': size is too small to be valid")
|
||||
return False
|
||||
return True
|
||||
# FIXME: should we support image deletion?
|
||||
# elif change == Change.deleted:
|
||||
# return True
|
||||
return False
|
||||
|
||||
|
||||
async def monitor_images_on_filesystem(app: FastAPI):
|
||||
|
||||
directories_to_monitor = []
|
||||
for image_type in ("qemu", "ios", "iou"):
|
||||
image_dir = default_images_directory(image_type)
|
||||
if os.path.isdir(image_dir):
|
||||
log.debug(f"Monitoring for new images in '{image_dir}'")
|
||||
directories_to_monitor.append(image_dir)
|
||||
|
||||
try:
|
||||
async for changes in awatch(
|
||||
*directories_to_monitor,
|
||||
watch_filter=image_filter,
|
||||
raise_interrupt=True
|
||||
):
|
||||
async with AsyncSession(app.state._db_engine) as db_session:
|
||||
images_repository = ImagesRepository(db_session)
|
||||
for change in changes:
|
||||
change_type, image_path = change
|
||||
if change_type == Change.added:
|
||||
try:
|
||||
image = await read_image_info(image_path)
|
||||
except InvalidImageError as e:
|
||||
log.warning(str(e))
|
||||
continue
|
||||
try:
|
||||
if await images_repository.get_image(image_path):
|
||||
continue
|
||||
await images_repository.add_image(**image)
|
||||
log.info(f"Discovered image '{image_path}' has been added to the database")
|
||||
except SQLAlchemyError as e:
|
||||
log.warning(f"Error while adding image '{image_path}' to the database: {e}")
|
||||
# if change_type == Change.deleted:
|
||||
# try:
|
||||
# if await images_repository.get_image(image_path):
|
||||
# success = await images_repository.delete_image(image_path)
|
||||
# if not success:
|
||||
# log.warning(f"Could not delete image '{image_path}' from the database")
|
||||
# else:
|
||||
# log.info(f"Image '{image_path}' has been deleted from the database")
|
||||
# except SQLAlchemyError as e:
|
||||
# log.warning(f"Error while deleting image '{image_path}' from the database: {e}")
|
||||
except KeyboardInterrupt:
|
||||
# send SIGTERM to the server PID so uvicorn can shutdown the process
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
|
||||
|
||||
async def discover_images_on_filesystem(app: FastAPI):
|
||||
async def discover_images_on_filesystem(app: FastAPI) -> None:
|
||||
|
||||
async with AsyncSession(app.state._db_engine) as db_session:
|
||||
images_repository = ImagesRepository(db_session)
|
||||
@ -228,3 +155,117 @@ async def discover_images_on_filesystem(app: FastAPI):
|
||||
|
||||
# monitor if images have been manually added
|
||||
asyncio.create_task(monitor_images_on_filesystem(app))
|
||||
|
||||
|
||||
async def update_disk_checksums(updated_disks: List[str]) -> None:
|
||||
"""
|
||||
Update the checksum of a list of disks in the database.
|
||||
|
||||
:param updated_disks: list of updated disks
|
||||
"""
|
||||
|
||||
from gns3server.api.server import app
|
||||
async with AsyncSession(app.state._db_engine) as db_session:
|
||||
images_repository = ImagesRepository(db_session)
|
||||
for path in updated_disks:
|
||||
image = await images_repository.get_image(path)
|
||||
if image:
|
||||
log.info(f"Updating image '{path}' in the database")
|
||||
checksum = await wait_run_in_executor(md5sum, path, cache_to_md5file=False)
|
||||
if image.checksum != checksum:
|
||||
await images_repository.update_image(path, checksum, "md5")
|
||||
|
||||
class EventHandler(PatternMatchingEventHandler):
|
||||
"""
|
||||
Watchdog event handler.
|
||||
"""
|
||||
|
||||
def __init__(self, queue: asyncio.Queue, loop: asyncio.BaseEventLoop, **kwargs):
|
||||
|
||||
self._loop = loop
|
||||
self._queue = queue
|
||||
|
||||
# ignore temporary files, md5sum files, hidden files and directories
|
||||
super().__init__(ignore_patterns=["*.tmp", "*.md5sum", ".*"], ignore_directories = True, **kwargs)
|
||||
|
||||
def on_closed(self, event: FileSystemEvent) -> None:
|
||||
# monitor for closed files (e.g. when a file has finished to be copied)
|
||||
if "/lib/" in event.src_path or "/lib64/" in event.src_path:
|
||||
return # ignore custom IOU libraries
|
||||
self._loop.call_soon_threadsafe(self._queue.put_nowait, event)
|
||||
|
||||
class EventIterator(object):
|
||||
"""
|
||||
Watchdog Event iterator.
|
||||
"""
|
||||
|
||||
def __init__(self, queue: asyncio.Queue):
|
||||
self.queue = queue
|
||||
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
async def __anext__(self):
|
||||
|
||||
item = await self.queue.get()
|
||||
if item is None:
|
||||
raise StopAsyncIteration
|
||||
return item
|
||||
|
||||
async def monitor_images_on_filesystem(app: FastAPI):
|
||||
|
||||
def watchdog(
|
||||
path: str,
|
||||
queue: asyncio.Queue,
|
||||
loop: asyncio.BaseEventLoop,
|
||||
app: FastAPI, recursive: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
Thread to monitor a directory for new images.
|
||||
"""
|
||||
|
||||
handler = EventHandler(queue, loop)
|
||||
observer = Observer()
|
||||
observer.schedule(handler, str(path), recursive=recursive)
|
||||
observer.start()
|
||||
log.info(f"Monitoring for new images in '{path}'")
|
||||
while True:
|
||||
time.sleep(1)
|
||||
# stop when the app is exiting
|
||||
if app.state.exiting:
|
||||
observer.stop()
|
||||
observer.join(10)
|
||||
log.info(f"Stopping monitoring for new images in '{path}'")
|
||||
loop.call_soon_threadsafe(queue.put_nowait, None)
|
||||
break
|
||||
|
||||
queue = asyncio.Queue()
|
||||
loop = asyncio.get_event_loop()
|
||||
server_config = Config.instance().settings.Server
|
||||
image_dir = os.path.expanduser(server_config.images_path)
|
||||
asyncio.get_event_loop().run_in_executor(None, watchdog,image_dir, queue, loop, app, True)
|
||||
|
||||
async for filesystem_event in EventIterator(queue):
|
||||
# read the file system event from the queue
|
||||
image_path = filesystem_event.src_path
|
||||
expected_image_type = None
|
||||
if "IOU" in image_path:
|
||||
expected_image_type = "iou"
|
||||
elif "QEMU" in image_path:
|
||||
expected_image_type = "qemu"
|
||||
elif "IOS" in image_path:
|
||||
expected_image_type = "ios"
|
||||
async with AsyncSession(app.state._db_engine) as db_session:
|
||||
images_repository = ImagesRepository(db_session)
|
||||
try:
|
||||
image = await read_image_info(image_path, expected_image_type)
|
||||
except InvalidImageError as e:
|
||||
log.warning(str(e))
|
||||
continue
|
||||
try:
|
||||
if await images_repository.get_image(image_path):
|
||||
continue
|
||||
await images_repository.add_image(**image)
|
||||
log.info(f"Discovered image '{image_path}' has been added to the database")
|
||||
except SQLAlchemyError as e:
|
||||
log.warning(f"Error while adding image '{image_path}' to the database: {e}")
|
||||
|
@ -30,6 +30,7 @@ import gns3server.utils.get_resource
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
import argparse
|
||||
|
||||
|
||||
def daemonize():
|
||||
@ -59,6 +60,42 @@ def daemonize():
|
||||
print("Second fork failed: %d (%s)\n" % (e.errno, e.strerror), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def parse_arguments(argv):
|
||||
"""
|
||||
Parse command line arguments
|
||||
|
||||
:param argv: Array of command line arguments
|
||||
"""
|
||||
from gns3server.version import __version__
|
||||
parser = argparse.ArgumentParser(description=f"GNS3 server version {__version__}")
|
||||
parser.add_argument("-v", "--version", help="show the version", action="version", version=__version__)
|
||||
parser.add_argument("--host", help="run on the given host/IP address")
|
||||
parser.add_argument("--port", help="run on the given port", type=int)
|
||||
parser.add_argument("--ssl", action="store_true", help="run in SSL mode")
|
||||
parser.add_argument("--config", help="Configuration file")
|
||||
parser.add_argument("--certfile", help="SSL cert file")
|
||||
parser.add_argument("--certkey", help="SSL key file")
|
||||
parser.add_argument("-L", "--local", action="store_true", help="local mode (allows some insecure operations)")
|
||||
parser.add_argument(
|
||||
"-A", "--allow", action="store_true", help="allow remote connections to local console ports"
|
||||
)
|
||||
parser.add_argument("-q", "--quiet", default=False, action="store_true", help="do not show logs on stdout")
|
||||
parser.add_argument("-d", "--debug", default=False, action="store_true", help="show debug logs")
|
||||
parser.add_argument("--logfile", "--log", help="send output to logfile instead of console")
|
||||
parser.add_argument("--logmaxsize", default=10000000, help="maximum logfile size in bytes (default is 10MB)")
|
||||
parser.add_argument(
|
||||
"--logbackupcount", default=10, help="number of historical log files to keep (default is 10)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--logcompression", default=False, action="store_true", help="compress inactive (historical) logs"
|
||||
)
|
||||
parser.add_argument("--daemon", action="store_true", help="start as a daemon")
|
||||
parser.add_argument("--pid", help="store process pid")
|
||||
parser.add_argument("--profile", help="Settings profile (blank will use default settings files)")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
return parser, args
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
@ -69,10 +106,11 @@ def main():
|
||||
raise SystemExit("Windows is not a supported platform to run the GNS3 server")
|
||||
if "--daemon" in sys.argv:
|
||||
daemonize()
|
||||
from gns3server.server import Server
|
||||
|
||||
try:
|
||||
asyncio.run(Server().run())
|
||||
parser, args = parse_arguments(sys.argv[1:])
|
||||
from gns3server.server import Server
|
||||
asyncio.run(Server().run(parser, args))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
@ -23,7 +23,6 @@ Start the program. Use main.py to load it.
|
||||
import os
|
||||
import datetime
|
||||
import locale
|
||||
import argparse
|
||||
import psutil
|
||||
import sys
|
||||
import asyncio
|
||||
@ -33,13 +32,10 @@ import uvicorn
|
||||
import secrets
|
||||
import string
|
||||
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.compute.port_manager import PortManager
|
||||
from gns3server.logger import init_logger
|
||||
from gns3server.version import __version__
|
||||
from gns3server.config import Config
|
||||
from gns3server.crash_report import CrashReport
|
||||
from gns3server.api.server import app
|
||||
from pydantic import ValidationError, SecretStr
|
||||
|
||||
import logging
|
||||
@ -90,40 +86,13 @@ class Server:
|
||||
else:
|
||||
log.info(f"Current locale is {language}.{encoding}")
|
||||
|
||||
def _parse_arguments(self, argv):
|
||||
def _setup_logging(self, args):
|
||||
"""
|
||||
Parse command line arguments and override local configuration
|
||||
Setup logging.
|
||||
|
||||
:params args: Array of command line arguments
|
||||
:param args: command line arguments
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description=f"GNS3 server version {__version__}")
|
||||
parser.add_argument("-v", "--version", help="show the version", action="version", version=__version__)
|
||||
parser.add_argument("--host", help="run on the given host/IP address")
|
||||
parser.add_argument("--port", help="run on the given port", type=int)
|
||||
parser.add_argument("--ssl", action="store_true", help="run in SSL mode")
|
||||
parser.add_argument("--config", help="Configuration file")
|
||||
parser.add_argument("--certfile", help="SSL cert file")
|
||||
parser.add_argument("--certkey", help="SSL key file")
|
||||
parser.add_argument("-L", "--local", action="store_true", help="local mode (allows some insecure operations)")
|
||||
parser.add_argument(
|
||||
"-A", "--allow", action="store_true", help="allow remote connections to local console ports"
|
||||
)
|
||||
parser.add_argument("-q", "--quiet", default=False, action="store_true", help="do not show logs on stdout")
|
||||
parser.add_argument("-d", "--debug", default=False, action="store_true", help="show debug logs")
|
||||
parser.add_argument("--logfile", "--log", help="send output to logfile instead of console")
|
||||
parser.add_argument("--logmaxsize", default=10000000, help="maximum logfile size in bytes (default is 10MB)")
|
||||
parser.add_argument(
|
||||
"--logbackupcount", default=10, help="number of historical log files to keep (default is 10)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--logcompression", default=False, action="store_true", help="compress inactive (historical) logs"
|
||||
)
|
||||
parser.add_argument("--daemon", action="store_true", help="start as a daemon")
|
||||
parser.add_argument("--pid", help="store process pid")
|
||||
parser.add_argument("--profile", help="Settings profile (blank will use default settings files)")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
level = logging.INFO
|
||||
if args.debug:
|
||||
level = logging.DEBUG
|
||||
@ -137,6 +106,15 @@ class Server:
|
||||
quiet=args.quiet,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _load_config_and_set_defaults(parser, args, argv=None):
|
||||
"""
|
||||
Parse command line arguments and override local configuration
|
||||
|
||||
:param parser: ArgumentParser instance
|
||||
:param args: command line arguments
|
||||
"""
|
||||
|
||||
try:
|
||||
if args.config:
|
||||
Config.instance(files=[args.config], profile=args.profile)
|
||||
@ -157,7 +135,10 @@ class Server:
|
||||
}
|
||||
|
||||
parser.set_defaults(**defaults)
|
||||
return parser.parse_args(argv)
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
args = parser.parse_args(argv)
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
def _set_config_defaults_from_command_line(args):
|
||||
@ -174,6 +155,8 @@ class Server:
|
||||
config.Server.enable_ssl = args.ssl
|
||||
|
||||
def _signal_handling(self):
|
||||
|
||||
from gns3server.controller import Controller
|
||||
def signal_handler(signame, *args):
|
||||
|
||||
try:
|
||||
@ -239,9 +222,10 @@ class Server:
|
||||
log.critical("Can't write pid file %s: %s", path, str(e))
|
||||
sys.exit(1)
|
||||
|
||||
async def run(self):
|
||||
async def run(self, parser, args):
|
||||
|
||||
args = self._parse_arguments(sys.argv[1:])
|
||||
self._setup_logging(args)
|
||||
args = self._load_config_and_set_defaults(parser, args)
|
||||
|
||||
if args.pid:
|
||||
self._pid_lock(args.pid)
|
||||
@ -256,7 +240,6 @@ class Server:
|
||||
|
||||
self._set_config_defaults_from_command_line(args)
|
||||
config = Config.instance().settings
|
||||
|
||||
if not config.Server.compute_password.get_secret_value():
|
||||
alphabet = string.ascii_letters + string.digits + string.punctuation
|
||||
generated_password = ''.join(secrets.choice(alphabet) for _ in range(16))
|
||||
@ -267,9 +250,9 @@ class Server:
|
||||
else:
|
||||
log.info(f"Compute authentication is enabled with username '{config.Server.compute_username}'")
|
||||
|
||||
# we only support Python 3 version >= 3.8
|
||||
if sys.version_info < (3, 8, 0):
|
||||
raise SystemExit("Python 3.8 or higher is required")
|
||||
# we only support Python 3 version >= 3.9
|
||||
if sys.version_info < (3, 9, 0):
|
||||
raise SystemExit("Python 3.9 or higher is required")
|
||||
|
||||
log.info(
|
||||
"Running with Python {major}.{minor}.{micro} and has PID {pid}".format(
|
||||
@ -297,11 +280,13 @@ class Server:
|
||||
host = config.Server.host
|
||||
port = config.Server.port
|
||||
|
||||
from gns3server.compute.port_manager import PortManager
|
||||
PortManager.instance().console_host = host
|
||||
self._signal_handling()
|
||||
|
||||
try:
|
||||
log.info(f"Starting server on {host}:{port}")
|
||||
from gns3server.api.server import app
|
||||
|
||||
# only show uvicorn access logs in debug mode
|
||||
access_log = False
|
||||
|
@ -14,8 +14,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from jose import JWTError, jwt
|
||||
from joserfc import jwt
|
||||
from joserfc.jwk import OctKey
|
||||
from joserfc.errors import JoseError
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import bcrypt
|
||||
|
||||
@ -56,7 +57,8 @@ class AuthService:
|
||||
secret_key = DEFAULT_JWT_SECRET_KEY
|
||||
log.error("A JWT secret key must be configured to secure the server, using an unsecured default key!")
|
||||
algorithm = Config.instance().settings.Controller.jwt_algorithm
|
||||
encoded_jwt = jwt.encode(to_encode, secret_key, algorithm=algorithm)
|
||||
key = OctKey.import_key(secret_key)
|
||||
encoded_jwt = jwt.encode({"alg": algorithm}, to_encode, key)
|
||||
return encoded_jwt
|
||||
|
||||
def get_username_from_token(self, token: str, secret_key: str = None) -> Optional[str]:
|
||||
@ -73,11 +75,12 @@ class AuthService:
|
||||
secret_key = DEFAULT_JWT_SECRET_KEY
|
||||
log.error("A JWT secret key must be configured to secure the server, using an unsecured default key!")
|
||||
algorithm = Config.instance().settings.Controller.jwt_algorithm
|
||||
payload = jwt.decode(token, secret_key, algorithms=[algorithm])
|
||||
username: str = payload.get("sub")
|
||||
key = OctKey.import_key(secret_key)
|
||||
payload = jwt.decode(token, key, algorithms=[algorithm])
|
||||
username: str = payload.claims.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
except (JWTError, ValidationError):
|
||||
except (JoseError, ValidationError, ValueError):
|
||||
raise credentials_exception
|
||||
return token_data.username
|
||||
|
@ -46,6 +46,6 @@
|
||||
|
||||
gtag('config', 'G-0BT7QQV1W1');
|
||||
</script>
|
||||
<script src="runtime.24fa95b7061d7056.js" type="module"></script><script src="polyfills.319c79dd175e50d0.js" type="module"></script><script src="main.e55eeff5c0ba1cf4.js" type="module"></script>
|
||||
<script src="runtime.24fa95b7061d7056.js" type="module"></script><script src="polyfills.319c79dd175e50d0.js" type="module"></script><script src="main.2e807eb4bc32f838.js" type="module"></script>
|
||||
|
||||
</body></html>
|
1
gns3server/static/web-ui/main.2e807eb4bc32f838.js
Normal file
1
gns3server/static/web-ui/main.2e807eb4bc32f838.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -97,18 +97,10 @@ async def wait_for_process_termination(process, timeout=10):
|
||||
:param timeout: Timeout in seconds
|
||||
"""
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=timeout)
|
||||
except ProcessLookupError:
|
||||
return
|
||||
else:
|
||||
while timeout > 0:
|
||||
if process.returncode is not None:
|
||||
return
|
||||
await asyncio.sleep(0.1)
|
||||
timeout -= 0.1
|
||||
raise asyncio.TimeoutError()
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=timeout)
|
||||
except ProcessLookupError:
|
||||
return
|
||||
|
||||
|
||||
async def _check_process(process, termination_callback):
|
||||
|
@ -40,10 +40,7 @@ class Pool:
|
||||
while len(self._tasks) > 0 or len(pending) > 0:
|
||||
while len(self._tasks) > 0 and len(pending) < self._concurrency:
|
||||
task, args, kwargs = self._tasks.pop(0)
|
||||
if sys.version_info >= (3, 7):
|
||||
t = asyncio.create_task(task(*args, **kwargs))
|
||||
else:
|
||||
t = asyncio.get_event_loop().create_task(task(*args, **kwargs))
|
||||
t = asyncio.create_task(task(*args, **kwargs))
|
||||
pending.add(t)
|
||||
(done, pending) = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
|
||||
for task in done:
|
||||
|
@ -20,6 +20,11 @@ import stat
|
||||
import aiofiles
|
||||
import shutil
|
||||
|
||||
try:
|
||||
import importlib_resources
|
||||
except ImportError:
|
||||
from importlib import resources as importlib_resources
|
||||
|
||||
from typing import List, AsyncGenerator
|
||||
from ..config import Config
|
||||
from . import force_unix_path
|
||||
@ -111,6 +116,14 @@ async def list_images(image_type):
|
||||
return images
|
||||
|
||||
|
||||
def get_builtin_disks() -> List[str]:
|
||||
builtin_disks = []
|
||||
for entry in importlib_resources.files('gns3server').joinpath("disks").iterdir():
|
||||
if entry.is_file():
|
||||
builtin_disks.append(entry.name)
|
||||
return builtin_disks
|
||||
|
||||
|
||||
async def read_image_info(path: str, expected_image_type: str = None) -> dict:
|
||||
|
||||
header_magic_len = 7
|
||||
@ -118,7 +131,7 @@ async def read_image_info(path: str, expected_image_type: str = None) -> dict:
|
||||
async with aiofiles.open(path, "rb") as f:
|
||||
image_header = await f.read(header_magic_len) # read the first 7 bytes of the file
|
||||
if len(image_header) >= header_magic_len:
|
||||
detected_image_type = check_valid_image_header(image_header)
|
||||
detected_image_type = check_valid_image_header(path, image_header)
|
||||
if expected_image_type and detected_image_type != expected_image_type:
|
||||
raise InvalidImageError(f"Detected image type for '{path}' is {detected_image_type}, "
|
||||
f"expected type is {expected_image_type}")
|
||||
@ -302,7 +315,7 @@ class InvalidImageError(Exception):
|
||||
return self._message
|
||||
|
||||
|
||||
def check_valid_image_header(data: bytes, allow_raw_image: bool = False) -> str:
|
||||
def check_valid_image_header(path: str, data: bytes, allow_raw_image: bool = False) -> str:
|
||||
|
||||
if data[:7] == b'\x7fELF\x01\x02\x01':
|
||||
# for IOS images: file must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1
|
||||
@ -317,7 +330,7 @@ def check_valid_image_header(data: bytes, allow_raw_image: bool = False) -> str:
|
||||
else:
|
||||
if allow_raw_image is True:
|
||||
return "qemu"
|
||||
raise InvalidImageError("Could not detect image type, please make sure it is a valid image")
|
||||
raise InvalidImageError(f"{path}: could not detect image type, please make sure it is a valid image")
|
||||
|
||||
|
||||
async def write_image(
|
||||
@ -342,7 +355,7 @@ async def write_image(
|
||||
async for chunk in stream:
|
||||
if check_image_header and len(chunk) >= header_magic_len:
|
||||
check_image_header = False
|
||||
image_type = check_valid_image_header(chunk, allow_raw_image)
|
||||
image_type = check_valid_image_header(image_path, chunk, allow_raw_image)
|
||||
await f.write(chunk)
|
||||
checksum.update(chunk)
|
||||
|
||||
|
@ -22,8 +22,8 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "3.0.1"
|
||||
__version_info__ = (3, 0, 1, 0)
|
||||
__version__ = "3.0.3"
|
||||
__version_info__ = (3, 0, 3, 0)
|
||||
|
||||
if "dev" in __version__:
|
||||
try:
|
||||
|
@ -10,7 +10,7 @@ authors = [
|
||||
{ name = "Jeremy Grossmann", email = "developers@gns3.com" }
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.9"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
@ -21,11 +21,11 @@ classifiers = [
|
||||
"Natural Language :: English",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: Implementation :: CPython"
|
||||
]
|
||||
|
||||
|
@ -1,23 +1,24 @@
|
||||
uvicorn==0.32.0
|
||||
pydantic==2.9.2
|
||||
fastapi==0.115.5
|
||||
python-multipart==0.0.16
|
||||
websockets==13.1
|
||||
uvicorn==0.33.0 # uvicorn 0.33 is the last version supporting Python 3.8
|
||||
pydantic==2.10.4
|
||||
fastapi==0.115.6
|
||||
python-multipart==0.0.20
|
||||
websockets==14.1
|
||||
aiohttp>=3.10.10,<3.11
|
||||
async-timeout==4.0.3
|
||||
async-timeout==5.0.1; python_version < '3.11'
|
||||
aiofiles>=24.1.0,<25.0
|
||||
Jinja2>=3.1.4,<3.2
|
||||
sentry-sdk>=2.17,<2.18 # optional dependency
|
||||
psutil>=6.1.0
|
||||
Jinja2>=3.1.5,<3.2
|
||||
sentry-sdk>=2.19.2,<2.20 # optional dependency
|
||||
psutil>=6.1.1
|
||||
distro>=1.9.0
|
||||
py-cpuinfo>=9.0.0,<10.0
|
||||
greenlet==3.1.1 # necessary to run sqlalchemy on Python 3.13
|
||||
sqlalchemy==2.0.36
|
||||
aiosqlite==0.20.0
|
||||
alembic==1.13.3
|
||||
bcrypt==4.2.0
|
||||
python-jose[cryptography]==3.3.0
|
||||
alembic==1.14.0
|
||||
bcrypt==4.2.1
|
||||
joserfc==1.0.1
|
||||
email-validator==2.2.0
|
||||
watchfiles==0.24.0
|
||||
watchdog==6.0.0
|
||||
zstandard==0.23.0
|
||||
platformdirs>=2.4.0,<3 # platformdirs >=3 conflicts when building Debian packages
|
||||
importlib-resources>=1.3; python_version <= '3.9'
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2015 GNS3 Technologies Inc.
|
||||
# Copyright (C) 2024 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -16,19 +16,20 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#
|
||||
# Install GNS3 on a remote Ubuntu LTS server
|
||||
# This create a dedicated user and setup all the package
|
||||
# and optionnaly a VPN
|
||||
# Install GNS3 on a remote Ubuntu server
|
||||
# This creates a dedicated user and setup all the packages
|
||||
# and optionally a VPN
|
||||
#
|
||||
|
||||
function help {
|
||||
echo "Usage:" >&2
|
||||
echo "--with-openvpn: Install OpenVPN" >&2
|
||||
echo "--with-iou: Install IOU" >&2
|
||||
echo "--with-i386-repository: Add the i386 repositories required by IOU if they are not already available on the system. Warning: this will replace your source.list in order to use the official Ubuntu mirror" >&2
|
||||
echo "--with-iou: Install IOU support" >&2
|
||||
echo "--with-i386-repository: Add the i386 repositories required by IOU i386 images. This is not needed for recent x86_64 IOU images." >&2
|
||||
echo "--with-welcome: Install GNS3-VM welcome.py script" >&2
|
||||
echo "--without-kvm: Disable KVM, required if system do not support it (limitation in some hypervisors and cloud providers). Warning: only disable KVM if strictly necessary as this will degrade performance" >&2
|
||||
echo "--unstable: Use the GNS3 unstable repository"
|
||||
echo "--unstable: Use the GNS3 unstable repository" >&2
|
||||
echo "--custom-repository <repository>: Use a custom repository" >&2
|
||||
echo "--help: This help" >&2
|
||||
}
|
||||
|
||||
@ -43,15 +44,17 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Default repository
|
||||
REPOSITORY="ppa"
|
||||
|
||||
# Read the options
|
||||
USE_VPN=0
|
||||
USE_IOU=0
|
||||
I386_REPO=0
|
||||
DISABLE_KVM=0
|
||||
UNSTABLE=0
|
||||
WELCOME_SETUP=0
|
||||
|
||||
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,with-welcome,without-kvm,unstable,help -n 'gns3-remote-install.sh' -- "$@"`
|
||||
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,with-welcome,without-kvm,unstable,custom-repository:,help -n 'gns3-remote-install.sh' -- "$@"`
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
help
|
||||
@ -83,9 +86,13 @@ while true ; do
|
||||
shift
|
||||
;;
|
||||
--unstable)
|
||||
UNSTABLE=1
|
||||
REPOSITORY="unstable"
|
||||
shift
|
||||
;;
|
||||
--custom-repository)
|
||||
REPOSITORY="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
help
|
||||
exit 1
|
||||
@ -103,91 +110,93 @@ UBUNTU_CODENAME=`lsb_release -c -s`
|
||||
|
||||
log "Add GNS3 repository"
|
||||
|
||||
if [ "$UBUNTU_CODENAME" == "trusty" ]
|
||||
if [ ! -f "/etc/apt/sources.list.d/ubuntu.sources" ]
|
||||
then
|
||||
if [ $UNSTABLE == 1 ]
|
||||
then
|
||||
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
|
||||
deb http://ppa.launchpad.net/gns3/unstable/ubuntu $UBUNTU_CODENAME main
|
||||
deb-src http://ppa.launchpad.net/gns3/unstable/ubuntu $UBUNTU_CODENAME main
|
||||
deb http://ppa.launchpad.net/gns3/qemu/ubuntu $UBUNTU_CODENAME main
|
||||
deb-src http://ppa.launchpad.net/gns3/qemu/ubuntu $UBUNTU_CODENAME main
|
||||
EOFLIST
|
||||
else
|
||||
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B83AAABFFBD82D21B543C8EA86C22C2EC6A24D7F
|
||||
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
|
||||
deb http://ppa.launchpad.net/gns3/ppa/ubuntu $UBUNTU_CODENAME main
|
||||
deb-src http://ppa.launchpad.net/gns3/ppa/ubuntu $UBUNTU_CODENAME main
|
||||
deb http://ppa.launchpad.net/gns3/qemu/ubuntu $UBUNTU_CODENAME main
|
||||
deb-src http://ppa.launchpad.net/gns3/qemu/ubuntu $UBUNTU_CODENAME main
|
||||
EOFLIST
|
||||
fi
|
||||
|
||||
else
|
||||
if [ $UNSTABLE == 1 ]
|
||||
then
|
||||
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
|
||||
deb http://ppa.launchpad.net/gns3/unstable/ubuntu $UBUNTU_CODENAME main
|
||||
deb-src http://ppa.launchpad.net/gns3/unstable/ubuntu $UBUNTU_CODENAME main
|
||||
|
||||
cat <<EOFLIST > /etc/apt/sources.list.d/gns3-ppa.sources
|
||||
Types: deb
|
||||
URIs: https://ppa.launchpadcontent.net/gns3/$REPOSITORY/ubuntu/
|
||||
Suites: $UBUNTU_CODENAME
|
||||
Components: main
|
||||
Signed-By:
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
.
|
||||
mQINBGY0jSYBEADMH5CvX8ZVX4XzAxdQ2CmF7t86IjFnQgtI18Q19nVnpKEGNyB5
|
||||
pgotDMzkhGnxuhvz2zE9PZhd8VgkodB81V607d/Dy8FfI7t1BVQhLvJDx0H/q6RE
|
||||
n2y9WxiuBzTHitoQTCTY3hjcr7AUNFFI64gUqwbkQmYbCWWsYOlDpRSkWKg8P8WK
|
||||
08RetwTI0Iwoz8j+BkbPlubuImiVfh1TeH23FBuGIwL1r1Cps0wel6JAi+jaU9WG
|
||||
j8MX3mQYFTAtk7f1lRubqWosB/A4xIu609pF1e1tAkWAGltYAeoFhDn+PfA9KgmV
|
||||
fvxfVR7zmxp31imTJgXgUFCz+H0Xb3vpve8XsrsHZUP6StJ3+6cFXjNBV6PuO1FT
|
||||
JWp86a+AYHg7+sUWcoJRZPCTbb/pOcCa0q1ch5qcLkiYEOGK+pYhbPptq6y8IsJW
|
||||
N6EDNCVvVqVyTJy14FZWoOqxcpUiDOQ+su28j8++V+PMo+FO3SQqwEZwJXk7LF/4
|
||||
wUipDCUh/WNjDqqgmYLoO+ttiiJPbEw3jtbO+zopbzYpyEC1f06Nz7uz1daOIN3J
|
||||
etFPzSqWCE7Eq+hoVmAAm8gVmQir3rFJbIGBAvAaOLQEOkUlOlS7AezqUhdyhGER
|
||||
Zrvc3eNqxY7G61SEHipEJ7/hpcDq0RRWCXHsoQqyHaPje826n2pGkJYt4QARAQAB
|
||||
tBZMYXVuY2hwYWQgUFBBIGZvciBHTlMziQJOBBMBCgA4FiEEuDqqv/vYLSG1Q8jq
|
||||
hsIsLsaiTX8FAmY0jSYCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQhsIs
|
||||
LsaiTX9z9xAAq1uHmRgfYmELS0cr2YEnTWHPVE6s95Qx+0cr5zzNeWfmoAS9uSyl
|
||||
z8bCm+Q2ZapzU/nOtkozU+RGjgcRRTKMVTyS0PjFX22965xHCRWnw79fPyrYouUw
|
||||
H2cAT8WSGYEeVAbqhJSns0RnDpXuaxmWE1wT+iitY/QAjeXo22Z2mjv2bFTitKbY
|
||||
hZbE5Eu8Olc5YHCVI0ofq84/Ii921iMibU6EDMmm/iOnMK2uHGbC59t0YG8Rm7mK
|
||||
uk6+TpxOULjFeCWSkF2Dr33m8JQmtYZuFUnmqWPuSdBo3J0O1b0qTg+EP9FbDAtj
|
||||
CoEKT/V1ccMBd3r77o23CGsvpV7bzEU60A+NsU8vb/AkOmouYiF+qaYDFGZDfWhK
|
||||
p1HFmd1kt7YdgxsmoKoFJkbt1bBdcFJLV0Jcad5sfArg2aFDYf2giMxAw4iQ+9jc
|
||||
MCuwWxiqWicPqJ5erNTzVfayBkjuZqBDVTO9wmG3DL4QmNosIBS7kq+NGrT8Ql22
|
||||
FqYfdIZJDlKVtJKHK8eKJSB0dbFawV2h5p/CvQlIm6nthg5FzOyjvCkPkvxvveq+
|
||||
SuNxFEscumFCgo7j7RMWHW9HWK3TUvMmYLMVjxL8kXyCwknp9GklBQHA/IPxRa/2
|
||||
eFqqkmVbmNAoMzzw5wqa/BPcFEbgn+E+TFyZqbzp0F4QzPJZFkz16SA=
|
||||
=xnj5
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
EOFLIST
|
||||
else
|
||||
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
|
||||
deb http://ppa.launchpad.net/gns3/ppa/ubuntu $UBUNTU_CODENAME main
|
||||
deb-src http://ppa.launchpad.net/gns3/ppa/ubuntu $UBUNTU_CODENAME main
|
||||
EOFLIST
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ $I386_REPO == 1 ]
|
||||
then
|
||||
cat <<EOFLIST2 >> /etc/apt/sources.list
|
||||
###### Ubuntu Main Repos
|
||||
deb http://archive.ubuntu.com/ubuntu/ $UBUNTU_CODENAME main universe multiverse
|
||||
deb-src http://archive.ubuntu.com/ubuntu/ $UBUNTU_CODENAME main universe multiverse
|
||||
log "Updating system packages and installing curl"
|
||||
apt update
|
||||
apt install -y curl
|
||||
|
||||
###### Ubuntu Update Repos
|
||||
deb http://archive.ubuntu.com/ubuntu/ ${UBUNTU_CODENAME}-security main universe multiverse
|
||||
deb http://archive.ubuntu.com/ubuntu/ ${UBUNTU_CODENAME}-updates main universe multiverse
|
||||
deb-src http://archive.ubuntu.com/ubuntu/ ${UBUNTU_CODENAME}-security main universe multiverse
|
||||
deb-src http://archive.ubuntu.com/ubuntu/ ${UBUNTU_CODENAME}-updates main universe multiverse
|
||||
EOFLIST2
|
||||
fi
|
||||
log "Upgrading packages"
|
||||
apt upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
|
||||
|
||||
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys A2E3EF7B
|
||||
log "Installing the GNS3 server and its dependencies"
|
||||
apt install -y gns3-server
|
||||
|
||||
log "Update system packages"
|
||||
apt-get update
|
||||
|
||||
log "Upgrade packages"
|
||||
apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
|
||||
|
||||
log "Install GNS3 packages"
|
||||
apt-get install -y gns3-server
|
||||
|
||||
log "Create user GNS3 with /opt/gns3 as home directory"
|
||||
log "Creating user GNS3 with /opt/gns3 as home directory"
|
||||
if [ ! -d "/opt/gns3" ]
|
||||
then
|
||||
useradd -m -d /opt/gns3 gns3
|
||||
fi
|
||||
|
||||
|
||||
log "Add GNS3 to the ubridge group"
|
||||
log "Adding GNS3 to the ubridge group"
|
||||
usermod -aG ubridge gns3
|
||||
|
||||
log "Install docker"
|
||||
log "Installing Docker"
|
||||
if [ ! -f "/usr/bin/docker" ]
|
||||
then
|
||||
curl -sSL https://get.docker.com | bash
|
||||
fi
|
||||
|
||||
log "Add GNS3 to the docker group"
|
||||
log "Adding GNS3 to the docker group"
|
||||
usermod -aG docker gns3
|
||||
|
||||
if [ $USE_IOU == 1 ]
|
||||
then
|
||||
log "Setup IOU"
|
||||
dpkg --add-architecture i386
|
||||
apt-get update
|
||||
log "Setting up IOU support"
|
||||
if [ $I386_REPO == 1 ]
|
||||
then
|
||||
log "Enabling i386 architecture for IOU support"
|
||||
dpkg --add-architecture i386
|
||||
apt update
|
||||
fi
|
||||
|
||||
apt-get install -y gns3-iou
|
||||
apt install -y gns3-iou
|
||||
|
||||
# Force the host name to gns3vm
|
||||
echo gns3vm > /etc/hostname
|
||||
@ -196,31 +205,18 @@ then
|
||||
|
||||
# Force hostid for IOU
|
||||
dd if=/dev/zero bs=4 count=1 of=/etc/hostid
|
||||
|
||||
# Block potential IOU phone home call (xml.cisco.com is not in use at this time)
|
||||
log "Block IOU phone home call"
|
||||
if [ "$UBUNTU_CODENAME" == "focal" ]
|
||||
then
|
||||
iptables -I OUTPUT -p udp --dport 53 -m string --hex-string "|03|xml|05|cisco|03|com" --algo bm -j DROP
|
||||
echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections
|
||||
echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections
|
||||
apt-get install -y iptables-persistent
|
||||
else
|
||||
echo "127.0.0.254 xml.cisco.com" | tee --append /etc/hosts
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
log "Add gns3 to the kvm group"
|
||||
log "Adding gns3 to the kvm group"
|
||||
usermod -aG kvm gns3
|
||||
|
||||
log "Setup GNS3 server"
|
||||
log "Setting up the GNS3 server configuration"
|
||||
|
||||
mkdir -p /etc/gns3
|
||||
cat <<EOFC > /etc/gns3/gns3_server.conf
|
||||
[Server]
|
||||
host = 0.0.0.0
|
||||
port = 3080
|
||||
port = 3080
|
||||
images_path = /opt/gns3/images
|
||||
projects_path = /opt/gns3/projects
|
||||
appliances_path = /opt/gns3/appliances
|
||||
@ -234,52 +230,15 @@ EOFC
|
||||
|
||||
if [ $DISABLE_KVM == 1 ]
|
||||
then
|
||||
log "Disable KVM support"
|
||||
log "Disabling KVM support"
|
||||
sed -i 's/hardware_acceleration = True/hardware_acceleration = False/g' /etc/gns3/gns3_server.conf
|
||||
fi
|
||||
|
||||
chown -R gns3:gns3 /etc/gns3
|
||||
chmod -R 700 /etc/gns3
|
||||
|
||||
if [ "$UBUNTU_CODENAME" == "trusty" ]
|
||||
then
|
||||
cat <<EOFI > /etc/init/gns3.conf
|
||||
description "GNS3 server"
|
||||
author "GNS3 Team"
|
||||
|
||||
start on filesystem or runlevel [2345]
|
||||
stop on runlevel [016]
|
||||
respawn
|
||||
console log
|
||||
|
||||
|
||||
script
|
||||
exec start-stop-daemon --start --make-pidfile --pidfile /var/run/gns3.pid --chuid gns3 --exec "/usr/bin/gns3server"
|
||||
end script
|
||||
|
||||
pre-start script
|
||||
echo "" > /var/log/upstart/gns3.log
|
||||
echo "[`date`] GNS3 Starting"
|
||||
end script
|
||||
|
||||
pre-stop script
|
||||
echo "[`date`] GNS3 Stopping"
|
||||
end script
|
||||
EOFI
|
||||
|
||||
chown root:root /etc/init/gns3.conf
|
||||
chmod 644 /etc/init/gns3.conf
|
||||
|
||||
|
||||
log "Start GNS3 service"
|
||||
set +e
|
||||
service gns3 stop
|
||||
set -e
|
||||
service gns3 start
|
||||
|
||||
else
|
||||
# Install systemd service
|
||||
cat <<EOFI > /lib/systemd/system/gns3.service
|
||||
log "Installing the GNS3 systemd service"
|
||||
cat <<EOFI > /lib/systemd/system/gns3.service
|
||||
[Unit]
|
||||
Description=GNS3 server
|
||||
After=network-online.target
|
||||
@ -302,15 +261,15 @@ LimitNOFILE=16384
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOFI
|
||||
chmod 755 /lib/systemd/system/gns3.service
|
||||
chown root:root /lib/systemd/system/gns3.service
|
||||
|
||||
log "Start GNS3 service"
|
||||
systemctl enable gns3
|
||||
systemctl start gns3
|
||||
fi
|
||||
chmod 755 /lib/systemd/system/gns3.service
|
||||
chown root:root /lib/systemd/system/gns3.service
|
||||
|
||||
log "GNS3 installed with success"
|
||||
log "Starting the GNS3 service"
|
||||
systemctl enable gns3
|
||||
systemctl start gns3
|
||||
|
||||
log "GNS3 has been installed with success"
|
||||
|
||||
if [ $WELCOME_SETUP == 1 ]
|
||||
then
|
||||
@ -319,11 +278,9 @@ gns3 ALL = (ALL) NOPASSWD: /usr/bin/apt-key
|
||||
gns3 ALL = (ALL) NOPASSWD: /usr/bin/apt-get
|
||||
gns3 ALL = (ALL) NOPASSWD: /usr/sbin/reboot
|
||||
EOFI
|
||||
NEEDRESTART_MODE=a apt-get install -y net-tools
|
||||
NEEDRESTART_MODE=a apt-get install -y python3-pip
|
||||
NEEDRESTART_MODE=a apt-get install -y dialog
|
||||
pip install --no-input --upgrade pip
|
||||
pip install --no-input pythondialog
|
||||
NEEDRESTART_MODE=a apt install -y net-tools
|
||||
NEEDRESTART_MODE=a apt install -y dialog
|
||||
NEEDRESTART_MODE=a apt install -y python3-dialog
|
||||
|
||||
#Pull down welcome script from repo
|
||||
curl https://raw.githubusercontent.com/GNS3/gns3-server/master/scripts/welcome.py > /usr/local/bin/welcome.py
|
||||
@ -350,19 +307,15 @@ fi
|
||||
|
||||
if [ $USE_VPN == 1 ]
|
||||
then
|
||||
log "Setup VPN"
|
||||
log "Setting up OpenVPN"
|
||||
|
||||
log "Change GNS3 to listen on VPN interface"
|
||||
log "Changing the GNS3 server configuration to listen on VPN interface"
|
||||
|
||||
sed -i 's/host = 0.0.0.0/host = 172.16.253.1/' /etc/gns3/gns3_server.conf
|
||||
|
||||
log "Install packages for OpenVPN"
|
||||
log "Installing the OpenVPN packages"
|
||||
|
||||
apt-get install -y \
|
||||
openvpn \
|
||||
uuid \
|
||||
dnsutils \
|
||||
nginx-light
|
||||
apt install -y openvpn uuid dnsutils nginx-light
|
||||
|
||||
MY_IP_ADDR=$(dig @ns1.google.com -t txt o-o.myaddr.l.google.com +short -4 | sed 's/"//g')
|
||||
|
||||
@ -370,7 +323,7 @@ log "IP detected: $MY_IP_ADDR"
|
||||
|
||||
UUID=$(uuid)
|
||||
|
||||
log "Update motd"
|
||||
log "Updating motd"
|
||||
|
||||
cat <<EOFMOTD > /etc/update-motd.d/70-openvpn
|
||||
#!/bin/sh
|
||||
@ -381,7 +334,7 @@ echo "http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn"
|
||||
echo ""
|
||||
echo "And add it to your openvpn client."
|
||||
echo ""
|
||||
echo "apt-get remove nginx-light to disable the HTTP server."
|
||||
echo "apt remove nginx-light to disable the HTTP server."
|
||||
echo "And remove this file with rm /etc/update-motd.d/70-openvpn"
|
||||
EOFMOTD
|
||||
chmod 755 /etc/update-motd.d/70-openvpn
|
||||
@ -391,7 +344,7 @@ mkdir -p /etc/openvpn/
|
||||
[ -d /dev/net ] || mkdir -p /dev/net
|
||||
[ -c /dev/net/tun ] || mknod /dev/net/tun c 10 200
|
||||
|
||||
log "Create keys"
|
||||
log "Creating OpenVPN keys"
|
||||
|
||||
[ -f /etc/openvpn/dh.pem ] || openssl dhparam -out /etc/openvpn/dh.pem 2048
|
||||
[ -f /etc/openvpn/key.pem ] || openssl genrsa -out /etc/openvpn/key.pem 2048
|
||||
@ -399,7 +352,7 @@ chmod 600 /etc/openvpn/key.pem
|
||||
[ -f /etc/openvpn/csr.pem ] || openssl req -new -key /etc/openvpn/key.pem -out /etc/openvpn/csr.pem -subj /CN=OpenVPN/
|
||||
[ -f /etc/openvpn/cert.pem ] || openssl x509 -req -in /etc/openvpn/csr.pem -out /etc/openvpn/cert.pem -signkey /etc/openvpn/key.pem -days 24855
|
||||
|
||||
log "Create client configuration"
|
||||
log "Creating OpenVPN client configuration"
|
||||
cat <<EOFCLIENT > /root/client.ovpn
|
||||
client
|
||||
nobind
|
||||
@ -441,7 +394,7 @@ status openvpn-status-1194.log
|
||||
log-append /var/log/openvpn-udp1194.log
|
||||
EOFUDP
|
||||
|
||||
log "Setup HTTP server for serving client certificate"
|
||||
log "Setting up an HTTP server for serving client certificate"
|
||||
mkdir -p /usr/share/nginx/openvpn/$UUID
|
||||
cp /root/client.ovpn /usr/share/nginx/openvpn/$UUID/$HOSTNAME.ovpn
|
||||
touch /usr/share/nginx/openvpn/$UUID/index.html
|
||||
@ -458,7 +411,7 @@ EOFNGINX
|
||||
service nginx stop
|
||||
service nginx start
|
||||
|
||||
log "Restart OpenVPN and GNS3"
|
||||
log "Restarting OpenVPN and GNS3"
|
||||
|
||||
set +e
|
||||
service openvpn stop
|
||||
@ -466,15 +419,15 @@ service openvpn start
|
||||
service gns3 stop
|
||||
service gns3 start
|
||||
|
||||
log "Download http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn to setup your OpenVPN client after rebooting the server"
|
||||
log "Please download http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn to setup your OpenVPN client after rebooting the server"
|
||||
|
||||
fi
|
||||
|
||||
if [ $WELCOME_SETUP == 1 ]
|
||||
then
|
||||
NEEDRESTART_MODE=a apt-get update
|
||||
NEEDRESTART_MODE=a apt-get upgrade
|
||||
python3 -c 'import sys; sys.path.append("/usr/local/bin/"); import welcome; ws = welcome.Welcome_dialog(); ws.repair_remote_install()'
|
||||
cd /opt/gns3
|
||||
su gns3
|
||||
NEEDRESTART_MODE=a apt update
|
||||
NEEDRESTART_MODE=a apt upgrade
|
||||
python3 -c 'import sys; sys.path.append("/usr/local/bin/"); import welcome; ws = welcome.Welcome_dialog(); ws.repair_remote_install()'
|
||||
cd /opt/gns3
|
||||
su gns3
|
||||
fi
|
||||
|
@ -29,27 +29,29 @@ from gns3server.utils.path import get_default_project_directory
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_get(app: FastAPI, compute_client: AsyncClient, windows_platform) -> None:
|
||||
class TestCapabilitiesRoutes:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'],
|
||||
'version': __version__,
|
||||
'platform': sys.platform,
|
||||
'cpus': psutil.cpu_count(logical=True),
|
||||
'memory': psutil.virtual_memory().total,
|
||||
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
|
||||
}
|
||||
async def test_get(self, app: FastAPI, compute_client: AsyncClient, windows_platform) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'],
|
||||
'version': __version__,
|
||||
'platform': sys.platform,
|
||||
'cpus': psutil.cpu_count(logical=True),
|
||||
'memory': psutil.virtual_memory().total,
|
||||
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
|
||||
}
|
||||
|
||||
|
||||
async def test_get_on_gns3vm(app: FastAPI, compute_client: AsyncClient, on_gns3vm) -> None:
|
||||
async def test_get_on_gns3vm(self, app: FastAPI, compute_client: AsyncClient, on_gns3vm) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'],
|
||||
'version': __version__,
|
||||
'platform': sys.platform,
|
||||
'cpus': psutil.cpu_count(logical=True),
|
||||
'memory': psutil.virtual_memory().total,
|
||||
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
|
||||
}
|
||||
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'],
|
||||
'version': __version__,
|
||||
'platform': sys.platform,
|
||||
'cpus': psutil.cpu_count(logical=True),
|
||||
'memory': psutil.virtual_memory().total,
|
||||
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
|
||||
}
|
||||
|
@ -28,154 +28,193 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> dict:
|
||||
class TestCloudNodesRoutes:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id),
|
||||
json={"name": "Cloud 1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> dict:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id),
|
||||
json={"name": "Cloud 1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_cloud_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
async def test_cloud_create(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project
|
||||
) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id),
|
||||
json={"name": "Cloud 1"})
|
||||
assert response.status_code == 201
|
||||
assert response.json()["name"] == "Cloud 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id),
|
||||
json={"name": "Cloud 1"})
|
||||
assert response.status_code == 201
|
||||
assert response.json()["name"] == "Cloud 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_get_cloud(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
async def test_get_cloud(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "Cloud 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "started"
|
||||
|
||||
|
||||
async def test_cloud_nio_create_udp(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_cloud_nio_update_udp(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
params["filters"] = {}
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_cloud_delete_nio(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
url = app.url_path_for("compute:delete_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_cloud_delete(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_cloud_update(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_cloud", project_id=vm["project_id"], node_id=vm["node_id"]),
|
||||
json={"name": "test"})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
|
||||
|
||||
async def test_cloud_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_cloud_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"),
|
||||
json=params)
|
||||
response = await compute_client.get(app.url_path_for("compute:get_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
assert response.json()["name"] == "Cloud 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "started"
|
||||
|
||||
|
||||
async def test_cloud_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_cloud_nio_create_udp(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_cloud_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"))
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_cloud_nio_update_udp(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
params["filters"] = {}
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_cloud_delete_nio(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
url = app.url_path_for("compute:delete_cloud_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_cloud_pcap(compute_api, vm, compute_project):
|
||||
#
|
||||
# from itertools import repeat
|
||||
# stream = repeat(42, times=10)
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file", return_value=stream):
|
||||
# response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]))
|
||||
# assert response.status_code == 200
|
||||
#
|
||||
async def test_cloud_delete(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_cloud_update(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_cloud", project_id=vm["project_id"], node_id=vm["node_id"]),
|
||||
json={"name": "test"})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
|
||||
|
||||
async def test_cloud_start_capture(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_cloud_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"),
|
||||
json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_cloud_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_cloud_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_cloud_pcap(self, compute_api, vm, compute_project):
|
||||
#
|
||||
# from itertools import repeat
|
||||
# stream = repeat(42, times=10)
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file", return_value=stream):
|
||||
# response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]))
|
||||
# assert response.status_code == 200
|
||||
#
|
||||
|
@ -26,41 +26,48 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_udp_allocation(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
class TestComputeRoutes:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:allocate_udp_port", project_id=compute_project.id), json={})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()['udp_port'] is not None
|
||||
async def test_udp_allocation(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:allocate_udp_port", project_id=compute_project.id), json={})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()['udp_port'] is not None
|
||||
|
||||
|
||||
async def test_interfaces(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_interfaces(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:network_interfaces"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert isinstance(response.json(), list)
|
||||
response = await compute_client.get(app.url_path_for("compute:network_interfaces"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert isinstance(response.json(), list)
|
||||
|
||||
|
||||
async def test_version_output(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_version_output(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:compute_version"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'version': __version__}
|
||||
response = await compute_client.get(app.url_path_for("compute:compute_version"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'version': __version__}
|
||||
|
||||
|
||||
async def test_compute_authentication(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_compute_authentication(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:compute_version"), auth=("admin", "invalid_password"))
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
response = await compute_client.get(app.url_path_for("compute:compute_version"), auth=("admin", "invalid_password"))
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_debug_output(compute_api):
|
||||
#
|
||||
# response = await compute_api.get('/debug')
|
||||
# assert response.status_code == 200
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_debug_output(compute_api):
|
||||
#
|
||||
# response = await compute_api.get('/debug')
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
async def test_statistics_output(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_statistics_output(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:compute_statistics"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
response = await compute_client.get(app.url_path_for("compute:compute_statistics"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -25,285 +25,299 @@ from unittest.mock import patch
|
||||
|
||||
from gns3server.compute.project import Project
|
||||
|
||||
pytestmark = [pytest.mark.asyncio]
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base_params() -> dict:
|
||||
"""Return standard parameters"""
|
||||
class TestDockerNodesRoutes:
|
||||
|
||||
params = {
|
||||
"name": "DOCKER-TEST-1",
|
||||
"image": "nginx",
|
||||
"start_command": "nginx-daemon",
|
||||
"adapters": 2,
|
||||
"environment": "YES=1\nNO=0",
|
||||
"console_type": "telnet",
|
||||
"console_resolution": "1280x1024",
|
||||
"extra_hosts": "test:127.0.0.1"
|
||||
}
|
||||
return params
|
||||
@pytest.fixture
|
||||
def base_params(self) -> dict:
|
||||
"""Return standard parameters"""
|
||||
|
||||
params = {
|
||||
"name": "DOCKER-TEST-1",
|
||||
"image": "nginx",
|
||||
"start_command": "nginx-daemon",
|
||||
"adapters": 2,
|
||||
"environment": "YES=1\nNO=0",
|
||||
"console_type": "telnet",
|
||||
"console_resolution": "1280x1024",
|
||||
"extra_hosts": "test:127.0.0.1"
|
||||
}
|
||||
return params
|
||||
|
||||
|
||||
# @pytest.yield_fixture(autouse=True)
|
||||
# def mock_connection():
|
||||
#
|
||||
# docker = Docker.instance()
|
||||
# docker._connected = True
|
||||
# docker._connector = MagicMock()
|
||||
# yield
|
||||
# Docker._instance = None
|
||||
# @pytest.yield_fixture(autouse=True)
|
||||
# def mock_connection():
|
||||
#
|
||||
# docker = Docker.instance()
|
||||
# docker._connected = True
|
||||
# docker._connector = MagicMock()
|
||||
# yield
|
||||
# Docker._instance = None
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict
|
||||
) -> dict:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_docker_node", project_id=compute_project.id),
|
||||
json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_docker_node", project_id=compute_project.id),
|
||||
json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_docker_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None:
|
||||
async def test_docker_create(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict
|
||||
) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "DOCKER-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["container_id"] == "8bd8153ea8f5"
|
||||
assert response.json()["image"] == "nginx:latest"
|
||||
assert response.json()["adapters"] == 2
|
||||
assert response.json()["environment"] == "YES=1\nNO=0"
|
||||
assert response.json()["console_resolution"] == "1280x1024"
|
||||
assert response.json()["extra_hosts"] == "test:127.0.0.1"
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "DOCKER-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["container_id"] == "8bd8153ea8f5"
|
||||
assert response.json()["image"] == "nginx:latest"
|
||||
assert response.json()["adapters"] == 2
|
||||
assert response.json()["environment"] == "YES=1\nNO=0"
|
||||
assert response.json()["console_resolution"] == "1280x1024"
|
||||
assert response.json()["extra_hosts"] == "test:127.0.0.1"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name, status_code",
|
||||
(
|
||||
("valid-name.com", status.HTTP_201_CREATED),
|
||||
("42name", status.HTTP_201_CREATED),
|
||||
("424242", status.HTTP_409_CONFLICT),
|
||||
("name42", status.HTTP_201_CREATED),
|
||||
("name.424242", status.HTTP_409_CONFLICT),
|
||||
("-name", status.HTTP_409_CONFLICT),
|
||||
("name%-test", status.HTTP_409_CONFLICT),
|
||||
("x" * 63, status.HTTP_201_CREATED),
|
||||
("x" * 64, status.HTTP_409_CONFLICT),
|
||||
(("x" * 62 + ".") * 4, status.HTTP_201_CREATED),
|
||||
("xx" + ("x" * 62 + ".") * 4, status.HTTP_409_CONFLICT),
|
||||
),
|
||||
)
|
||||
async def test_docker_create_with_invalid_name(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict,
|
||||
name: str,
|
||||
status_code: int
|
||||
) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"name, status_code",
|
||||
(
|
||||
("valid-name.com", status.HTTP_201_CREATED),
|
||||
("42name", status.HTTP_201_CREATED),
|
||||
("424242", status.HTTP_409_CONFLICT),
|
||||
("name42", status.HTTP_201_CREATED),
|
||||
("name.424242", status.HTTP_409_CONFLICT),
|
||||
("-name", status.HTTP_409_CONFLICT),
|
||||
("name%-test", status.HTTP_409_CONFLICT),
|
||||
("x" * 63, status.HTTP_201_CREATED),
|
||||
("x" * 64, status.HTTP_409_CONFLICT),
|
||||
(("x" * 62 + ".") * 4, status.HTTP_201_CREATED),
|
||||
("xx" + ("x" * 62 + ".") * 4, status.HTTP_409_CONFLICT),
|
||||
),
|
||||
)
|
||||
async def test_docker_create_with_invalid_name(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict,
|
||||
name: str,
|
||||
status_code: int
|
||||
) -> None:
|
||||
|
||||
base_params["name"] = name
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params
|
||||
)
|
||||
assert response.status_code == status_code
|
||||
base_params["name"] = name
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params
|
||||
)
|
||||
assert response.status_code == status_code
|
||||
|
||||
|
||||
async def test_docker_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_docker_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock:
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:start_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_pause(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:pause_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_unpause(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:unpause_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_docker_update_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
url = app.url_path_for("compute:update_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"):
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_docker_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:delete_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"):
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port,
|
||||
"start_command": "yes",
|
||||
"environment": "GNS3=1\nGNS4=0",
|
||||
"extra_hosts": "test:127.0.0.1"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock:
|
||||
response = await compute_client.put(app.url_path_for("compute:update_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert mock.called
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
assert response.json()["start_command"] == "yes"
|
||||
assert response.json()["environment"] == "GNS3=1\nGNS4=0"
|
||||
assert response.json()["extra_hosts"] == "test:127.0.0.1"
|
||||
|
||||
|
||||
async def test_docker_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:start_docker_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock:
|
||||
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
response = await compute_client.post(app.url_path_for("compute:start_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_docker_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_docker_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_duplicate(app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None:
|
||||
async def test_docker_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
# create destination node first
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_docker_node",
|
||||
project_id=vm["project_id"]), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
response = await compute_client.post(app.url_path_for("compute:duplicate_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
async def test_docker_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_pause(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:pause_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_unpause(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:unpause_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_docker_update_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
url = app.url_path_for("compute:update_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"):
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_docker_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:delete_docker_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"):
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_docker_update(self, app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port,
|
||||
"start_command": "yes",
|
||||
"environment": "GNS3=1\nGNS4=0",
|
||||
"extra_hosts": "test:127.0.0.1"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock:
|
||||
response = await compute_client.put(app.url_path_for("compute:update_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert mock.called
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
assert response.json()["start_command"] == "yes"
|
||||
assert response.json()["environment"] == "GNS3=1\nGNS4=0"
|
||||
assert response.json()["extra_hosts"] == "test:127.0.0.1"
|
||||
|
||||
|
||||
async def test_docker_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:start_docker_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock:
|
||||
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_docker_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_docker_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
async def test_docker_duplicate(self, app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None:
|
||||
|
||||
# create destination node first
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_docker_node",
|
||||
project_id=vm["project_id"]), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
response = await compute_client.post(app.url_path_for("compute:duplicate_docker_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
@ -26,196 +26,208 @@ from httpx import AsyncClient
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
# @pytest.yield_fixture(scope="module")
|
||||
# async def vm(compute_api, compute_project, fake_image):
|
||||
#
|
||||
# dynamips_path = "/fake/dynamips"
|
||||
# params = {
|
||||
# "name": "My router",
|
||||
# "platform": "c3745",
|
||||
# "image": fake_image,
|
||||
# "ram": 128
|
||||
# }
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True) as mock:
|
||||
# response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params)
|
||||
# assert mock.called
|
||||
# assert response.status == 201
|
||||
#
|
||||
# #with asyncio_patch("gns3server.compute.dynamips.Dynamips.find_dynamips", return_value=dynamips_path):
|
||||
# # yield response.json
|
||||
|
||||
class TestDynamipsNodesRoutes:
|
||||
|
||||
# @pytest.yield_fixture(scope="module")
|
||||
# async def vm(compute_api, compute_project, fake_image):
|
||||
#
|
||||
# dynamips_path = "/fake/dynamips"
|
||||
# params = {
|
||||
# "name": "My router",
|
||||
# "platform": "c3745",
|
||||
# "image": fake_image,
|
||||
# "ram": 128
|
||||
# }
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True) as mock:
|
||||
# response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params)
|
||||
# assert mock.called
|
||||
# assert response.status == 201
|
||||
#
|
||||
# #with asyncio_patch("gns3server.compute.dynamips.Dynamips.find_dynamips", return_value=dynamips_path):
|
||||
# # yield response.json
|
||||
|
||||
|
||||
# async def test_dynamips_vm_create(compute_api, compute_project, fake_image):
|
||||
#
|
||||
# params = {
|
||||
# "name": "My router",
|
||||
# "platform": "c3745",
|
||||
# "image": os.path.basename(fake_image),
|
||||
# "ram": 128
|
||||
# }
|
||||
#
|
||||
# print(fake_image)
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True):
|
||||
# response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params)
|
||||
# assert response.status == 201
|
||||
# assert response.json["name"] == "My router"
|
||||
# assert response.json["project_id"] == compute_project.id
|
||||
# assert response.json["dynamips_id"]
|
||||
# async def test_dynamips_vm_create(compute_api, compute_project, fake_image):
|
||||
#
|
||||
# params = {
|
||||
# "name": "My router",
|
||||
# "platform": "c3745",
|
||||
# "image": os.path.basename(fake_image),
|
||||
# "ram": 128
|
||||
# }
|
||||
#
|
||||
# print(fake_image)
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True):
|
||||
# response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params)
|
||||
# assert response.status == 201
|
||||
# assert response.json["name"] == "My router"
|
||||
# assert response.json["project_id"] == compute_project.id
|
||||
# assert response.json["dynamips_id"]
|
||||
|
||||
|
||||
# def test_dynamips_vm_get(compute_api, project, vm):
|
||||
# response = compute_api.get("/projects/{project_id}/dynamips/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
|
||||
# assert response.status == 200
|
||||
# assert response.route == "/projects/{project_id}/dynamips/nodes/{node_id}"
|
||||
# assert response.json["name"] == "My router"
|
||||
# assert response.json["project_id"] == project.id
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_start(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.start", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_stop(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.stop", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_suspend(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.suspend", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_resume(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.resume", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
# def test_dynamips_vm_get(compute_api, project, vm):
|
||||
# response = compute_api.get("/projects/{project_id}/dynamips/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
|
||||
# assert response.status == 200
|
||||
# assert response.route == "/projects/{project_id}/dynamips/nodes/{node_id}"
|
||||
# assert response.json["name"] == "My router"
|
||||
# assert response.json["project_id"] == project.id
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_start(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.start", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_stop(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.stop", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_suspend(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.suspend", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
#
|
||||
#
|
||||
# def test_dynamips_vm_resume(compute_api, vm):
|
||||
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.resume", return_value=True) as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
# assert mock.called
|
||||
# assert response.status == 204
|
||||
|
||||
|
||||
# def test_vbox_nio_create_udp(compute_api, vm):
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"],
|
||||
# node_id=vm["node_id"]), {"type": "nio_udp",
|
||||
# "lport": 4242,
|
||||
# "rport": 4343,
|
||||
# "rhost": "127.0.0.1"},
|
||||
# example=True)
|
||||
#
|
||||
# assert mock.called
|
||||
# args, kwgars = mock.call_args
|
||||
# assert args[0] == 0
|
||||
#
|
||||
# assert response.status == 201
|
||||
# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio"
|
||||
# assert response.json["type"] == "nio_udp"
|
||||
#
|
||||
#
|
||||
# def test_vbox_delete_nio(compute_api, vm):
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
|
||||
# response = compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
|
||||
#
|
||||
# assert mock.called
|
||||
# args, kwgars = mock.call_args
|
||||
# assert args[0] == 0
|
||||
#
|
||||
# assert response.status == 204
|
||||
# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio"
|
||||
#
|
||||
#
|
||||
# def test_vbox_update(compute_api, vm, free_console_port):
|
||||
# response = compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
|
||||
# "console": free_console_port})
|
||||
# assert response.status == 200
|
||||
# assert response.json["name"] == "test"
|
||||
# assert response.json["console"] == free_console_port
|
||||
# def test_vbox_nio_create_udp(compute_api, vm):
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
|
||||
# response = compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"],
|
||||
# node_id=vm["node_id"]), {"type": "nio_udp",
|
||||
# "lport": 4242,
|
||||
# "rport": 4343,
|
||||
# "rhost": "127.0.0.1"},
|
||||
# example=True)
|
||||
#
|
||||
# assert mock.called
|
||||
# args, kwgars = mock.call_args
|
||||
# assert args[0] == 0
|
||||
#
|
||||
# assert response.status == 201
|
||||
# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio"
|
||||
# assert response.json["type"] == "nio_udp"
|
||||
#
|
||||
#
|
||||
# def test_vbox_delete_nio(compute_api, vm):
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
|
||||
# response = compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
|
||||
#
|
||||
# assert mock.called
|
||||
# args, kwgars = mock.call_args
|
||||
# assert args[0] == 0
|
||||
#
|
||||
# assert response.status == 204
|
||||
# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio"
|
||||
#
|
||||
#
|
||||
# def test_vbox_update(compute_api, vm, free_console_port):
|
||||
# response = compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
|
||||
# "console": free_console_port})
|
||||
# assert response.status == 200
|
||||
# assert response.json["name"] == "test"
|
||||
# assert response.json["console"] == free_console_port
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_image(tmpdir) -> str:
|
||||
"""Create a fake Dynamips image on disk"""
|
||||
@pytest.fixture
|
||||
def fake_image(self, tmpdir) -> str:
|
||||
"""Create a fake Dynamips image on disk"""
|
||||
|
||||
path = str(tmpdir / "7200.bin")
|
||||
with open(path, "wb+") as f:
|
||||
f.write(b'\x7fELF\x01\x02\x01')
|
||||
os.chmod(path, stat.S_IREAD)
|
||||
return path
|
||||
path = str(tmpdir / "7200.bin")
|
||||
with open(path, "wb+") as f:
|
||||
f.write(b'\x7fELF\x01\x02\x01')
|
||||
os.chmod(path, stat.S_IREAD)
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_file(tmpdir) -> str:
|
||||
"""Create a fake file disk"""
|
||||
@pytest.fixture
|
||||
def fake_file(self, tmpdir) -> str:
|
||||
"""Create a fake file disk"""
|
||||
|
||||
path = str(tmpdir / "7200.txt")
|
||||
with open(path, "w+") as f:
|
||||
f.write('1')
|
||||
os.chmod(path, stat.S_IREAD)
|
||||
return path
|
||||
path = str(tmpdir / "7200.txt")
|
||||
with open(path, "w+") as f:
|
||||
f.write('1')
|
||||
os.chmod(path, stat.S_IREAD)
|
||||
return path
|
||||
|
||||
|
||||
async def test_images(app: FastAPI, compute_client: AsyncClient, tmpdir, fake_image: str, fake_file: str) -> None:
|
||||
async def test_images(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
tmpdir, fake_image: str,
|
||||
fake_file: str
|
||||
) -> None:
|
||||
|
||||
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)):
|
||||
response = await compute_client.get(app.url_path_for("compute:get_dynamips_images"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == [{"filename": "7200.bin",
|
||||
"path": "7200.bin",
|
||||
"filesize": 7,
|
||||
"md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}]
|
||||
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)):
|
||||
response = await compute_client.get(app.url_path_for("compute:get_dynamips_images"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == [{"filename": "7200.bin",
|
||||
"path": "7200.bin",
|
||||
"filesize": 7,
|
||||
"md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}]
|
||||
|
||||
|
||||
async def test_upload_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
async def test_upload_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
with open(os.path.join(images_dir, "IOS", "test2")) as f:
|
||||
assert f.read() == "TEST"
|
||||
with open(os.path.join(images_dir, "IOS", "test2")) as f:
|
||||
assert f.read() == "TEST"
|
||||
|
||||
with open(os.path.join(images_dir, "IOS", "test2.md5sum")) as f:
|
||||
checksum = f.read()
|
||||
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
|
||||
with open(os.path.join(images_dir, "IOS", "test2.md5sum")) as f:
|
||||
checksum = f.read()
|
||||
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
|
||||
|
||||
|
||||
async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_upload_image_forbidden_location(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
file_path = "%2e%2e/hello"
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
file_path = "%2e%2e/hello"
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
async def test_download_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_download_image_forbidden(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None:
|
||||
async def test_download_image_forbidden(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None:
|
||||
|
||||
file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
|
||||
response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename=file_path))
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
|
||||
response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename=file_path))
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.getuid() == 0, reason="Root can delete any image")
|
||||
async def test_upload_image_permission_denied(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
@pytest.mark.skipif(os.getuid() == 0, reason="Root can delete any image")
|
||||
async def test_upload_image_permission_denied(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
images_dir: str
|
||||
) -> None:
|
||||
|
||||
os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True)
|
||||
with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f:
|
||||
f.write("")
|
||||
os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0)
|
||||
os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True)
|
||||
with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f:
|
||||
f.write("")
|
||||
os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0)
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
|
@ -28,427 +28,474 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def ethernet_switch(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> dict:
|
||||
class TestEthernetSwitchNodesRoutes:
|
||||
|
||||
params = {"name": "Ethernet Switch"}
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock:
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id),
|
||||
json=params
|
||||
@pytest_asyncio.fixture
|
||||
async def ethernet_switch(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> dict:
|
||||
|
||||
params = {"name": "Ethernet Switch"}
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock:
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id),
|
||||
json=params
|
||||
)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
json_response = response.json()
|
||||
node = compute_project.get_node(json_response["node_id"])
|
||||
node._hypervisor = AsyncioMagicMock()
|
||||
node._hypervisor.send = AsyncioMagicMock()
|
||||
node._hypervisor.version = "0.2.16"
|
||||
return json_response
|
||||
|
||||
|
||||
async def test_ethernet_switch_create(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project
|
||||
) -> None:
|
||||
|
||||
params = {"name": "Ethernet Switch 1"}
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock:
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id),
|
||||
json=params
|
||||
)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "Ethernet Switch 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_ethernet_switch_get(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(
|
||||
app.url_path_for(
|
||||
"compute:get_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]
|
||||
)
|
||||
)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
json_response = response.json()
|
||||
node = compute_project.get_node(json_response["node_id"])
|
||||
node._hypervisor = AsyncioMagicMock()
|
||||
node._hypervisor.send = AsyncioMagicMock()
|
||||
node._hypervisor.version = "0.2.16"
|
||||
return json_response
|
||||
|
||||
|
||||
async def test_ethernet_switch_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {"name": "Ethernet Switch 1"}
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock:
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id),
|
||||
json=params
|
||||
)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "Ethernet Switch 1"
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "Ethernet Switch"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "started"
|
||||
|
||||
|
||||
async def test_ethernet_switch_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, ethernet_switch: dict) -> None:
|
||||
async def test_ethernet_switch_duplicate(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(
|
||||
app.url_path_for(
|
||||
"compute:get_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "Ethernet Switch"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "started"
|
||||
# create destination switch first
|
||||
params = {"name": "Ethernet Switch 2"}
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock:
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:create_ethernet_switch",
|
||||
project_id=compute_project.id),
|
||||
json=params
|
||||
)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_ethernet_switch_duplicate(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
# create destination switch first
|
||||
params = {"name": "Ethernet Switch 2"}
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock:
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:create_ethernet_switch",
|
||||
project_id=compute_project.id),
|
||||
json=params
|
||||
"compute:duplicate_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]), json=params
|
||||
)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:duplicate_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]), json=params
|
||||
|
||||
async def test_ethernet_switch_update(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console_type": "telnet"
|
||||
}
|
||||
|
||||
response = await compute_client.put(
|
||||
app.url_path_for(
|
||||
"compute:update_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]),
|
||||
json=params
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
node._hypervisor.send.assert_called_with("ethsw rename \"Ethernet Switch\" \"test\"")
|
||||
|
||||
|
||||
async def test_ethernet_switch_update_ports(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
port_params = {
|
||||
"ports_mapping": [
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "qinq",
|
||||
"vlan": 1
|
||||
},
|
||||
{
|
||||
"name": "Ethernet1",
|
||||
"port_number": 1,
|
||||
"type": "qinq",
|
||||
"vlan": 2,
|
||||
"ethertype": "0x88A8"
|
||||
},
|
||||
{
|
||||
"name": "Ethernet2",
|
||||
"port_number": 2,
|
||||
"type": "dot1q",
|
||||
"vlan": 3,
|
||||
},
|
||||
{
|
||||
"name": "Ethernet3",
|
||||
"port_number": 3,
|
||||
"type": "access",
|
||||
"vlan": 4,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
response = await compute_client.put(
|
||||
app.url_path_for(
|
||||
"compute:update_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]),
|
||||
json=port_params
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
nio_params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
for port_mapping in port_params["ports_mapping"]:
|
||||
port_number = port_mapping["port_number"]
|
||||
vlan = port_mapping["vlan"]
|
||||
port_type = port_mapping["type"]
|
||||
ethertype = port_mapping.get("ethertype", "")
|
||||
url = app.url_path_for(
|
||||
"compute:create_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number=f"{port_number}"
|
||||
)
|
||||
await compute_client.post(url, json=nio_params)
|
||||
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
nio = node.get_nio(port_number)
|
||||
calls = [
|
||||
call.send(f'nio create_udp {nio.name} 4242 127.0.0.1 4343'),
|
||||
call.send(f'ethsw add_nio "Ethernet Switch" {nio.name}'),
|
||||
call.send(f'ethsw set_{port_type}_port "Ethernet Switch" {nio.name} {vlan} {ethertype}'.strip())
|
||||
]
|
||||
node._hypervisor.send.assert_has_calls(calls)
|
||||
node._hypervisor.send.reset_mock()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ports_settings",
|
||||
(
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "dot42q", # invalid port type
|
||||
"vlan": 1,
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "access", # missing vlan field
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "dot1q",
|
||||
"vlan": 1,
|
||||
"ethertype": "0x88A8" # EtherType is only for QinQ
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "qinq",
|
||||
"vlan": 1,
|
||||
"ethertype": "0x4242" # not a valid EtherType
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "access",
|
||||
"vlan": 0, # minimum vlan number is 1
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "access",
|
||||
"vlan": 4242, # maximum vlan number is 4094
|
||||
}
|
||||
),
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
async def test_ethernet_switch_update_ports_invalid(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict,
|
||||
ports_settings: dict,
|
||||
) -> None:
|
||||
|
||||
port_params = {
|
||||
"ports_mapping": [ports_settings]
|
||||
}
|
||||
|
||||
response = await compute_client.put(
|
||||
app.url_path_for(
|
||||
"compute:update_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]),
|
||||
json=port_params
|
||||
)
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
|
||||
async def test_ethernet_switch_update(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
async def test_ethernet_switch_delete(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console_type": "telnet"
|
||||
}
|
||||
|
||||
response = await compute_client.put(
|
||||
app.url_path_for(
|
||||
"compute:update_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]),
|
||||
json=params
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
node._hypervisor.send.assert_called_with("ethsw rename \"Ethernet Switch\" \"test\"")
|
||||
response = await compute_client.delete(
|
||||
app.url_path_for(
|
||||
"compute:delete_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_update_ports(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
async def test_ethernet_switch_start(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
port_params = {
|
||||
"ports_mapping": [
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "qinq",
|
||||
"vlan": 1
|
||||
},
|
||||
{
|
||||
"name": "Ethernet1",
|
||||
"port_number": 1,
|
||||
"type": "qinq",
|
||||
"vlan": 2,
|
||||
"ethertype": "0x88A8"
|
||||
},
|
||||
{
|
||||
"name": "Ethernet2",
|
||||
"port_number": 2,
|
||||
"type": "dot1q",
|
||||
"vlan": 3,
|
||||
},
|
||||
{
|
||||
"name": "Ethernet3",
|
||||
"port_number": 3,
|
||||
"type": "access",
|
||||
"vlan": 4,
|
||||
}
|
||||
],
|
||||
}
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:start_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
response = await compute_client.put(
|
||||
app.url_path_for(
|
||||
"compute:update_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]),
|
||||
json=port_params
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
nio_params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
async def test_ethernet_switch_stop(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:stop_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_suspend(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:suspend_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_reload(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:reload_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_create_udp(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
for port_mapping in port_params["ports_mapping"]:
|
||||
port_number = port_mapping["port_number"]
|
||||
vlan = port_mapping["vlan"]
|
||||
port_type = port_mapping["type"]
|
||||
ethertype = port_mapping.get("ethertype", "")
|
||||
url = app.url_path_for(
|
||||
"compute:create_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number=f"{port_number}"
|
||||
port_number="0"
|
||||
)
|
||||
await compute_client.post(url, json=nio_params)
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
nio = node.get_nio(port_number)
|
||||
nio = node.get_nio(0)
|
||||
calls = [
|
||||
call.send(f'nio create_udp {nio.name} 4242 127.0.0.1 4343'),
|
||||
call.send(f'ethsw add_nio "Ethernet Switch" {nio.name}'),
|
||||
call.send(f'ethsw set_{port_type}_port "Ethernet Switch" {nio.name} {vlan} {ethertype}'.strip())
|
||||
call.send(f'ethsw set_access_port "Ethernet Switch" {nio.name} 1')
|
||||
]
|
||||
node._hypervisor.send.assert_has_calls(calls)
|
||||
node._hypervisor.send.reset_mock()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ports_settings",
|
||||
(
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "dot42q", # invalid port type
|
||||
"vlan": 1,
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "access", # missing vlan field
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "dot1q",
|
||||
"vlan": 1,
|
||||
"ethertype": "0x88A8" # EtherType is only for QinQ
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "qinq",
|
||||
"vlan": 1,
|
||||
"ethertype": "0x4242" # not a valid EtherType
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "access",
|
||||
"vlan": 0, # minimum vlan number is 1
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"name": "Ethernet0",
|
||||
"port_number": 0,
|
||||
"type": "access",
|
||||
"vlan": 4242, # maximum vlan number is 4094
|
||||
}
|
||||
),
|
||||
)
|
||||
)
|
||||
async def test_ethernet_switch_update_ports_invalid(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict,
|
||||
ports_settings: dict,
|
||||
) -> None:
|
||||
async def test_ethernet_switch_delete_nio(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
port_params = {
|
||||
"ports_mapping": [ports_settings]
|
||||
}
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
response = await compute_client.put(
|
||||
app.url_path_for(
|
||||
"compute:update_ethernet_switch",
|
||||
url = app.url_path_for(
|
||||
"compute:create_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]),
|
||||
json=port_params
|
||||
)
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
|
||||
async def test_ethernet_switch_delete(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
response = await compute_client.delete(
|
||||
app.url_path_for(
|
||||
"compute:delete_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"]
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
node._hypervisor.send.reset_mock()
|
||||
nio = node.get_nio(0)
|
||||
|
||||
async def test_ethernet_switch_start(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:start_ethernet_switch",
|
||||
url = app.url_path_for(
|
||||
"compute:delete_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_stop(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:stop_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_suspend(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:suspend_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_reload(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
response = await compute_client.post(
|
||||
app.url_path_for(
|
||||
"compute:reload_ethernet_switch",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"])
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_ethernet_switch_create_udp(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for(
|
||||
"compute:create_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"
|
||||
)
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
nio = node.get_nio(0)
|
||||
calls = [
|
||||
call.send(f'nio create_udp {nio.name} 4242 127.0.0.1 4343'),
|
||||
call.send(f'ethsw add_nio "Ethernet Switch" {nio.name}'),
|
||||
call.send(f'ethsw set_access_port "Ethernet Switch" {nio.name} 1')
|
||||
]
|
||||
node._hypervisor.send.assert_has_calls(calls)
|
||||
|
||||
|
||||
async def test_ethernet_switch_delete_nio(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for(
|
||||
"compute:create_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"
|
||||
)
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
node = compute_project.get_node(ethernet_switch["node_id"])
|
||||
node._hypervisor.send.reset_mock()
|
||||
nio = node.get_nio(0)
|
||||
|
||||
url = app.url_path_for(
|
||||
"compute:delete_ethernet_switch_nio",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"
|
||||
)
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
calls = [
|
||||
call(f'ethsw remove_nio "Ethernet Switch" {nio.name}'),
|
||||
call(f'nio delete {nio.name}')
|
||||
]
|
||||
node._hypervisor.send.assert_has_calls(calls)
|
||||
|
||||
|
||||
async def test_ethernet_switch_start_capture(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_ethernet_switch_capture",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_ethernet_switch_stop_capture(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_ethernet_switch_capture",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0"
|
||||
)
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
calls = [
|
||||
call(f'ethsw remove_nio "Ethernet Switch" {nio.name}'),
|
||||
call(f'nio delete {nio.name}')
|
||||
]
|
||||
node._hypervisor.send.assert_has_calls(calls)
|
||||
|
||||
|
||||
async def test_ethernet_switch_start_capture(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_ethernet_switch_capture",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_ethernet_switch_stop_capture(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
ethernet_switch: dict
|
||||
) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_ethernet_switch_capture",
|
||||
project_id=ethernet_switch["project_id"],
|
||||
node_id=ethernet_switch["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
@ -28,452 +28,502 @@ from unittest.mock import patch
|
||||
|
||||
from gns3server.compute.project import Project
|
||||
|
||||
pytestmark = [pytest.mark.asyncio]
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_iou_bin(images_dir) -> str:
|
||||
"""Create a fake IOU image on disk"""
|
||||
class TestIOUNodesRoutes:
|
||||
|
||||
path = os.path.join(images_dir, "IOU", "iou.bin")
|
||||
with open(path, "w+") as f:
|
||||
f.write('\x7fELF\x01\x01\x01')
|
||||
os.chmod(path, stat.S_IREAD | stat.S_IEXEC)
|
||||
return path
|
||||
@pytest.fixture
|
||||
def fake_iou_bin(self, images_dir) -> str:
|
||||
"""Create a fake IOU image on disk"""
|
||||
|
||||
path = os.path.join(images_dir, "IOU", "iou.bin")
|
||||
with open(path, "w+") as f:
|
||||
f.write('\x7fELF\x01\x01\x01')
|
||||
os.chmod(path, stat.S_IREAD | stat.S_IEXEC)
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base_params(tmpdir, fake_iou_bin) -> dict:
|
||||
"""Return standard parameters"""
|
||||
@pytest.fixture
|
||||
def base_params(self, tmpdir, fake_iou_bin) -> dict:
|
||||
"""Return standard parameters"""
|
||||
|
||||
return {"application_id": 42, "name": "IOU-TEST-1", "path": "iou.bin"}
|
||||
return {"application_id": 42, "name": "IOU-TEST-1", "path": "iou.bin"}
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict
|
||||
) -> dict:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
def startup_config_file(compute_project: Project, vm: dict) -> str:
|
||||
def startup_config_file(self, compute_project: Project, vm: dict) -> str:
|
||||
|
||||
directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"])
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
return os.path.join(directory, "startup-config.cfg")
|
||||
directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"])
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
return os.path.join(directory, "startup-config.cfg")
|
||||
|
||||
|
||||
async def test_iou_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None:
|
||||
async def test_iou_create(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "IOU-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["serial_adapters"] == 2
|
||||
assert response.json()["ethernet_adapters"] == 2
|
||||
assert response.json()["ram"] == 256
|
||||
assert response.json()["nvram"] == 128
|
||||
assert response.json()["l1_keepalives"] is False
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "IOU-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["serial_adapters"] == 2
|
||||
assert response.json()["ethernet_adapters"] == 2
|
||||
assert response.json()["ram"] == 256
|
||||
assert response.json()["nvram"] == 128
|
||||
assert response.json()["l1_keepalives"] is False
|
||||
|
||||
|
||||
async def test_iou_create_with_params(app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict) -> None:
|
||||
async def test_iou_create_with_params(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict
|
||||
) -> None:
|
||||
|
||||
params = base_params
|
||||
params["ram"] = 1024
|
||||
params["nvram"] = 512
|
||||
params["serial_adapters"] = 4
|
||||
params["ethernet_adapters"] = 0
|
||||
params["l1_keepalives"] = True
|
||||
params["startup_config_content"] = "hostname test"
|
||||
params["use_default_iou_values"] = False
|
||||
params = base_params
|
||||
params["ram"] = 1024
|
||||
params["nvram"] = 512
|
||||
params["serial_adapters"] = 4
|
||||
params["ethernet_adapters"] = 0
|
||||
params["l1_keepalives"] = True
|
||||
params["startup_config_content"] = "hostname test"
|
||||
params["use_default_iou_values"] = False
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "IOU-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["serial_adapters"] == 4
|
||||
assert response.json()["ethernet_adapters"] == 0
|
||||
assert response.json()["ram"] == 1024
|
||||
assert response.json()["nvram"] == 512
|
||||
assert response.json()["l1_keepalives"] is True
|
||||
assert response.json()["use_default_iou_values"] is False
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "IOU-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["serial_adapters"] == 4
|
||||
assert response.json()["ethernet_adapters"] == 0
|
||||
assert response.json()["ram"] == 1024
|
||||
assert response.json()["nvram"] == 512
|
||||
assert response.json()["l1_keepalives"] is True
|
||||
assert response.json()["use_default_iou_values"] is False
|
||||
|
||||
with open(startup_config_file(compute_project, response.json())) as f:
|
||||
assert f.read() == "hostname test"
|
||||
with open(self.startup_config_file(compute_project, response.json())) as f:
|
||||
assert f.read() == "hostname test"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name, status_code",
|
||||
(
|
||||
("valid-name", status.HTTP_201_CREATED),
|
||||
("42name", status.HTTP_409_CONFLICT),
|
||||
("name42", status.HTTP_201_CREATED),
|
||||
("-name", status.HTTP_409_CONFLICT),
|
||||
("name%-test", status.HTTP_409_CONFLICT),
|
||||
("x" * 63, status.HTTP_201_CREATED),
|
||||
("x" * 64, status.HTTP_409_CONFLICT),
|
||||
),
|
||||
)
|
||||
async def test_iou_create_with_invalid_name(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict,
|
||||
name: str,
|
||||
status_code: int
|
||||
) -> None:
|
||||
|
||||
base_params["name"] = name
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params
|
||||
@pytest.mark.parametrize(
|
||||
"name, status_code",
|
||||
(
|
||||
("valid-name", status.HTTP_201_CREATED),
|
||||
("42name", status.HTTP_409_CONFLICT),
|
||||
("name42", status.HTTP_201_CREATED),
|
||||
("-name", status.HTTP_409_CONFLICT),
|
||||
("name%-test", status.HTTP_409_CONFLICT),
|
||||
("x" * 63, status.HTTP_201_CREATED),
|
||||
("x" * 64, status.HTTP_409_CONFLICT),
|
||||
),
|
||||
)
|
||||
assert response.status_code == status_code
|
||||
async def test_iou_create_with_invalid_name(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict,
|
||||
name: str,
|
||||
status_code: int
|
||||
) -> None:
|
||||
|
||||
base_params["name"] = name
|
||||
response = await compute_client.post(
|
||||
app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params
|
||||
)
|
||||
assert response.status_code == status_code
|
||||
|
||||
|
||||
async def test_iou_create_startup_config_already_exist(
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict) -> None:
|
||||
"""We don't erase a startup-config if already exist at project creation"""
|
||||
async def test_iou_create_startup_config_already_exist(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
base_params: dict) -> None:
|
||||
"""We don't erase a startup-config if already exist at project creation"""
|
||||
|
||||
node_id = str(uuid.uuid4())
|
||||
startup_config_file_path = startup_config_file(compute_project, {'node_id': node_id})
|
||||
with open(startup_config_file_path, 'w+') as f:
|
||||
f.write("echo hello")
|
||||
node_id = str(uuid.uuid4())
|
||||
startup_config_file_path = self.startup_config_file(compute_project, {'node_id': node_id})
|
||||
with open(startup_config_file_path, 'w+') as f:
|
||||
f.write("echo hello")
|
||||
|
||||
params = base_params
|
||||
params["node_id"] = node_id
|
||||
params["startup_config_content"] = "hostname test"
|
||||
params = base_params
|
||||
params["node_id"] = node_id
|
||||
params["startup_config_content"] = "hostname test"
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
with open(startup_config_file(compute_project, response.json())) as f:
|
||||
assert f.read() == "echo hello"
|
||||
with open(self.startup_config_file(compute_project, response.json())) as f:
|
||||
assert f.read() == "echo hello"
|
||||
|
||||
|
||||
async def test_iou_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
async def test_iou_get(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_iou_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "IOU-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["serial_adapters"] == 2
|
||||
assert response.json()["ethernet_adapters"] == 2
|
||||
assert response.json()["ram"] == 256
|
||||
assert response.json()["nvram"] == 128
|
||||
assert response.json()["l1_keepalives"] is False
|
||||
response = await compute_client.get(app.url_path_for("compute:get_iou_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "IOU-TEST-1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["serial_adapters"] == 2
|
||||
assert response.json()["ethernet_adapters"] == 2
|
||||
assert response.json()["ram"] == 256
|
||||
assert response.json()["nvram"] == 128
|
||||
assert response.json()["l1_keepalives"] is False
|
||||
|
||||
|
||||
async def test_iou_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json={})
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json={})
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_iou_start_with_iourc(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_start_with_iourc(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {"iourc_content": "test"}
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
params = {"iourc_content": "test"}
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_iou_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_iou_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_iou_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_iou_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
|
||||
async def test_iou_update(
|
||||
self, app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
free_console_port: int
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port,
|
||||
"ram": 512,
|
||||
"nvram": 2048,
|
||||
"ethernet_adapters": 4,
|
||||
"serial_adapters": 0,
|
||||
"l1_keepalives": True,
|
||||
"use_default_iou_values": True,
|
||||
}
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port,
|
||||
"ram": 512,
|
||||
"nvram": 2048,
|
||||
"ethernet_adapters": 4,
|
||||
"serial_adapters": 0,
|
||||
"l1_keepalives": True,
|
||||
"use_default_iou_values": True,
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
assert response.json()["ethernet_adapters"] == 4
|
||||
assert response.json()["serial_adapters"] == 0
|
||||
assert response.json()["ram"] == 512
|
||||
assert response.json()["nvram"] == 2048
|
||||
assert response.json()["l1_keepalives"] is True
|
||||
assert response.json()["use_default_iou_values"] is True
|
||||
response = await compute_client.put(app.url_path_for("compute:update_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
assert response.json()["ethernet_adapters"] == 4
|
||||
assert response.json()["serial_adapters"] == 0
|
||||
assert response.json()["ram"] == 512
|
||||
assert response.json()["nvram"] == 2048
|
||||
assert response.json()["l1_keepalives"] is True
|
||||
assert response.json()["use_default_iou_values"] is True
|
||||
|
||||
|
||||
async def test_iou_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_iou_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
await compute_client.post(url, json=params)
|
||||
params["filters"] = {}
|
||||
|
||||
url = app.url_path_for("compute:update_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_iou_nio_create_ethernet(app: FastAPI, compute_client: AsyncClient, vm: dict, ethernet_device: str) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_ethernet",
|
||||
"ethernet_device": ethernet_device
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_ethernet"
|
||||
assert response.json()["ethernet_device"] == ethernet_device
|
||||
|
||||
|
||||
async def test_iou_nio_create_ethernet_different_port(app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
ethernet_device: str) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_ethernet",
|
||||
"ethernet_device": ethernet_device
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="3")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_ethernet"
|
||||
assert response.json()["ethernet_device"] == ethernet_device
|
||||
|
||||
|
||||
async def test_iou_nio_create_tap(app: FastAPI, compute_client: AsyncClient, vm: dict, ethernet_device: str) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_tap",
|
||||
"tap_device": ethernet_device
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True):
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_tap"
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_iou_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
params = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
await compute_client.post(url, json=params)
|
||||
await compute_client.post(url, json=params)
|
||||
params["filters"] = {}
|
||||
|
||||
url = app.url_path_for("compute:delete_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
url = app.url_path_for("compute:update_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_iou_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_nio_create_ethernet(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
ethernet_device: str
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
params = {
|
||||
"type": "nio_ethernet",
|
||||
"ethernet_device": ethernet_device
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_iou_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_ethernet"
|
||||
assert response.json()["ethernet_device"] == ethernet_device
|
||||
|
||||
|
||||
async def test_iou_nio_create_ethernet_different_port(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
ethernet_device: str
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_ethernet",
|
||||
"ethernet_device": ethernet_device
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="3")
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_ethernet"
|
||||
assert response.json()["ethernet_device"] == ethernet_device
|
||||
|
||||
|
||||
async def test_iou_nio_create_tap(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
ethernet_device: str
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_tap",
|
||||
"tap_device": ethernet_device
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_tap"
|
||||
|
||||
|
||||
async def test_iou_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_iou_delete_nio(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_iou_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
url = app.url_path_for("compute:create_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_iou_pcap(compute_api, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
url = app.url_path_for("compute:delete_iou_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="1",
|
||||
port_number="0")
|
||||
|
||||
|
||||
async def test_images(app: FastAPI, compute_client: AsyncClient, fake_iou_bin: str) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_iou_images"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}]
|
||||
|
||||
|
||||
async def test_upload_image(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None:
|
||||
|
||||
with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)):
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_iou_image", filename="test2"), content=b"TEST")
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
with open(str(tmpdir / "test2")) as f:
|
||||
assert f.read() == "TEST"
|
||||
|
||||
with open(str(tmpdir / "test2.md5sum")) as f:
|
||||
checksum = f.read()
|
||||
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
|
||||
async def test_iou_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_iou_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_iou_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
file_path = "%2e%2e/hello"
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
url = app.url_path_for("compute:stop_iou_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_iou_pcap(compute_api, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_download_image_forbidden(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None:
|
||||
async def test_images(self, app: FastAPI, compute_client: AsyncClient, fake_iou_bin: str) -> None:
|
||||
|
||||
file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
|
||||
response = await compute_client.get(app.url_path_for("compute:download_iou_image", filename=file_path))
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
response = await compute_client.get(app.url_path_for("compute:get_iou_images"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}]
|
||||
|
||||
|
||||
async def test_iou_duplicate(app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None:
|
||||
async def test_upload_image(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None:
|
||||
|
||||
# create destination node first
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=vm["project_id"]), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)):
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_iou_image", filename="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
with open(str(tmpdir / "test2")) as f:
|
||||
assert f.read() == "TEST"
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:duplicate_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
with open(str(tmpdir / "test2.md5sum")) as f:
|
||||
checksum = f.read()
|
||||
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
|
||||
|
||||
|
||||
async def test_upload_image_forbidden_location(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
file_path = "%2e%2e/hello"
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
async def test_download_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_download_image_forbidden(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None:
|
||||
|
||||
file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
|
||||
response = await compute_client.get(app.url_path_for("compute:download_iou_image", filename=file_path))
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
async def test_iou_duplicate(self, app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None:
|
||||
|
||||
# create destination node first
|
||||
response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=vm["project_id"]), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:duplicate_iou_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
@ -27,167 +27,188 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, ubridge_path: str, on_gns3vm) -> dict:
|
||||
class TestNATNodesRoutes:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id),
|
||||
json={"name": "Nat 1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
ubridge_path: str,
|
||||
on_gns3vm
|
||||
) -> dict:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id),
|
||||
json={"name": "Nat 1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_nat_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> None:
|
||||
async def test_nat_create(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
on_gns3vm
|
||||
) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id),
|
||||
json={"name": "Nat 1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "Nat 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id),
|
||||
json={"name": "Nat 1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "Nat 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_nat_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
async def test_nat_get(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_nat_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "Nat 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "started"
|
||||
|
||||
|
||||
async def test_nat_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_nat_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
await compute_client.post(url, json=params)
|
||||
params["filters"] = {}
|
||||
|
||||
url = app.url_path_for("compute:update_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_nat_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
url = app.url_path_for("compute:delete_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock:
|
||||
response = await compute_client.delete(url)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_nat_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_nat_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_nat_update(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_nat_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json={"name": "test"})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
|
||||
|
||||
async def test_nat_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_nat_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
response = await compute_client.get(app.url_path_for("compute:get_nat_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
assert response.json()["name"] == "Nat 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "started"
|
||||
|
||||
|
||||
async def test_nat_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
async def test_nat_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_nat_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
url = app.url_path_for("compute:create_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_nat_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
await compute_client.post(url, json=params)
|
||||
params["filters"] = {}
|
||||
|
||||
url = app.url_path_for("compute:update_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_nat_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
url = app.url_path_for("compute:delete_nat_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock:
|
||||
response = await compute_client.delete(url)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_nat_pcap(compute_api, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
async def test_nat_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_nat_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_nat_update(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_nat_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json={"name": "test"})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
|
||||
|
||||
async def test_nat_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_nat_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_nat_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_nat_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_nat_pcap(self, compute_api, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -30,173 +30,197 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base_params(tmpdir) -> dict:
|
||||
"""Return standard parameters"""
|
||||
class TestComputeProjectRoutes:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"project_id": str(uuid.uuid4())
|
||||
}
|
||||
return params
|
||||
@pytest.fixture
|
||||
def base_params(self, tmpdir) -> dict:
|
||||
"""Return standard parameters"""
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"project_id": str(uuid.uuid4())
|
||||
}
|
||||
return params
|
||||
|
||||
|
||||
async def test_create_project_without_dir(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
|
||||
async def test_create_project_without_dir(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
base_params: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["project_id"] == base_params["project_id"]
|
||||
assert response.json()["name"] == base_params["name"]
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["project_id"] == base_params["project_id"]
|
||||
assert response.json()["name"] == base_params["name"]
|
||||
|
||||
|
||||
async def test_show_project(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
|
||||
async def test_show_project(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
base_params: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project", project_id=base_params["project_id"]))
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project", project_id=base_params["project_id"]))
|
||||
|
||||
#print(response.json().keys())
|
||||
#assert len(response.json().keys()) == 3
|
||||
assert response.json()["project_id"] == base_params["project_id"]
|
||||
assert response.json()["name"] == base_params["name"]
|
||||
assert response.json()["variables"] is None
|
||||
#print(response.json().keys())
|
||||
#assert len(response.json().keys()) == 3
|
||||
assert response.json()["project_id"] == base_params["project_id"]
|
||||
assert response.json()["name"] == base_params["name"]
|
||||
assert response.json()["variables"] is None
|
||||
|
||||
|
||||
async def test_show_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_show_project_invalid_uuid(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project",
|
||||
project_id="50010203-0405-0607-0809-0a0b0c0d0e42"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project",
|
||||
project_id="50010203-0405-0607-0809-0a0b0c0d0e42"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_list_projects(app: FastAPI, compute_client: AsyncClient) -> dict:
|
||||
async def test_list_projects(self, app: FastAPI, compute_client: AsyncClient) -> dict:
|
||||
|
||||
ProjectManager.instance()._projects = {}
|
||||
ProjectManager.instance()._projects = {}
|
||||
|
||||
params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_projects"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 2
|
||||
assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json()]
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_projects"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 2
|
||||
assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json()]
|
||||
|
||||
|
||||
async def test_delete_project(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
async def test_delete_project(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=compute_project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=compute_project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
async def test_update_project(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
|
||||
async def test_update_project(self, app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params = {"variables": [{"name": "TEST1", "value": "VAL1"}]}
|
||||
response = await compute_client.put(app.url_path_for("compute:update_compute_project", project_id=base_params["project_id"]),
|
||||
json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["variables"] == [{"name": "TEST1", "value": "VAL1"}]
|
||||
params = {"variables": [{"name": "TEST1", "value": "VAL1"}]}
|
||||
response = await compute_client.put(app.url_path_for("compute:update_compute_project", project_id=base_params["project_id"]),
|
||||
json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["variables"] == [{"name": "TEST1", "value": "VAL1"}]
|
||||
|
||||
|
||||
async def test_delete_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_delete_project_invalid_uuid(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=str(uuid.uuid4())))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=str(uuid.uuid4())))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_close_project(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
async def test_close_project(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=compute_project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=compute_project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_close_project_two_client_connected(compute_api, compute_project):
|
||||
#
|
||||
# ProjectHandler._notifications_listening = {compute_project.id: 2}
|
||||
# with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
|
||||
# response = await compute_client.post("/projects/{project_id}/close".format(project_id=compute_project.id))
|
||||
# assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
# assert not mock.called
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_close_project_two_client_connected(compute_api, compute_project):
|
||||
#
|
||||
# ProjectHandler._notifications_listening = {compute_project.id: 2}
|
||||
# with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
|
||||
# response = await compute_client.post("/projects/{project_id}/close".format(project_id=compute_project.id))
|
||||
# assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
# assert not mock.called
|
||||
|
||||
|
||||
async def test_close_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_close_project_invalid_uuid(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=str(uuid.uuid4())))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=str(uuid.uuid4())))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_get_file(app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
async def test_get_file(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
||||
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
|
||||
with open(os.path.join(project.path, "hello"), "w+") as f:
|
||||
f.write("world")
|
||||
with open(os.path.join(project.path, "hello"), "w+") as f:
|
||||
f.write("world")
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="hello"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.content == b"world"
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="hello"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.content == b"world"
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="false"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="false"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path="../hello"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
response = await compute_client.get(app.url_path_for("compute:get_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path="../hello"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_get_file_forbidden_location(app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None:
|
||||
async def test_get_file_forbidden_location(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
config,
|
||||
tmpdir
|
||||
) -> None:
|
||||
|
||||
config.settings.Server.projects_path = str(tmpdir)
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
|
||||
response = await compute_client.get(
|
||||
app.url_path_for(
|
||||
"compute:get_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path=file_path
|
||||
config.settings.Server.projects_path = str(tmpdir)
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
|
||||
response = await compute_client.get(
|
||||
app.url_path_for(
|
||||
"compute:get_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path=file_path
|
||||
)
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
async def test_write_file(app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None:
|
||||
async def test_write_file(self, app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None:
|
||||
|
||||
config.settings.Server.projects_path = str(tmpdir)
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
config.settings.Server.projects_path = str(tmpdir)
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path="hello"), content=b"world")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path="hello"), content=b"world")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
with open(os.path.join(project.path, "hello")) as f:
|
||||
assert f.read() == "world"
|
||||
with open(os.path.join(project.path, "hello")) as f:
|
||||
assert f.read() == "world"
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path="../hello"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path="../hello"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_write_file_forbidden_location(app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None:
|
||||
async def test_write_file_forbidden_location(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
config,
|
||||
tmpdir
|
||||
) -> None:
|
||||
|
||||
config.settings.Server.projects_path = str(tmpdir)
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
config.settings.Server.projects_path = str(tmpdir)
|
||||
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||
|
||||
file_path = "%2e%2e/hello"
|
||||
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path=file_path), content=b"world")
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
file_path = "%2e%2e/hello"
|
||||
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
|
||||
project_id=project.id,
|
||||
file_path=file_path), content=b"world")
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,221 +28,223 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
vboxmanage_path = "/fake/VboxManage"
|
||||
params = {
|
||||
"name": "VMTEST",
|
||||
"vmname": "VMTEST",
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path):
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_vbox_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"name": "VM1",
|
||||
"vmname": "VM1",
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
class TestVirtualBoxNodesRoutes:
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
vboxmanage_path = "/fake/VboxManage"
|
||||
params = {
|
||||
"name": "VMTEST",
|
||||
"vmname": "VMTEST",
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "VM1"
|
||||
|
||||
with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path):
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_vbox_create(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"name": "VM1",
|
||||
"vmname": "VM1",
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "VM1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vbox_get(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "VMTEST"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vbox_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "VMTEST"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vbox_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:start_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_suspend(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:suspend_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_resume(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:resume_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_virtualbox_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vbox_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm):
|
||||
#
|
||||
# params = {
|
||||
# "type": "nio_udp",
|
||||
# "lport": 4242,
|
||||
# "rport": 4343,
|
||||
# "rhost": "127.0.0.1",
|
||||
# "filters": {}
|
||||
# }
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'):
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'):
|
||||
# response = await compute_client.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
|
||||
#
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vbox_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:delete_virtualbox_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
|
||||
response = await compute_client.delete(url)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_vbox_update(app: FastAPI, compute_client: AsyncClient, vm, free_console_port):
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_virtualbox_start_capture(app: FastAPI, compute_client: AsyncClient, vm):
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_virtualbox_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_vbox_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:start_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_virtualbox_stop_capture(app: FastAPI, compute_client: AsyncClient, vm):
|
||||
|
||||
url = app.url_path_for("compute:stop_virtualbox_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_virtualbox_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_suspend(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:suspend_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_resume(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:resume_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vbox_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_virtualbox_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vbox_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm):
|
||||
#
|
||||
# params = {
|
||||
# "type": "nio_udp",
|
||||
# "lport": 4242,
|
||||
# "rport": 4343,
|
||||
# "rhost": "127.0.0.1",
|
||||
# "filters": {}
|
||||
# }
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'):
|
||||
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'):
|
||||
# response = await compute_client.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
|
||||
#
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vbox_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:delete_virtualbox_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
|
||||
response = await compute_client.delete(url)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_vbox_update(self, app: FastAPI, compute_client: AsyncClient, vm, free_console_port):
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_virtualbox_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_virtualbox_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm):
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_virtualbox_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_virtualbox_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm):
|
||||
|
||||
url = app.url_path_for("compute:stop_virtualbox_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_virtualbox_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -28,226 +28,246 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> dict:
|
||||
class TestVMwareNodesRoutes:
|
||||
|
||||
params = {
|
||||
"name": "VMTEST",
|
||||
"vmx_path": vmx_path,
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> dict:
|
||||
|
||||
params = {
|
||||
"name": "VMTEST",
|
||||
"vmx_path": vmx_path,
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vmx_path(tmpdir: str) -> str:
|
||||
"""
|
||||
Return a fake VMX file
|
||||
"""
|
||||
|
||||
path = str(tmpdir / "test.vmx")
|
||||
with open(path, 'w+') as f:
|
||||
f.write("1")
|
||||
return path
|
||||
|
||||
|
||||
async def test_vmware_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> None:
|
||||
|
||||
params = {
|
||||
"name": "VM1",
|
||||
"vmx_path": vmx_path,
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "VM1"
|
||||
@pytest.fixture
|
||||
def vmx_path(self, tmpdir: str) -> str:
|
||||
"""
|
||||
Return a fake VMX file
|
||||
"""
|
||||
|
||||
path = str(tmpdir / "test.vmx")
|
||||
with open(path, 'w+') as f:
|
||||
f.write("1")
|
||||
return path
|
||||
|
||||
|
||||
async def test_vmware_create(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vmx_path: str
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "VM1",
|
||||
"vmx_path": vmx_path,
|
||||
"linked_clone": False
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True):
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id),
|
||||
json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "VM1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vmware_get(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_vmware_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "VMTEST"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vmware_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_vmware_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "VMTEST"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vmware_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_suspend(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:suspend_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_resume(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:resume_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_vmware_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vmware_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm):
|
||||
#
|
||||
# params = {
|
||||
# "type": "nio_udp",
|
||||
# "lport": 4242,
|
||||
# "rport": 4343,
|
||||
# "rhost": "127.0.0.1",
|
||||
# "filters": {}
|
||||
# }
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'):
|
||||
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'):
|
||||
# with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock:
|
||||
# response = await compute_client.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vmware_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:delete_vmware_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock:
|
||||
response = await compute_client.delete(url)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
|
||||
|
||||
async def test_vmware_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_vmware_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock:
|
||||
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_vmware_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_vmware_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_vmware_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vmware_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_suspend(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:suspend_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_resume(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:resume_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_vmware_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vmware_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm):
|
||||
#
|
||||
# params = {
|
||||
# "type": "nio_udp",
|
||||
# "lport": 4242,
|
||||
# "rport": 4343,
|
||||
# "rhost": "127.0.0.1",
|
||||
# "filters": {}
|
||||
# }
|
||||
#
|
||||
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'):
|
||||
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'):
|
||||
# with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock:
|
||||
# response = await compute_client.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vmware_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:delete_vmware_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock:
|
||||
response = await compute_client.delete(url)
|
||||
assert mock.called
|
||||
args, kwgars = mock.call_args
|
||||
assert args[0] == 0
|
||||
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vmware_update(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
free_console_port: int
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": free_console_port
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_vmware_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == free_console_port
|
||||
|
||||
|
||||
async def test_vmware_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_vmware_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock:
|
||||
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_vmware_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_vmware_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vmware_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -28,251 +28,279 @@ from gns3server.compute.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
class TestVPCSNodesRoutes:
|
||||
|
||||
params = {"name": "PC TEST 1"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_vpcs_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {"name": "PC TEST 1"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vpcs_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_vpcs_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "stopped"
|
||||
|
||||
|
||||
async def test_vpcs_create_startup_script(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"name": "PC TEST 1",
|
||||
"startup_script": "ip 192.168.1.2\necho TEST"
|
||||
}
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vpcs_create_port(app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
free_console_port: int) -> None:
|
||||
|
||||
params = {
|
||||
"name": "PC TEST 1",
|
||||
"console": free_console_port
|
||||
}
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["console"] == free_console_port
|
||||
|
||||
|
||||
async def test_vpcs_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vpcs_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params["filters"] = {}
|
||||
url = app.url_path_for("compute:update_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vpcs_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"):
|
||||
@pytest_asyncio.fixture
|
||||
async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {"name": "PC TEST 1"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
return response.json()
|
||||
|
||||
|
||||
async def test_vpcs_create(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
|
||||
|
||||
params = {"name": "PC TEST 1"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vpcs_get(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
response = await compute_client.get(app.url_path_for("compute:get_vpcs_node", project_id=vm["project_id"], node_id=vm["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["status"] == "stopped"
|
||||
|
||||
|
||||
async def test_vpcs_create_startup_script(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "PC TEST 1",
|
||||
"startup_script": "ip 192.168.1.2\necho TEST"
|
||||
}
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
|
||||
|
||||
async def test_vpcs_create_port(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
free_console_port: int
|
||||
) -> None:
|
||||
|
||||
params = {
|
||||
"name": "PC TEST 1",
|
||||
"console": free_console_port
|
||||
}
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "PC TEST 1"
|
||||
assert response.json()["project_id"] == compute_project.id
|
||||
assert response.json()["console"] == free_console_port
|
||||
|
||||
|
||||
async def test_vpcs_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
url = app.url_path_for("compute:delete_vpcs_node_nio",
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vpcs_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:create_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_duplicate(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
|
||||
|
||||
# create destination node first
|
||||
params = {"name": "PC TEST 1"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
response = await compute_client.post(app.url_path_for("compute:duplicate_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_vpcs_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
|
||||
|
||||
console_port = free_console_port
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": console_port
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == console_port
|
||||
|
||||
|
||||
async def test_vpcs_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_vpcs_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params["filters"] = {}
|
||||
url = app.url_path_for("compute:update_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.put(url, json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["type"] == "nio_udp"
|
||||
|
||||
|
||||
async def test_vpcs_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"):
|
||||
url = app.url_path_for("compute:create_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
await compute_client.post(url, json=params)
|
||||
|
||||
url = app.url_path_for("compute:delete_vpcs_node_nio",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
response = await compute_client.delete(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:start_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_vpcs_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_vpcs_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock:
|
||||
|
||||
response = await compute_client.post(app.url_path_for("compute:stop_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vpcs_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project: Project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock:
|
||||
response = await compute_client.post(app.url_path_for("compute:reload_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock:
|
||||
response = await compute_client.delete(app.url_path_for("compute:delete_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_vpcs_duplicate(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
compute_project: Project,
|
||||
vm: dict
|
||||
) -> None:
|
||||
|
||||
# create destination node first
|
||||
params = {"name": "PC TEST 1"}
|
||||
response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
params = {"destination_node_id": response.json()["node_id"]}
|
||||
response = await compute_client.post(app.url_path_for("compute:duplicate_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_vpcs_update(
|
||||
self,
|
||||
app: FastAPI,
|
||||
compute_client: AsyncClient,
|
||||
vm: dict,
|
||||
free_console_port: int
|
||||
) -> None:
|
||||
|
||||
console_port = free_console_port
|
||||
params = {
|
||||
"name": "test",
|
||||
"console": console_port
|
||||
}
|
||||
|
||||
response = await compute_client.put(app.url_path_for("compute:update_vpcs_node",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"]), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
assert response.json()["console"] == console_port
|
||||
|
||||
|
||||
async def test_vpcs_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
params = {
|
||||
"capture_file_name": "test.pcap",
|
||||
"data_link_type": "DLT_EN10MB"
|
||||
}
|
||||
|
||||
url = app.url_path_for("compute:start_vpcs_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock:
|
||||
response = await compute_client.post(url, json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert mock.called
|
||||
assert "test.pcap" in response.json()["pcap_file_path"]
|
||||
|
||||
|
||||
async def test_vpcs_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
|
||||
|
||||
url = app.url_path_for("compute:stop_vpcs_node_capture",
|
||||
project_id=vm["project_id"],
|
||||
node_id=vm["node_id"],
|
||||
adapter_number="0",
|
||||
port_number="0")
|
||||
|
||||
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
|
||||
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock:
|
||||
response = await compute_client.post(url)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert mock.called
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_vpcs_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project: Project):
|
||||
#
|
||||
# with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"):
|
||||
# with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"):
|
||||
# response = await compute_client.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -28,14 +28,16 @@ pytestmark = pytest.mark.asyncio
|
||||
|
||||
class TestApplianceRoutes:
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _install_builtin_appliances(self, controller: Controller):
|
||||
# @pytest.fixture(autouse=True)
|
||||
# def _install_builtin_appliances(self, controller: Controller):
|
||||
#
|
||||
# controller.appliance_manager.install_builtin_appliances()
|
||||
# controller.appliance_manager.load_appliances()
|
||||
|
||||
controller.appliance_manager.install_builtin_appliances()
|
||||
async def test_appliances_list(self, app: FastAPI, client: AsyncClient, controller: Controller) -> None:
|
||||
|
||||
await controller.appliance_manager.install_builtin_appliances()
|
||||
controller.appliance_manager.load_appliances()
|
||||
|
||||
async def test_appliances_list(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_appliances"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) > 0
|
||||
|
@ -27,43 +27,45 @@ from gns3server.config import Config
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_shutdown_local(app: FastAPI, client: AsyncClient, config: Config) -> None:
|
||||
class TestControllerRoutes:
|
||||
|
||||
os.kill = MagicMock()
|
||||
config.settings.Server.local = True
|
||||
response = await client.post(app.url_path_for("shutdown"))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert os.kill.called
|
||||
|
||||
|
||||
async def test_shutdown_non_local(app: FastAPI, client: AsyncClient, config: Config) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("shutdown"))
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_debug(controller_api, config, tmpdir):
|
||||
#
|
||||
# config._main_config_file = str(tmpdir / "test.conf")
|
||||
# config.set("Server", "local", True)
|
||||
# response = await controller_api.post('/debug')
|
||||
# assert response.status_code == 201
|
||||
# debug_dir = os.path.join(config.config_dir, "debug")
|
||||
# assert os.path.exists(debug_dir)
|
||||
# assert os.path.exists(os.path.join(debug_dir, "controller.txt"))
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_debug_non_local(controller_api, config, tmpdir):
|
||||
#
|
||||
# config._main_config_file = str(tmpdir / "test.conf")
|
||||
# config.set("Server", "local", False)
|
||||
# response = await controller_api.post('/debug')
|
||||
# assert response.status_code == 403
|
||||
|
||||
|
||||
async def test_statistics_output(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("statistics"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
async def test_shutdown_local(self, app: FastAPI, client: AsyncClient, config: Config) -> None:
|
||||
|
||||
os.kill = MagicMock()
|
||||
config.settings.Server.local = True
|
||||
response = await client.post(app.url_path_for("shutdown"))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert os.kill.called
|
||||
|
||||
|
||||
async def test_shutdown_non_local(self, app: FastAPI, client: AsyncClient, config: Config) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("shutdown"))
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_debug(controller_api, config, tmpdir):
|
||||
#
|
||||
# config._main_config_file = str(tmpdir / "test.conf")
|
||||
# config.set("Server", "local", True)
|
||||
# response = await controller_api.post('/debug')
|
||||
# assert response.status_code == 201
|
||||
# debug_dir = os.path.join(config.config_dir, "debug")
|
||||
# assert os.path.exists(debug_dir)
|
||||
# assert os.path.exists(os.path.join(debug_dir, "controller.txt"))
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_debug_non_local(controller_api, config, tmpdir):
|
||||
#
|
||||
# config._main_config_file = str(tmpdir / "test.conf")
|
||||
# config.set("Server", "local", False)
|
||||
# response = await controller_api.post('/debug')
|
||||
# assert response.status_code == 403
|
||||
|
||||
|
||||
async def test_statistics_output(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("statistics"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -26,89 +26,91 @@ from gns3server.controller.project import Project
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_create_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
class TestDrawingsRoutes:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["drawing_id"] is not None
|
||||
|
||||
|
||||
async def test_get_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
response = await client.get(app.url_path_for(
|
||||
"get_drawing",
|
||||
project_id=project.id,
|
||||
drawing_id=response.json()["drawing_id"])
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["x"] == 10
|
||||
|
||||
|
||||
async def test_update_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
response = await client.put(app.url_path_for(
|
||||
"update_drawing",
|
||||
project_id=project.id,
|
||||
drawing_id=response.json()["drawing_id"]),
|
||||
json={"x": 42}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["x"] == 42
|
||||
|
||||
|
||||
async def test_all_drawings(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
response = await client.get(app.url_path_for("get_drawings", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
# test listing links from a closed project
|
||||
await project.close(ignore_notification=True)
|
||||
response = await client.get(app.url_path_for("get_drawings", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
|
||||
async def test_delete_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
drawing = Drawing(project)
|
||||
project._drawings = {drawing.id: drawing}
|
||||
response = await client.delete(app.url_path_for(
|
||||
"delete_drawing",
|
||||
project_id=project.id,
|
||||
drawing_id=drawing.id)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert drawing.id not in project.drawings
|
||||
async def test_create_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["drawing_id"] is not None
|
||||
|
||||
|
||||
async def test_get_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
response = await client.get(app.url_path_for(
|
||||
"get_drawing",
|
||||
project_id=project.id,
|
||||
drawing_id=response.json()["drawing_id"])
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["x"] == 10
|
||||
|
||||
|
||||
async def test_update_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
response = await client.put(app.url_path_for(
|
||||
"update_drawing",
|
||||
project_id=project.id,
|
||||
drawing_id=response.json()["drawing_id"]),
|
||||
json={"x": 42}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["x"] == 42
|
||||
|
||||
|
||||
async def test_all_drawings(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
params = {
|
||||
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||
"x": 10,
|
||||
"y": 20,
|
||||
"z": 0
|
||||
}
|
||||
|
||||
await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||
response = await client.get(app.url_path_for("get_drawings", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
# test listing links from a closed project
|
||||
await project.close(ignore_notification=True)
|
||||
response = await client.get(app.url_path_for("get_drawings", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
|
||||
async def test_delete_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
drawing = Drawing(project)
|
||||
project._drawings = {drawing.id: drawing}
|
||||
response = await client.delete(app.url_path_for(
|
||||
"delete_drawing",
|
||||
project_id=project.id,
|
||||
drawing_id=drawing.id)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert drawing.id not in project.drawings
|
||||
|
@ -24,32 +24,34 @@ from tests.utils import asyncio_patch
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_list_vms(app: FastAPI, client: AsyncClient) -> None:
|
||||
class TestGNS3VMRoutes:
|
||||
|
||||
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]):
|
||||
response = await client.get(app.url_path_for("get_vms", engine="vmware"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == [
|
||||
{
|
||||
"vmname": "test"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_engines(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_engines"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) > 0
|
||||
|
||||
|
||||
async def test_put_gns3vm(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.put(app.url_path_for("update_gns3vm_settings"), json={"vmname": "TEST VM"})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["vmname"] == "TEST VM"
|
||||
|
||||
|
||||
async def test_get_gns3vm(app: FastAPI, client: AsyncClient) -> None:
|
||||
response = await client.get(app.url_path_for("get_gns3vm_settings"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
async def test_list_vms(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]):
|
||||
response = await client.get(app.url_path_for("get_vms", engine="vmware"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == [
|
||||
{
|
||||
"vmname": "test"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_engines(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_engines"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) > 0
|
||||
|
||||
|
||||
async def test_put_gns3vm(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.put(app.url_path_for("update_gns3vm_settings"), json={"vmname": "TEST VM"})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["vmname"] == "TEST VM"
|
||||
|
||||
|
||||
async def test_get_gns3vm(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
response = await client.get(app.url_path_for("get_gns3vm_settings"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -19,6 +19,7 @@ import os
|
||||
import pytest
|
||||
import hashlib
|
||||
|
||||
from tests.utils import asyncio_patch
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import FastAPI, status
|
||||
from httpx import AsyncClient
|
||||
@ -261,10 +262,13 @@ class TestImageRoutes:
|
||||
|
||||
async def test_prune_images(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("prune_images"))
|
||||
images_repo = ImagesRepository(db_session)
|
||||
images_in_db = await images_repo.get_images()
|
||||
assert len(images_in_db) != 0
|
||||
|
||||
response = await client.delete(app.url_path_for("prune_images"))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
images_repo = ImagesRepository(db_session)
|
||||
images_in_db = await images_repo.get_images()
|
||||
assert len(images_in_db) == 0
|
||||
|
||||
@ -275,7 +279,7 @@ class TestImageRoutes:
|
||||
controller: Controller
|
||||
) -> None:
|
||||
|
||||
controller.appliance_manager.install_builtin_appliances()
|
||||
await controller.appliance_manager.install_builtin_appliances()
|
||||
controller.appliance_manager.load_appliances() # make sure appliances are loaded
|
||||
image_path = "tests/resources/empty30G.qcow2"
|
||||
image_name = os.path.basename(image_path)
|
||||
@ -292,3 +296,32 @@ class TestImageRoutes:
|
||||
assert len(templates) == 1
|
||||
assert templates[0].name == "Empty VM"
|
||||
assert templates[0].version == "30G"
|
||||
await templates_repo.delete_template(templates[0].template_id)
|
||||
|
||||
async def test_install_all(
|
||||
self, app: FastAPI,
|
||||
client: AsyncClient,
|
||||
db_session: AsyncSession,
|
||||
controller: Controller
|
||||
) -> None:
|
||||
|
||||
image_path = "tests/resources/empty100G.qcow2"
|
||||
image_name = os.path.basename(image_path)
|
||||
with open(image_path, "rb") as f:
|
||||
image_data = f.read()
|
||||
response = await client.post(
|
||||
app.url_path_for("upload_image", image_path=image_name),
|
||||
content=image_data)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
controller.appliance_manager.load_appliances() # make sure appliances are loaded
|
||||
with asyncio_patch("gns3server.api.routes.controller.images.get_builtin_disks", return_value=[]) as mock:
|
||||
response = await client.post(app.url_path_for("install_images"))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
templates_repo = TemplatesRepository(db_session)
|
||||
templates = await templates_repo.get_templates()
|
||||
assert len(templates) == 1
|
||||
assert templates[0].name == "Empty VM"
|
||||
assert templates[0].version == "100G"
|
@ -35,30 +35,88 @@ from gns3server.controller.udp_link import UDPLink
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def nodes(compute: Compute, project: Project) -> Tuple[Node, Node]:
|
||||
class TestLinkRoutes:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node1 = await project.add_node(compute, "node1", None, node_type="qemu")
|
||||
node1._ports = [EthernetPort("E0", 0, 0, 3)]
|
||||
node2 = await project.add_node(compute, "node2", None, node_type="qemu")
|
||||
node2._ports = [EthernetPort("E0", 0, 2, 4)]
|
||||
return node1, node2
|
||||
|
||||
|
||||
async def test_create_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
|
||||
|
||||
node1, node2 = nodes
|
||||
|
||||
filters = {
|
||||
"latency": [10],
|
||||
"frequency_drop": [50]
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
@pytest_asyncio.fixture
|
||||
async def nodes(self, compute: Compute, project: Project) -> Tuple[Node, Node]:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node1 = await project.add_node(compute, "node1", None, node_type="qemu")
|
||||
node1._ports = [EthernetPort("E0", 0, 0, 3)]
|
||||
node2 = await project.add_node(compute, "node2", None, node_type="qemu")
|
||||
node2._ports = [EthernetPort("E0", 0, 2, 4)]
|
||||
return node1, node2
|
||||
|
||||
|
||||
async def test_create_link(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
nodes: Tuple[Node, Node]
|
||||
) -> None:
|
||||
|
||||
node1, node2 = nodes
|
||||
|
||||
filters = {
|
||||
"latency": [10],
|
||||
"frequency_drop": [50]
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
],
|
||||
"filters": filters
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["link_id"] is not None
|
||||
assert len(response.json()["nodes"]) == 2
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
assert len(project.links) == 1
|
||||
assert list(project.links.values())[0].filters == filters
|
||||
|
||||
|
||||
async def test_create_link_failure(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
compute: Compute,
|
||||
project: Project
|
||||
) -> None:
|
||||
"""
|
||||
Make sure the link is deleted if we failed to create it.
|
||||
|
||||
The failure is triggered by connecting the link to itself
|
||||
"""
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node1 = await project.add_node(compute, "node1", None, node_type="qemu")
|
||||
node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)]
|
||||
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
@ -71,6 +129,168 @@ async def test_create_link(app: FastAPI, client: AsyncClient, project: Project,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
assert len(project.links) == 0
|
||||
|
||||
|
||||
async def test_get_link(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
nodes: Tuple[Node, Node]
|
||||
) -> None:
|
||||
|
||||
node1, node2 = nodes
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
link_id = response.json()["link_id"]
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
response = await client.get(app.url_path_for("get_link", project_id=project.id, link_id=link_id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
|
||||
|
||||
async def test_update_link_suspend(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
nodes: Tuple[Node, Node]
|
||||
) -> None:
|
||||
|
||||
node1, node2 = nodes
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
link_id = response.json()["link_id"]
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
|
||||
response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Hello",
|
||||
"x": 64,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
],
|
||||
"suspend": True
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 64
|
||||
assert response.json()["suspend"]
|
||||
assert response.json()["filters"] == {}
|
||||
|
||||
|
||||
async def test_update_link(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
nodes: Tuple[Node, Node]
|
||||
) -> None:
|
||||
|
||||
filters = {
|
||||
"latency": [10],
|
||||
"frequency_drop": [50]
|
||||
}
|
||||
|
||||
node1, node2 = nodes
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
link_id = response.json()["link_id"]
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
|
||||
response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Hello",
|
||||
"x": 64,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
@ -79,309 +299,127 @@ async def test_create_link(app: FastAPI, client: AsyncClient, project: Project,
|
||||
],
|
||||
"filters": filters
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["link_id"] is not None
|
||||
assert len(response.json()["nodes"]) == 2
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
assert len(project.links) == 1
|
||||
assert list(project.links.values())[0].filters == filters
|
||||
|
||||
|
||||
async def test_create_link_failure(app: FastAPI, client: AsyncClient, compute: Compute, project: Project) -> None:
|
||||
"""
|
||||
Make sure the link is deleted if we failed to create it.
|
||||
|
||||
The failure is triggered by connecting the link to itself
|
||||
"""
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node1 = await project.add_node(compute, "node1", None, node_type="qemu")
|
||||
node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)]
|
||||
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 64
|
||||
assert list(project.links.values())[0].filters == filters
|
||||
|
||||
|
||||
async def test_list_link(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
nodes: Tuple[Node, Node]
|
||||
) -> None:
|
||||
|
||||
filters = {
|
||||
"latency": [10],
|
||||
"frequency_drop": [50]
|
||||
}
|
||||
|
||||
node1, node2 = nodes
|
||||
nodes = [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
"port_number": 3
|
||||
},
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
assert len(project.links) == 0
|
||||
|
||||
|
||||
async def test_get_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
|
||||
|
||||
node1, node2 = nodes
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
link_id = response.json()["link_id"]
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
response = await client.get(app.url_path_for("get_link", project_id=project.id, link_id=link_id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
|
||||
|
||||
async def test_update_link_suspend(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
|
||||
|
||||
node1, node2 = nodes
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
link_id = response.json()["link_id"]
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
|
||||
response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Hello",
|
||||
"x": 64,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
],
|
||||
"suspend": True
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 64
|
||||
assert response.json()["suspend"]
|
||||
assert response.json()["filters"] == {}
|
||||
|
||||
|
||||
async def test_update_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
|
||||
|
||||
filters = {
|
||||
"latency": [10],
|
||||
"frequency_drop": [50]
|
||||
}
|
||||
|
||||
node1, node2 = nodes
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Text",
|
||||
"x": 42,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
link_id = response.json()["link_id"]
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 42
|
||||
|
||||
response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3,
|
||||
"label": {
|
||||
"text": "Hello",
|
||||
"x": 64,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
],
|
||||
"filters": filters
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["nodes"][0]["label"]["x"] == 64
|
||||
assert list(project.links.values())[0].filters == filters
|
||||
|
||||
|
||||
async def test_list_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
|
||||
|
||||
filters = {
|
||||
"latency": [10],
|
||||
"frequency_drop": [50]
|
||||
}
|
||||
|
||||
node1, node2 = nodes
|
||||
nodes = [
|
||||
{
|
||||
"node_id": node1.id,
|
||||
"adapter_number": 0,
|
||||
"port_number": 3
|
||||
},
|
||||
{
|
||||
"node_id": node2.id,
|
||||
"adapter_number": 2,
|
||||
"port_number": 4
|
||||
}
|
||||
]
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": nodes,
|
||||
"filters": filters
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
response = await client.get(app.url_path_for("get_links", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
assert response.json()[0]["filters"] == filters
|
||||
|
||||
# test listing links from a closed project
|
||||
await project.close(ignore_notification=True)
|
||||
response = await client.get(app.url_path_for("get_links", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
assert response.json()[0]["filters"] == filters
|
||||
|
||||
|
||||
async def test_reset_link(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = UDPLink(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.delete") as delete_mock:
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as create_mock:
|
||||
response = await client.post(app.url_path_for("reset_link", project_id=project.id, link_id=link.id))
|
||||
assert delete_mock.called
|
||||
assert create_mock.called
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_start_capture(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock:
|
||||
response = await client.post(app.url_path_for("start_capture", project_id=project.id, link_id=link.id), json={})
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
|
||||
await client.post(app.url_path_for("create_link", project_id=project.id), json={
|
||||
"nodes": nodes,
|
||||
"filters": filters
|
||||
})
|
||||
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_stop_capture(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock:
|
||||
response = await client.post(app.url_path_for("stop_capture", project_id=project.id, link_id=link.id))
|
||||
response = await client.get(app.url_path_for("get_links", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
assert response.json()[0]["filters"] == filters
|
||||
|
||||
# test listing links from a closed project
|
||||
await project.close(ignore_notification=True)
|
||||
response = await client.get(app.url_path_for("get_links", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
assert response.json()[0]["filters"] == filters
|
||||
|
||||
|
||||
async def test_reset_link(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = UDPLink(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.delete") as delete_mock:
|
||||
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as create_mock:
|
||||
response = await client.post(app.url_path_for("reset_link", project_id=project.id, link_id=link.id))
|
||||
assert delete_mock.called
|
||||
assert create_mock.called
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_start_capture(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock:
|
||||
response = await client.post(app.url_path_for("start_capture", project_id=project.id, link_id=link.id), json={})
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_stop_capture(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock:
|
||||
response = await client.post(app.url_path_for("stop_capture", project_id=project.id, link_id=link.id))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
# async def test_pcap(controller_api, http_client, project):
|
||||
#
|
||||
# async def pcap_capture():
|
||||
# async with http_client.get(controller_api.get_url("/projects/{}/links/{}/pcap".format(project.id, link.id))) as response:
|
||||
# response.body = await response.content.read(5)
|
||||
# print("READ", response.body)
|
||||
# return response
|
||||
#
|
||||
# with asyncio_patch("gns3server.controller.link.Link.capture_node") as mock:
|
||||
# link = Link(project)
|
||||
# link._capture_file_name = "test"
|
||||
# link._capturing = True
|
||||
# with open(link.capture_file_path, "w+") as f:
|
||||
# f.write("hello")
|
||||
# project._links = {link.id: link}
|
||||
# response = await pcap_capture()
|
||||
# assert mock.called
|
||||
# assert response.status_code == 200
|
||||
# assert b'hello' == response.body
|
||||
|
||||
|
||||
async def test_delete_link(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.link.Link.delete") as mock:
|
||||
response = await client.delete(app.url_path_for("delete_link", project_id=project.id, link_id=link.id))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
# async def test_pcap(controller_api, http_client, project):
|
||||
#
|
||||
# async def pcap_capture():
|
||||
# async with http_client.get(controller_api.get_url("/projects/{}/links/{}/pcap".format(project.id, link.id))) as response:
|
||||
# response.body = await response.content.read(5)
|
||||
# print("READ", response.body)
|
||||
# return response
|
||||
#
|
||||
# with asyncio_patch("gns3server.controller.link.Link.capture_node") as mock:
|
||||
# link = Link(project)
|
||||
# link._capture_file_name = "test"
|
||||
# link._capturing = True
|
||||
# with open(link.capture_file_path, "w+") as f:
|
||||
# f.write("hello")
|
||||
# project._links = {link.id: link}
|
||||
# response = await pcap_capture()
|
||||
# assert mock.called
|
||||
# assert response.status_code == 200
|
||||
# assert b'hello' == response.body
|
||||
|
||||
|
||||
async def test_delete_link(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with asyncio_patch("gns3server.controller.link.Link.delete") as mock:
|
||||
response = await client.delete(app.url_path_for("delete_link", project_id=project.id, link_id=link.id))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_list_filters(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock:
|
||||
response = await client.get(app.url_path_for("get_filters", project_id=project.id, link_id=link.id))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == FILTERS
|
||||
|
||||
|
||||
async def test_list_filters(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
link = Link(project)
|
||||
project._links = {link.id: link}
|
||||
with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock:
|
||||
response = await client.get(app.url_path_for("get_filters", project_id=project.id, link_id=link.id))
|
||||
assert mock.called
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == FILTERS
|
||||
|
@ -31,416 +31,537 @@ from gns3server.controller.compute import Compute
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def node(project: Project, compute: Compute) -> Node:
|
||||
|
||||
node = Node(project, compute, "test", node_type="vpcs")
|
||||
project._nodes[node.id] = node
|
||||
return node
|
||||
|
||||
|
||||
async def test_create_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "test"
|
||||
assert "name" not in response.json()["properties"]
|
||||
|
||||
|
||||
async def test_list_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
response = await client.get(app.url_path_for("get_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()[0]["name"] == "test"
|
||||
|
||||
# test listing nodes from a closed project
|
||||
await project.close(ignore_notification=True)
|
||||
response = await client.get(app.url_path_for("get_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()[0]["name"] == "test"
|
||||
|
||||
|
||||
async def test_get_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
response = await client.get(app.url_path_for("get_node", project_id=project.id, node_id=response.json()["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
|
||||
|
||||
async def test_update_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.put = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.put(app.url_path_for("update_node", project_id=project.id, node_id=node.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["name"] == "test"
|
||||
assert "name" not in response.json()["properties"]
|
||||
|
||||
|
||||
async def test_start_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("start_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_stop_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("stop_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_suspend_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("suspend_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_reload_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("reload_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_reset_console_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("reset_console_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_start_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("start_node", project_id=project.id, node_id=node.id), json={})
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_stop_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("stop_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
async def test_suspend_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("suspend_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_reload_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node):
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("reload_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_isolate_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node):
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("isolate_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_unisolate_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node):
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("unisolate_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_duplicate_node(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json({"console": 2035})
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.post(app.url_path_for("duplicate_node", project_id=project.id, node_id=node.id),
|
||||
json={"x": 10, "y": 5, "z": 0})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_delete_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.delete(app.url_path_for("delete_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_dynamips_idle_pc(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"idlepc": "0x60606f54"}
|
||||
compute.get = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "dynamips" # force Dynamips node type
|
||||
response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["idlepc"] == "0x60606f54"
|
||||
|
||||
|
||||
async def test_dynamips_idle_pc_wrong_node_type(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_dynamips_idlepc_proposals(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = ["0x60606f54", "0x33805a22"]
|
||||
compute.get = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "dynamips" # force Dynamips node type
|
||||
response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == ["0x60606f54", "0x33805a22"]
|
||||
|
||||
|
||||
async def test_dynamips_idlepc_proposals_wrong_node_type(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_qemu_disk_image_create(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "qemu" # force Qemu node type
|
||||
response = await client.post(
|
||||
app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"format": "qcow2", "size": 30}
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_qemu_disk_image_create_wrong_node_type(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.post(
|
||||
app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"format": "qcow2", "size": 30}
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_qemu_disk_image_update(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
compute.put = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "qemu" # force Qemu node type
|
||||
response = await client.put(
|
||||
app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"extend": 10}
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_qemu_disk_image_update_wrong_node_type(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.put(
|
||||
app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"extend": 10}
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_qemu_disk_image_delete(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
compute.delete = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "qemu" # force Qemu node type
|
||||
response = await client.delete(
|
||||
app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2")
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_qemu_disk_image_delete_wrong_node_type(
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.delete(
|
||||
app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2")
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_get_file(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.body = b"world"
|
||||
response.status = status.HTTP_200_OK
|
||||
compute.http_query = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.get(app.url_path_for("get_file", project_id=project.id, node_id=node.id, file_path="hello"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.content == b'world'
|
||||
|
||||
compute.http_query.assert_called_with(
|
||||
"GET",
|
||||
"/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(
|
||||
class TestNodeRoutes:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def node(self, project: Project, compute: Compute) -> Node:
|
||||
|
||||
node = Node(project, compute, "test", node_type="vpcs")
|
||||
project._nodes[node.id] = node
|
||||
return node
|
||||
|
||||
|
||||
async def test_create_node(self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == "test"
|
||||
assert "name" not in response.json()["properties"]
|
||||
|
||||
|
||||
async def test_list_node(self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
response = await client.get(app.url_path_for("get_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()[0]["name"] == "test"
|
||||
|
||||
# test listing nodes from a closed project
|
||||
await project.close(ignore_notification=True)
|
||||
response = await client.get(app.url_path_for("get_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()[0]["name"] == "test"
|
||||
|
||||
|
||||
async def test_get_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
response = await client.get(app.url_path_for("get_node", project_id=project.id, node_id=response.json()["node_id"]))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["name"] == "test"
|
||||
|
||||
|
||||
async def test_update_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.put = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.put(app.url_path_for("update_node", project_id=project.id, node_id=node.id), json={
|
||||
"name": "test",
|
||||
"node_type": "vpcs",
|
||||
"compute_id": "example.com",
|
||||
"properties": {
|
||||
"startup_script": "echo test"
|
||||
}
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["name"] == "test"
|
||||
assert "name" not in response.json()["properties"]
|
||||
|
||||
|
||||
async def test_start_all_nodes(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("start_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_stop_all_nodes(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("stop_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_suspend_all_nodes(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("suspend_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_reload_all_nodes(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("reload_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_reset_console_all_nodes(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("reset_console_all_nodes", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_start_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("start_node", project_id=project.id, node_id=node.id), json={})
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_stop_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("stop_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
async def test_suspend_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("suspend_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_reload_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
):
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("reload_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_isolate_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
):
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("isolate_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_unisolate_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for("unisolate_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_duplicate_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json({"console": 2035})
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.post(app.url_path_for("duplicate_node", project_id=project.id, node_id=node.id),
|
||||
json={"x": 10, "y": 5, "z": 0})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
|
||||
async def test_delete_node(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
compute.post = AsyncioMagicMock()
|
||||
response = await client.delete(app.url_path_for("delete_node", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_dynamips_idle_pc(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"idlepc": "0x60606f54"}
|
||||
compute.get = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "dynamips" # force Dynamips node type
|
||||
response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["idlepc"] == "0x60606f54"
|
||||
|
||||
|
||||
async def test_dynamips_idle_pc_wrong_node_type(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_dynamips_idlepc_proposals(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.json = ["0x60606f54", "0x33805a22"]
|
||||
compute.get = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "dynamips" # force Dynamips node type
|
||||
response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == ["0x60606f54", "0x33805a22"]
|
||||
|
||||
|
||||
async def test_dynamips_idlepc_proposals_wrong_node_type(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_qemu_disk_image_create(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "qemu" # force Qemu node type
|
||||
response = await client.post(
|
||||
app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"format": "qcow2", "size": 30}
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_qemu_disk_image_create_wrong_node_type(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.post(
|
||||
app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"format": "qcow2", "size": 30}
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_qemu_disk_image_update(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
compute.put = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "qemu" # force Qemu node type
|
||||
response = await client.put(
|
||||
app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"extend": 10}
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_qemu_disk_image_update_wrong_node_type(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.put(
|
||||
app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"),
|
||||
json={"extend": 10}
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_qemu_disk_image_delete(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
compute.delete = AsyncioMagicMock(return_value=response)
|
||||
|
||||
node._node_type = "qemu" # force Qemu node type
|
||||
response = await client.delete(
|
||||
app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2")
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
|
||||
async def test_qemu_disk_image_delete_wrong_node_type(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = await client.delete(
|
||||
app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2")
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
async def test_get_file(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
response = MagicMock()
|
||||
response.body = b"world"
|
||||
response.status = status.HTTP_200_OK
|
||||
compute.http_query = AsyncioMagicMock(return_value=response)
|
||||
|
||||
response = await client.get(app.url_path_for("get_file", project_id=project.id, node_id=node.id, file_path="hello"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.content == b'world'
|
||||
|
||||
compute.http_query.assert_called_with(
|
||||
"GET",
|
||||
"/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(
|
||||
project_id=project.id,
|
||||
node_id=node.id),
|
||||
timeout=None,
|
||||
raw=True)
|
||||
|
||||
response = await client.get(app.url_path_for(
|
||||
"get_file",
|
||||
project_id=project.id,
|
||||
node_id=node.id),
|
||||
timeout=None,
|
||||
raw=True)
|
||||
|
||||
response = await client.get(app.url_path_for(
|
||||
"get_file",
|
||||
project_id=project.id,
|
||||
node_id=node.id,
|
||||
file_path="../hello"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_post_file(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
|
||||
|
||||
compute.http_query = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for(
|
||||
"post_file",
|
||||
project_id=project.id,
|
||||
node_id=node.id,
|
||||
file_path="hello"), content=b"hello")
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)
|
||||
|
||||
response = await client.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_get_and_post_with_nested_paths_normalization(controller_api, project, node, compute):
|
||||
#
|
||||
# response = MagicMock()
|
||||
# response.body = b"world"
|
||||
# compute.http_query = AsyncioMagicMock(return_value=response)
|
||||
# response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id))
|
||||
# assert response.status_code == 200
|
||||
# assert response.content == b'world'
|
||||
#
|
||||
# compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True)
|
||||
#
|
||||
# compute.http_query = AsyncioMagicMock()
|
||||
# response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True)
|
||||
# assert response.status_code == 201
|
||||
#
|
||||
# compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)
|
||||
node_id=node.id,
|
||||
file_path="../hello"))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
async def test_post_file(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
project: Project,
|
||||
compute: Compute,
|
||||
node: Node
|
||||
) -> None:
|
||||
|
||||
compute.http_query = AsyncioMagicMock()
|
||||
response = await client.post(app.url_path_for(
|
||||
"post_file",
|
||||
project_id=project.id,
|
||||
node_id=node.id,
|
||||
file_path="hello"), content=b"hello")
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)
|
||||
|
||||
response = await client.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id))
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_get_and_post_with_nested_paths_normalization(controller_api, project, node, compute):
|
||||
#
|
||||
# response = MagicMock()
|
||||
# response.body = b"world"
|
||||
# compute.http_query = AsyncioMagicMock(return_value=response)
|
||||
# response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id))
|
||||
# assert response.status_code == 200
|
||||
# assert response.content == b'world'
|
||||
#
|
||||
# compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True)
|
||||
#
|
||||
# compute.http_query = AsyncioMagicMock()
|
||||
# response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True)
|
||||
# assert response.status_code == 201
|
||||
#
|
||||
# compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,47 +30,49 @@ from gns3server.controller.snapshot import Snapshot
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
|
||||
class TestSnapshotRoutes:
|
||||
|
||||
u = str(uuid.uuid4())
|
||||
params = {"name": "test", "project_id": u}
|
||||
await client.post(app.url_path_for("create_project"), json=params)
|
||||
project = controller.get_project(u)
|
||||
return project
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def snapshot(project: Project):
|
||||
|
||||
snapshot = await project.snapshot("test")
|
||||
return snapshot
|
||||
|
||||
|
||||
async def test_list_snapshots(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
|
||||
|
||||
assert snapshot.name == "test"
|
||||
response = await client.get(app.url_path_for("get_snapshots", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
|
||||
async def test_delete_snapshot(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
|
||||
|
||||
response = await client.delete(app.url_path_for("delete_snapshot", project_id=project.id, snapshot_id=snapshot.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert not os.path.exists(snapshot.path)
|
||||
|
||||
|
||||
async def test_restore_snapshot(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("restore_snapshot", project_id=project.id, snapshot_id=snapshot.id))
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == project.name
|
||||
|
||||
|
||||
async def test_create_snapshot(app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("create_snapshot", project_id=project.id), json={"name": "snap1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1
|
||||
@pytest_asyncio.fixture
|
||||
async def project(self, app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
|
||||
|
||||
u = str(uuid.uuid4())
|
||||
params = {"name": "test", "project_id": u}
|
||||
await client.post(app.url_path_for("create_project"), json=params)
|
||||
controller_project = controller.get_project(u)
|
||||
return controller_project
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def snapshot(self, project: Project):
|
||||
|
||||
controller_snapshot = await project.snapshot("test")
|
||||
return controller_snapshot
|
||||
|
||||
|
||||
async def test_list_snapshots(self, app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
|
||||
|
||||
assert snapshot.name == "test"
|
||||
response = await client.get(app.url_path_for("get_snapshots", project_id=project.id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
|
||||
async def test_delete_snapshot(self, app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
|
||||
|
||||
response = await client.delete(app.url_path_for("delete_snapshot", project_id=project.id, snapshot_id=snapshot.id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert not os.path.exists(snapshot.path)
|
||||
|
||||
|
||||
async def test_restore_snapshot(self, app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("restore_snapshot", project_id=project.id, snapshot_id=snapshot.id))
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.json()["name"] == project.name
|
||||
|
||||
|
||||
async def test_create_snapshot(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("create_snapshot", project_id=project.id), json={"name": "snap1"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1
|
||||
|
@ -28,41 +28,43 @@ from gns3server.controller import Controller
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_symbols(app: FastAPI, client: AsyncClient) -> None:
|
||||
class TestSymbolRoutes:
|
||||
|
||||
response = await client.get(app.url_path_for("get_symbols"))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert {
|
||||
'symbol_id': ':/symbols/classic/firewall.svg',
|
||||
'filename': 'firewall.svg',
|
||||
'builtin': True,
|
||||
'theme': 'Classic'
|
||||
} in response.json()
|
||||
|
||||
|
||||
async def test_get(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
|
||||
|
||||
controller.symbols.theme = "Classic"
|
||||
url = app.url_path_for("get_symbol", symbol_id=urllib.parse.quote(':/symbols/classic/firewall.svg'))
|
||||
response = await client.get(url)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
|
||||
assert response.headers['CONTENT-LENGTH'] == '9381'
|
||||
assert '</svg>' in response.text
|
||||
|
||||
# Reply with the default symbol
|
||||
response = await client.get(app.url_path_for("get_symbol", symbol_id="404.png"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_upload(app: FastAPI, client: AsyncClient, symbols_dir: str) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("upload_symbol", symbol_id="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
with open(os.path.join(symbols_dir, "test2")) as f:
|
||||
assert f.read() == "TEST"
|
||||
|
||||
response = await client.get(app.url_path_for("get_symbol", symbol_id="test2"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
async def test_symbols(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_symbols"))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert {
|
||||
'symbol_id': ':/symbols/classic/firewall.svg',
|
||||
'filename': 'firewall.svg',
|
||||
'builtin': True,
|
||||
'theme': 'Classic'
|
||||
} in response.json()
|
||||
|
||||
|
||||
async def test_get(self, app: FastAPI, client: AsyncClient, controller: Controller) -> None:
|
||||
|
||||
controller.symbols.theme = "Classic"
|
||||
url = app.url_path_for("get_symbol", symbol_id=urllib.parse.quote(':/symbols/classic/firewall.svg'))
|
||||
response = await client.get(url)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
|
||||
assert response.headers['CONTENT-LENGTH'] == '9381'
|
||||
assert '</svg>' in response.text
|
||||
|
||||
# Reply with the default symbol
|
||||
response = await client.get(app.url_path_for("get_symbol", symbol_id="404.png"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_upload(self, app: FastAPI, client: AsyncClient, symbols_dir: str) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("upload_symbol", symbol_id="test2"), content=b"TEST")
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
with open(os.path.join(symbols_dir, "test2")) as f:
|
||||
assert f.read() == "TEST"
|
||||
|
||||
response = await client.get(app.url_path_for("get_symbol", symbol_id="test2"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -16,6 +16,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
import uuid
|
||||
import unittest.mock
|
||||
@ -145,29 +147,39 @@ class TestTemplateRoutes:
|
||||
tmpdir: str,
|
||||
) -> None:
|
||||
|
||||
path = os.path.join(tmpdir, "test.qcow2")
|
||||
with open(path, "wb+") as f:
|
||||
image1 = os.path.join(tmpdir, "image1.qcow2")
|
||||
with open(image1, "wb+") as f:
|
||||
f.write(b'\x42\x42\x42\x42')
|
||||
|
||||
image2 = os.path.join(tmpdir, "image2.qcow2")
|
||||
with open(image2, "wb+") as f:
|
||||
f.write(b'\x42\x42\x42\x42')
|
||||
|
||||
images_repo = ImagesRepository(db_session)
|
||||
await images_repo.add_image("test.qcow2", "qemu", 42, path, "e342eb86c1229b6c154367a5476969b5", "md5")
|
||||
await images_repo.add_image("image1.qcow2", "qemu", 42, image1, "e342eb86c1229b6c154367a5476969b5", "md5")
|
||||
await images_repo.add_image("image2.qcow2", "qemu", 42, image2, "e342eb86c1229b6c154367a5476969b5", "md5")
|
||||
|
||||
template_id = str(uuid.uuid4())
|
||||
params = {"template_id": template_id,
|
||||
"name": "QEMU_TEMPLATE",
|
||||
"compute_id": "local",
|
||||
"hda_disk_image": "test.qcow2",
|
||||
"hda_disk_image": "image1.qcow2",
|
||||
"hdb_disk_image": "image2.qcow2",
|
||||
"template_type": "qemu"}
|
||||
|
||||
response = await client.post(app.url_path_for("create_template"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
templates_repo = TemplatesRepository(db_session)
|
||||
images = await templates_repo.get_template_images(response.json().get("template_id"))
|
||||
assert len(images) == 2
|
||||
|
||||
response = await client.delete(
|
||||
app.url_path_for("delete_template", template_id=template_id),
|
||||
params={"prune_images": True}
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
images_repo = ImagesRepository(db_session)
|
||||
images = await images_repo.get_images()
|
||||
assert len(images) == 0
|
||||
|
||||
|
@ -21,7 +21,8 @@ from typing import Optional
|
||||
from fastapi import FastAPI, HTTPException, status
|
||||
from sqlalchemy import update
|
||||
from httpx import AsyncClient
|
||||
from jose import jwt
|
||||
from joserfc import jwt
|
||||
from joserfc.jwk import OctKey
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from gns3server.db.repositories.users import UsersRepository
|
||||
@ -166,16 +167,23 @@ class TestAuthTokens:
|
||||
|
||||
jwt_secret = config.settings.Controller.jwt_secret_key
|
||||
token = auth_service.create_access_token(test_user.username)
|
||||
payload = jwt.decode(token, jwt_secret, algorithms=["HS256"])
|
||||
username = payload.get("sub")
|
||||
key = OctKey.import_key(jwt_secret)
|
||||
payload = jwt.decode(token, key, algorithms=["HS256"])
|
||||
username = payload.claims.get("sub")
|
||||
assert username == test_user.username
|
||||
|
||||
async def test_token_missing_user_is_invalid(self, app: FastAPI, client: AsyncClient, config: Config) -> None:
|
||||
async def test_decode_token_with_wrong_algorithm(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_user: User,
|
||||
config: Config
|
||||
) -> None:
|
||||
|
||||
jwt_secret = config.settings.Controller.jwt_secret_key
|
||||
token = auth_service.create_access_token(None)
|
||||
with pytest.raises(jwt.JWTError):
|
||||
jwt.decode(token, jwt_secret, algorithms=["HS256"])
|
||||
token = auth_service.create_access_token(test_user.username)
|
||||
with pytest.raises(ValueError):
|
||||
jwt.decode(token, jwt_secret, algorithms=["ES256"])
|
||||
|
||||
async def test_can_retrieve_username_from_token(
|
||||
self,
|
||||
@ -236,31 +244,16 @@ class TestUserLogin:
|
||||
|
||||
# check that token exists in response and has user encoded within it
|
||||
token = response.json().get("access_token")
|
||||
payload = jwt.decode(token, jwt_secret, algorithms=["HS256"])
|
||||
assert "sub" in payload
|
||||
username = payload.get("sub")
|
||||
key = OctKey.import_key(jwt_secret)
|
||||
payload = jwt.decode(token, key, algorithms=["HS256"])
|
||||
assert "sub" in payload.claims
|
||||
username = payload.claims.get("sub")
|
||||
assert username == test_user.username
|
||||
|
||||
# check that token is proper type
|
||||
assert "token_type" in response.json()
|
||||
assert response.json().get("token_type") == "bearer"
|
||||
|
||||
async def test_user_can_authenticate_using_json(
|
||||
self,
|
||||
app: FastAPI,
|
||||
unauthorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
config: Config
|
||||
) -> None:
|
||||
|
||||
credentials = {
|
||||
"username": test_user.username,
|
||||
"password": "user1_password",
|
||||
}
|
||||
response = await unauthorized_client.post(app.url_path_for("authenticate"), json=credentials)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json().get("access_token")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"username, password, status_code",
|
||||
(
|
||||
@ -290,7 +283,19 @@ class TestUserLogin:
|
||||
assert response.status_code == status_code
|
||||
assert "access_token" not in response.json()
|
||||
|
||||
async def test_user_can_use_token_as_url_param(
|
||||
|
||||
class TestUnauthorizedUser:
|
||||
|
||||
async def test_user_cannot_access_own_data_if_not_authenticated(
|
||||
self, app: FastAPI,
|
||||
unauthorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
|
||||
response = await unauthorized_client.get(app.url_path_for("get_logged_in_user"))
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
async def test_user_can_authenticate_using_json(
|
||||
self,
|
||||
app: FastAPI,
|
||||
unauthorized_client: AsyncClient,
|
||||
@ -302,15 +307,14 @@ class TestUserLogin:
|
||||
"username": test_user.username,
|
||||
"password": "user1_password",
|
||||
}
|
||||
|
||||
response = await unauthorized_client.post(app.url_path_for("authenticate"), json=credentials)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
token = response.json().get("access_token")
|
||||
assert response.json().get("access_token")
|
||||
|
||||
token = response.json().get("access_token")
|
||||
response = await unauthorized_client.get(app.url_path_for("statistics"), params={"token": token})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
class TestUserMe:
|
||||
|
||||
async def test_authenticated_user_can_retrieve_own_data(
|
||||
@ -327,15 +331,6 @@ class TestUserMe:
|
||||
assert user.email == test_user.email
|
||||
assert user.user_id == test_user.user_id
|
||||
|
||||
async def test_user_cannot_access_own_data_if_not_authenticated(
|
||||
self, app: FastAPI,
|
||||
unauthorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
) -> None:
|
||||
|
||||
response = await unauthorized_client.get(app.url_path_for("get_logged_in_user"))
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
async def test_authenticated_user_can_update_own_data(
|
||||
self,
|
||||
app: FastAPI,
|
||||
|
@ -25,38 +25,41 @@ from gns3server.version import __version__
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
async def test_version_output(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_version"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'controller_host': '127.0.0.1', 'local': False, 'version': __version__}
|
||||
class TestVersionRoutes:
|
||||
|
||||
|
||||
async def test_version_input(app: FastAPI, client: AsyncClient) -> None:
|
||||
async def test_version_output(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = {'version': __version__}
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'version': __version__}
|
||||
|
||||
|
||||
async def test_version_invalid_input(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = {'version': "0.4.2"}
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
assert response.json() == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__)}
|
||||
|
||||
|
||||
async def test_version_invalid_input_schema(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = {'version': "0.4.2", "bla": "blu"}
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
|
||||
|
||||
async def test_version_invalid_json(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = "BOUM"
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
response = await client.get(app.url_path_for("get_version"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'controller_host': '127.0.0.1', 'local': False, 'version': __version__}
|
||||
|
||||
|
||||
async def test_version_input(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = {'version': __version__}
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {'version': __version__}
|
||||
|
||||
|
||||
async def test_version_invalid_input(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = {'version': "0.4.2"}
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
assert response.json() == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__)}
|
||||
|
||||
|
||||
async def test_version_invalid_input_schema(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = {'version': "0.4.2", "bla": "blu"}
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_409_CONFLICT
|
||||
|
||||
|
||||
async def test_version_invalid_json(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
params = "BOUM"
|
||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
@ -29,55 +29,57 @@ from gns3server.utils.get_resource import get_resource
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
def get_static(filename):
|
||||
class TestIndexRoutes:
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
return os.path.join(os.path.abspath(os.path.join(current_dir, '../..', '..', 'gns3server', 'static')), filename)
|
||||
def get_static(self, filename):
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
return os.path.join(os.path.abspath(os.path.join(current_dir, '../..', '..', 'gns3server', 'static')), filename)
|
||||
|
||||
|
||||
async def test_debug(app: FastAPI, client: AsyncClient) -> None:
|
||||
async def test_debug(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("debug"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
html = response.read().decode()
|
||||
assert "Website" in html
|
||||
assert __version__ in html
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_controller(http_client, controller):
|
||||
#
|
||||
# await controller.add_project(name="test")
|
||||
# response = await http_client.get('/controller')
|
||||
# assert "test" in await response.text()
|
||||
# assert response.status_code == 200
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_compute(http_client):
|
||||
#
|
||||
# response = await http_client.get('/compute')
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_project(http_client, controller):
|
||||
#
|
||||
# project = await controller.add_project(name="test")
|
||||
# response = await http_client.get('/projects/{}'.format(project.id))
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
async def test_web_ui(app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("web_ui", file_path="index.html"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_web_ui_not_found(app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
|
||||
|
||||
with patch('gns3server.utils.get_resource.get_resource') as mock:
|
||||
mock.return_value = str(tmpdir)
|
||||
response = await client.get(app.url_path_for("web_ui", file_path="not-found.txt"))
|
||||
# should serve web-ui/index.html
|
||||
response = await client.get(app.url_path_for("debug"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
html = response.read().decode()
|
||||
assert "Website" in html
|
||||
assert __version__ in html
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_controller(http_client, controller):
|
||||
#
|
||||
# await controller.add_project(name="test")
|
||||
# response = await http_client.get('/controller')
|
||||
# assert "test" in await response.text()
|
||||
# assert response.status_code == 200
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_compute(http_client):
|
||||
#
|
||||
# response = await http_client.get('/compute')
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_project(http_client, controller):
|
||||
#
|
||||
# project = await controller.add_project(name="test")
|
||||
# response = await http_client.get('/projects/{}'.format(project.id))
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
async def test_web_ui(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("web_ui", file_path="index.html"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
async def test_web_ui_not_found(self, app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
|
||||
|
||||
with patch('gns3server.utils.get_resource.get_resource') as mock:
|
||||
mock.return_value = str(tmpdir)
|
||||
response = await client.get(app.url_path_for("web_ui", file_path="not-found.txt"))
|
||||
# should serve web-ui/index.html
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
@ -44,40 +44,49 @@ ALLOWED_CONTROLLER_ENDPOINTS = [
|
||||
("/v3/symbols/default_symbols", "GET")
|
||||
]
|
||||
|
||||
class TestRoutes:
|
||||
|
||||
# Controller endpoints have a OAuth2 bearer token authentication
|
||||
async def test_controller_endpoints_require_authentication(app: FastAPI, unauthorized_client: AsyncClient) -> None:
|
||||
# Controller endpoints have a OAuth2 bearer token authentication
|
||||
async def test_controller_endpoints_require_authentication(
|
||||
self,
|
||||
app: FastAPI,
|
||||
unauthorized_client: AsyncClient
|
||||
) -> None:
|
||||
|
||||
for route in app.routes:
|
||||
if isinstance(route, APIRoute):
|
||||
for method in list(route.methods):
|
||||
if (route.path, method) not in ALLOWED_CONTROLLER_ENDPOINTS:
|
||||
response = await getattr(unauthorized_client, method.lower())(route.path)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
elif isinstance(route, APIWebSocketRoute):
|
||||
params = {"token": "wrong_token"}
|
||||
async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app)) as client:
|
||||
async with aconnect_ws(route.path, client, params=params) as ws:
|
||||
json_notification = await ws.receive_json()
|
||||
assert json_notification['event'] == {
|
||||
'message': 'Could not authenticate while connecting to controller WebSocket: Could not validate credentials'
|
||||
}
|
||||
|
||||
|
||||
# Compute endpoints have a basic HTTP authentication
|
||||
async def test_compute_endpoints_require_authentication(app: FastAPI, unauthorized_client: AsyncClient) -> None:
|
||||
|
||||
for route in app.routes:
|
||||
if isinstance(route, Mount):
|
||||
for compute_route in route.routes:
|
||||
if isinstance(compute_route, APIRoute):
|
||||
for method in list(compute_route.methods):
|
||||
response = await getattr(unauthorized_client, method.lower())(route.path + compute_route.path)
|
||||
for route in app.routes:
|
||||
if isinstance(route, APIRoute):
|
||||
for method in list(route.methods):
|
||||
if (route.path, method) not in ALLOWED_CONTROLLER_ENDPOINTS:
|
||||
response = await getattr(unauthorized_client, method.lower())(route.path)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
elif isinstance(compute_route, APIWebSocketRoute):
|
||||
async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app)) as client:
|
||||
async with aconnect_ws(route.path + compute_route.path, client, auth=("wrong_user", "password123")) as ws:
|
||||
json_notification = await ws.receive_json()
|
||||
assert json_notification['event'] == {
|
||||
'message': 'Could not authenticate while connecting to compute WebSocket: Could not validate credentials'
|
||||
}
|
||||
elif isinstance(route, APIWebSocketRoute):
|
||||
params = {"token": "wrong_token"}
|
||||
async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app=app)) as client:
|
||||
async with aconnect_ws(route.path, client, params=params) as ws:
|
||||
json_notification = await ws.receive_json()
|
||||
assert json_notification['event'] == {
|
||||
'message': 'Could not authenticate while connecting to controller WebSocket: Could not validate credentials'
|
||||
}
|
||||
|
||||
|
||||
# Compute endpoints have a basic HTTP authentication
|
||||
async def test_compute_endpoints_require_authentication(
|
||||
self,
|
||||
app: FastAPI,
|
||||
unauthorized_client: AsyncClient
|
||||
) -> None:
|
||||
|
||||
for route in app.routes:
|
||||
if isinstance(route, Mount):
|
||||
for compute_route in route.routes:
|
||||
if isinstance(compute_route, APIRoute):
|
||||
for method in list(compute_route.methods):
|
||||
response = await getattr(unauthorized_client, method.lower())(route.path + compute_route.path)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
elif isinstance(compute_route, APIWebSocketRoute):
|
||||
async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app=app)) as client:
|
||||
async with aconnect_ws(route.path + compute_route.path, client, auth=("wrong_user", "password123")) as ws:
|
||||
json_notification = await ws.receive_json()
|
||||
assert json_notification['event'] == {
|
||||
'message': 'Could not authenticate while connecting to compute WebSocket: Could not validate credentials'
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ from gns3server.compute.nios.nio_udp import NIOUDP
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
@pytest.fixture
|
||||
def nio():
|
||||
|
||||
@ -39,7 +41,6 @@ async def manager():
|
||||
return m
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_json_with_ports(on_gns3vm, compute_project, manager):
|
||||
|
||||
ports = [
|
||||
@ -78,7 +79,7 @@ async def test_json_with_ports(on_gns3vm, compute_project, manager):
|
||||
}
|
||||
|
||||
|
||||
def test_json_without_ports(on_gns3vm, compute_project, manager):
|
||||
async def test_json_without_ports(on_gns3vm, compute_project, manager):
|
||||
"""
|
||||
If no interface is provide the cloud is pre-fill with non special interfaces
|
||||
"""
|
||||
@ -117,7 +118,6 @@ def test_json_without_ports(on_gns3vm, compute_project, manager):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_port_mappings(on_gns3vm, compute_project):
|
||||
"""
|
||||
We don't allow an empty interface in the middle of port list
|
||||
@ -158,7 +158,6 @@ async def test_update_port_mappings(on_gns3vm, compute_project):
|
||||
assert cloud.ports_mapping == ports1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_linux_ethernet_raw_add_nio(linux_platform, compute_project, nio):
|
||||
ports = [
|
||||
{
|
||||
@ -186,7 +185,6 @@ async def test_linux_ethernet_raw_add_nio(linux_platform, compute_project, nio):
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_linux_ethernet_raw_add_nio_bridge(linux_platform, compute_project, nio):
|
||||
"""
|
||||
Bridge can't be connected directly to a cloud we use a tap in the middle
|
||||
|
@ -1,5 +1,4 @@
|
||||
import pytest
|
||||
import asyncio
|
||||
import pytest_asyncio
|
||||
import tempfile
|
||||
import shutil
|
||||
@ -13,6 +12,7 @@ import stat
|
||||
from fastapi import FastAPI
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
||||
from httpx import AsyncClient
|
||||
from httpx_ws.transport import ASGIWebSocketTransport
|
||||
from unittest.mock import MagicMock, patch
|
||||
from pathlib import Path
|
||||
|
||||
@ -34,25 +34,14 @@ sys._called_from_test = True
|
||||
sys.original_platform = sys.platform
|
||||
|
||||
|
||||
# https://github.com/pytest-dev/pytest-asyncio/issues/68
|
||||
# this event_loop is used by pytest-asyncio, and redefining it
|
||||
# is currently the only way of changing the scope of this fixture
|
||||
@pytest.fixture(scope="class")
|
||||
def event_loop(request):
|
||||
|
||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="class")
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def app() -> FastAPI:
|
||||
|
||||
from gns3server.api.server import app as gns3app
|
||||
yield gns3app
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="class")
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def db_engine():
|
||||
|
||||
db_url = os.getenv("GNS3_TEST_DATABASE_URI", "sqlite+aiosqlite:///:memory:") # "sqlite:///./sql_test_app.db"
|
||||
@ -61,7 +50,7 @@ async def db_engine():
|
||||
#await engine.sync_engine.dispose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="class")
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def db_session(db_engine):
|
||||
|
||||
# recreate database tables for each class
|
||||
@ -82,7 +71,7 @@ async def db_session(db_engine):
|
||||
await session.close()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def base_client(app: FastAPI, db_session: AsyncSession) -> AsyncClient:
|
||||
|
||||
async def _get_test_db():
|
||||
@ -94,14 +83,14 @@ async def base_client(app: FastAPI, db_session: AsyncSession) -> AsyncClient:
|
||||
app.dependency_overrides[get_db_session] = _get_test_db
|
||||
|
||||
async with AsyncClient(
|
||||
app=app,
|
||||
base_url="http://test-api",
|
||||
headers={"Content-Type": "application/json"}
|
||||
headers={"Content-Type": "application/json"},
|
||||
transport=ASGIWebSocketTransport(app=app)
|
||||
) as async_client:
|
||||
yield async_client
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def test_user(db_session: AsyncSession) -> User:
|
||||
|
||||
new_user = schemas.UserCreate(
|
||||
@ -121,7 +110,7 @@ async def test_user(db_session: AsyncSession) -> User:
|
||||
return user
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def test_compute(db_session: AsyncSession) -> Compute:
|
||||
|
||||
new_compute = schemas.ComputeCreate(
|
||||
@ -140,12 +129,12 @@ async def test_compute(db_session: AsyncSession) -> Compute:
|
||||
return await compute_repo.create_compute(new_compute)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
def unauthorized_client(base_client: AsyncClient, test_user: User) -> AsyncClient:
|
||||
return base_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
def authorized_client(base_client: AsyncClient, test_user: User) -> AsyncClient:
|
||||
|
||||
access_token = auth_service.create_access_token(test_user.username)
|
||||
@ -156,7 +145,7 @@ def authorized_client(base_client: AsyncClient, test_user: User) -> AsyncClient:
|
||||
return base_client
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def client(base_client: AsyncClient) -> AsyncClient:
|
||||
|
||||
# The super admin is automatically created when the users table is created
|
||||
@ -169,7 +158,7 @@ async def client(base_client: AsyncClient) -> AsyncClient:
|
||||
return base_client
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
@pytest_asyncio.fixture(loop_scope="class", scope="class")
|
||||
async def compute_client(base_client: AsyncClient) -> AsyncClient:
|
||||
|
||||
# default compute username is 'gns3'
|
||||
@ -400,10 +389,12 @@ def run_around_tests(monkeypatch, config, port_manager):
|
||||
config.settings.VMware.vmrun_path = tmppath
|
||||
config.settings.Dynamips.dynamips_path = tmppath
|
||||
|
||||
|
||||
# Force turn off KVM because it's not available on CI
|
||||
config.settings.Qemu.enable_hardware_acceleration = False
|
||||
|
||||
# avoid monitoring for new images while testing
|
||||
config.settings.Server.auto_discover_images = False
|
||||
|
||||
monkeypatch.setattr("gns3server.utils.path.get_default_project_directory", lambda *args: os.path.join(tmppath, 'projects'))
|
||||
|
||||
# Force sys.platform to the original value. Because it seems not be restored correctly after each test
|
||||
|
@ -245,7 +245,8 @@ async def test_start(controller):
|
||||
}
|
||||
|
||||
#with asyncio_patch("gns3server.controller.compute.Compute.connect") as mock:
|
||||
await controller.start()
|
||||
with asyncio_patch("gns3server.controller.Controller._install_builtin_disks", return_value=[]):
|
||||
await controller.start()
|
||||
#assert mock.called
|
||||
assert len(controller.computes) == 1 # Local compute is created
|
||||
assert controller.computes["local"].name == f"{socket.gethostname()} (controller)"
|
||||
@ -266,8 +267,9 @@ async def test_start_vm(controller):
|
||||
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start") as mock:
|
||||
with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network"):
|
||||
with asyncio_patch("gns3server.controller.compute.Compute.connect"):
|
||||
await controller.start()
|
||||
assert mock.called
|
||||
with asyncio_patch("gns3server.controller.Controller._install_builtin_disks", return_value=[]):
|
||||
await controller.start()
|
||||
assert mock.called
|
||||
assert "local" in controller.computes
|
||||
assert "vm" in controller.computes
|
||||
assert len(controller.computes) == 2 # Local compute and vm are created
|
||||
@ -356,7 +358,7 @@ async def test_install_base_configs(controller, config, tmpdir):
|
||||
with open(str(tmpdir / 'iou_l2_base_startup-config.txt'), 'w+') as f:
|
||||
f.write('test')
|
||||
|
||||
controller._install_base_configs()
|
||||
await controller._install_base_configs()
|
||||
assert os.path.exists(str(tmpdir / 'iou_l3_base_startup-config.txt'))
|
||||
|
||||
# Check is the file has not been overwritten
|
||||
@ -385,12 +387,13 @@ async def test_install_base_configs(controller, config, tmpdir):
|
||||
async def test_install_builtin_disks(controller, config, tmpdir, builtin_disk):
|
||||
|
||||
config.settings.Server.images_path = str(tmpdir)
|
||||
controller._install_builtin_disks()
|
||||
await controller._install_builtin_disks()
|
||||
# we only install Qemu empty disks at this time
|
||||
assert os.path.exists(str(tmpdir / "QEMU" / builtin_disk))
|
||||
|
||||
|
||||
def test_appliances(controller, config, tmpdir):
|
||||
@pytest.mark.asyncio
|
||||
async def test_appliances(controller, config, tmpdir):
|
||||
|
||||
my_appliance = {
|
||||
"name": "My Appliance",
|
||||
@ -406,7 +409,7 @@ def test_appliances(controller, config, tmpdir):
|
||||
json.dump(my_appliance, f)
|
||||
|
||||
config.settings.Server.appliances_path = str(tmpdir)
|
||||
controller.appliance_manager.install_builtin_appliances()
|
||||
await controller.appliance_manager.install_builtin_appliances()
|
||||
controller.appliance_manager.load_appliances()
|
||||
assert len(controller.appliance_manager.appliances) > 0
|
||||
for appliance in controller.appliance_manager.appliances.values():
|
||||
|
2
tests/pytest.ini
Normal file
2
tests/pytest.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
asyncio_default_fixture_loop_scope=function
|
BIN
tests/resources/empty100G.qcow2
Normal file
BIN
tests/resources/empty100G.qcow2
Normal file
Binary file not shown.
@ -21,6 +21,7 @@ import pytest
|
||||
import locale
|
||||
import tempfile
|
||||
|
||||
from gns3server.main import parse_arguments as parse
|
||||
from gns3server.server import Server
|
||||
from gns3server.config import Config
|
||||
|
||||
@ -35,12 +36,18 @@ def test_locale_check():
|
||||
assert locale.getlocale() == ('fr_FR', 'UTF-8')
|
||||
|
||||
|
||||
def parse_arguments(server, argv):
|
||||
|
||||
parser, args= parse(argv)
|
||||
return server._load_config_and_set_defaults(parser, args, argv)
|
||||
|
||||
|
||||
def test_parse_arguments(capsys, config, tmpdir):
|
||||
|
||||
server = Server()
|
||||
server_config = config.settings.Server
|
||||
with pytest.raises(SystemExit):
|
||||
server._parse_arguments(["--fail"])
|
||||
parse_arguments(server, ["--fail"])
|
||||
out, err = capsys.readouterr()
|
||||
assert "usage" in err
|
||||
assert "fail" in err
|
||||
@ -67,48 +74,49 @@ def test_parse_arguments(capsys, config, tmpdir):
|
||||
# assert __version__ in out
|
||||
# assert "optional arguments" in out
|
||||
|
||||
assert server._parse_arguments(["--host", "192.168.1.1"]).host == "192.168.1.1"
|
||||
assert server._parse_arguments([]).host == "0.0.0.0"
|
||||
assert parse_arguments(server, ["--host", "192.168.1.1"]).host == "192.168.1.1"
|
||||
assert parse_arguments(server, []).host == "0.0.0.0"
|
||||
server_config.host = "192.168.1.2"
|
||||
assert server._parse_arguments(["--host", "192.168.1.1"]).host == "192.168.1.1"
|
||||
assert server._parse_arguments([]).host == "192.168.1.2"
|
||||
|
||||
assert server._parse_arguments(["--port", "8002"]).port == 8002
|
||||
assert server._parse_arguments([]).port == 3080
|
||||
assert parse_arguments(server, ["--host", "192.168.1.1"]).host == "192.168.1.1"
|
||||
assert parse_arguments(server, []).host == "192.168.1.2"
|
||||
|
||||
assert parse_arguments(server, ["--port", "8002"]).port == 8002
|
||||
assert parse_arguments(server, []).port == 3080
|
||||
server_config.port = 8003
|
||||
assert server._parse_arguments([]).port == 8003
|
||||
assert parse_arguments(server, []).port == 8003
|
||||
|
||||
assert server._parse_arguments(["--ssl"]).ssl
|
||||
assert server._parse_arguments([]).ssl is False
|
||||
assert parse_arguments(server, ["--ssl"]).ssl
|
||||
assert parse_arguments(server,[]).ssl is False
|
||||
with tempfile.NamedTemporaryFile(dir=str(tmpdir)) as f:
|
||||
server_config.certfile = f.name
|
||||
server_config.certkey = f.name
|
||||
server_config.enable_ssl = True
|
||||
assert server._parse_arguments([]).ssl
|
||||
assert parse_arguments(server, []).ssl
|
||||
|
||||
assert server._parse_arguments(["--certfile", "bla"]).certfile == "bla"
|
||||
assert server._parse_arguments(["--certkey", "blu"]).certkey == "blu"
|
||||
assert parse_arguments(server, ["--certfile", "bla"]).certfile == "bla"
|
||||
assert parse_arguments(server,["--certkey", "blu"]).certkey == "blu"
|
||||
|
||||
assert server._parse_arguments(["-L"]).local
|
||||
assert server._parse_arguments(["--local"]).local
|
||||
assert parse_arguments(server,["-L"]).local
|
||||
assert parse_arguments(server,["--local"]).local
|
||||
server_config.local = False
|
||||
assert server._parse_arguments([]).local is False
|
||||
assert parse_arguments(server,[]).local is False
|
||||
server_config.local = True
|
||||
assert server._parse_arguments([]).local
|
||||
assert parse_arguments(server,[]).local
|
||||
|
||||
assert server._parse_arguments(["-A"]).allow
|
||||
assert server._parse_arguments(["--allow"]).allow
|
||||
assert server._parse_arguments([]).allow is False
|
||||
assert parse_arguments(server,["-A"]).allow
|
||||
assert parse_arguments(server,["--allow"]).allow
|
||||
assert parse_arguments(server,[]).allow is False
|
||||
server_config.allow_remote_console = True
|
||||
assert server._parse_arguments([]).allow
|
||||
assert parse_arguments(server,[]).allow
|
||||
|
||||
assert server._parse_arguments(["-q"]).quiet
|
||||
assert server._parse_arguments(["--quiet"]).quiet
|
||||
assert server._parse_arguments([]).quiet is False
|
||||
assert parse_arguments(server,["-q"]).quiet
|
||||
assert parse_arguments(server,["--quiet"]).quiet
|
||||
assert parse_arguments(server,[]).quiet is False
|
||||
|
||||
assert server._parse_arguments(["-d"]).debug
|
||||
assert server._parse_arguments(["--debug"]).debug
|
||||
assert server._parse_arguments([]).debug is False
|
||||
assert parse_arguments(server,["-d"]).debug
|
||||
assert parse_arguments(server,["--debug"]).debug
|
||||
assert parse_arguments(server,[]).debug is False
|
||||
|
||||
|
||||
def test_set_config_with_args(tmpdir):
|
||||
@ -118,7 +126,7 @@ def test_set_config_with_args(tmpdir):
|
||||
with tempfile.NamedTemporaryFile(dir=str(tmpdir)) as f:
|
||||
certfile = f.name
|
||||
certkey = f.name
|
||||
args = server._parse_arguments(["--host",
|
||||
args = parse_arguments(server, ["--host",
|
||||
"192.168.1.1",
|
||||
"--local",
|
||||
"--allow",
|
||||
|
Reference in New Issue
Block a user