Merge remote-tracking branch 'origin/3.0' into gh-pages

This commit is contained in:
github-actions 2022-01-17 08:34:35 +00:00
commit c554861070
103 changed files with 1564 additions and 983 deletions

View File

@ -1,5 +1,21 @@
# Change Log
## 2.2.29 08/01/2022
* Release web UI 2.2.29
* Add NixOS in list of distributions with a package
## 2.2.28 15/12/2021
* Fix compute Docker test. Fixes #2003
* Release web UI 2.2.28
* Simpler Systemd service file. Ref #1996
## 2.2.27 12/11/2021
* Release web UI 2.2.27
* Fix unhandled KeyError exception when starting Docker container. Ref #1991
## 2.2.26 08/10/2021
* Release web UI 2.2.26

View File

@ -1,21 +1,37 @@
FROM python:3.6-alpine3.11
FROM ubuntu:focal
WORKDIR /gns3server
RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y \
locales \
locales-all
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONBUFFERED 1
COPY ./requirements.txt /gns3server/requirements.txt
RUN set -eux \
&& apk add --no-cache --virtual .build-deps build-base \
gcc libc-dev musl-dev linux-headers python3-dev \
vpcs qemu libvirt ubridge \
&& pip install --no-cache-dir --upgrade pip setuptools wheel \
&& pip install --no-cache-dir -r /gns3server/requirements.txt
RUN DEBIAN_FRONTEND=noninteractive apt install -y \
locales \
software-properties-common \
python3-pip \
python3-all \
python3-setuptools \
python3-dev \
busybox-static \
gcc \
qemu-kvm \
libvirt-daemon-system
RUN add-apt-repository ppa:gns3/ppa && apt update && DEBIAN_FRONTEND=noninteractive apt install -y \
vpcs \
ubridge \
dynamips
COPY . /gns3server
RUN mkdir -p ~/.config/GNS3/3.0/
RUN cp scripts/gns3_server.conf ~/.config/GNS3/3.0/
RUN python3 setup.py install

View File

@ -58,6 +58,7 @@ GNS3 is perhaps packaged for your distribution:
* Gentoo: https://packages.gentoo.org/package/net-misc/gns3-server
* Alpine: https://pkgs.alpinelinux.org/package/v3.10/community/x86_64/gns3-server
* NixOS: https://search.nixos.org/packages?channel=21.11&from=0&size=50&sort=relevance&type=packages&query=gns3-server
Linux (Debian based)

View File

@ -1,3 +1,18 @@
[Controller]
; Options for JWT tokens (user authentication)
jwt_secret_key = efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76cea5e33d4e
jwt_algorithm = HS256
jwt_access_token_expire_minutes = 1440
; Initial default super admin username
; It cannot be changed once the controller has started once
default_admin_username = admin
; Initial default super admin password
; It cannot be changed once the controller has started once
default_admin_password = admin
[Server]
; What protocol the server uses (http or https)
@ -57,20 +72,10 @@ udp_end_port_range = 30000
; uBridge executable location, default: search in PATH
;ubridge_path = ubridge
; Option to enable HTTP authentication.
enable_http_auth = False
; Username for HTTP authentication.
user = gns3
; Password for HTTP authentication.
password = gns3
; Initial default super admin username
; It cannot be changed once the server has started once
default_admin_username = "admin"
; Initial default super admin password
; It cannot be changed once the server has started once
default_admin_password = "admin"
; Username for compute HTTP authentication.
compute_username = gns3
; Password for compute HTTP authentication.
compute_password = gns3
; Only allow these interfaces to be used by GNS3, for the Cloud node for example (Linux/OSX only)
; Do not forget to allow virbr0 in order for the NAT node to work
@ -80,12 +85,6 @@ allowed_interfaces = eth0,eth1,virbr0
; Default is virbr0 on Linux (requires libvirt) and vmnet8 for other platforms (requires VMware)
default_nat_interface = vmnet10
[Controller]
; Options for JWT tokens (user authentication)
jwt_secret_key = efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76cea5e33d4e
jwt_algorithm = HS256
jwt_access_token_expire_minutes = 1440
[VPCS]
; VPCS executable location, default: search in PATH
;vpcs_path = vpcs

View File

@ -1,8 +1,8 @@
-r requirements.txt
pytest==6.2.4
flake8==3.9.2
pytest-timeout==1.4.2
pytest-asyncio==0.15.1
pytest==6.2.5
flake8==4.0.1
pytest-timeout==2.0.1
pytest-asyncio==0.16.0
requests==2.26.0
httpx==0.18.2
httpx==0.21.1

View File

@ -15,13 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from fastapi import FastAPI, Request
from fastapi import FastAPI, Request, Depends
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
from gns3server.compute.error import ImageMissingError, NodeError
from gns3server.compute.ubridge.ubridge_error import UbridgeError
from .dependencies.authentication import compute_authentication
from gns3server.compute.compute_error import (
ComputeError,
ComputeNotFoundError,
@ -49,13 +51,15 @@ from . import virtualbox_nodes
from . import vmware_nodes
from . import vpcs_nodes
compute_api = FastAPI(
title="GNS3 compute API",
dependencies=[Depends(compute_authentication)],
description="This page describes the private compute API for GNS3. PLEASE DO NOT USE DIRECTLY!",
version="v3",
)
compute_api.state.controller_host = None
@compute_api.exception_handler(ComputeError)
async def controller_error_handler(request: Request, exc: ComputeError):

View File

@ -21,7 +21,7 @@ API routes for capabilities
import sys
import psutil
from fastapi import APIRouter
from fastapi import APIRouter, Request
from gns3server.version import __version__
from gns3server.compute import MODULES
@ -32,12 +32,15 @@ router = APIRouter()
@router.get("/capabilities", response_model=schemas.Capabilities)
def get_capabilities() -> dict:
def get_capabilities(request: Request) -> dict:
node_types = []
for module in MODULES:
node_types.extend(module.node_types())
# record the controller hostname or IP address
request.app.state.controller_host = request.client.host
return {
"version": __version__,
"platform": sys.platform,

View File

@ -0,0 +1,37 @@
#
# Copyright (C) 2021 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import secrets
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from gns3server.config import Config
from typing import Optional
security = HTTPBasic()
def compute_authentication(credentials: Optional[HTTPBasicCredentials] = Depends(security)) -> None:
server_settings = Config.instance().settings.Server
username = secrets.compare_digest(credentials.username, server_settings.compute_username)
password = secrets.compare_digest(credentials.password, server_settings.compute_password.get_secret_value())
if not (username and password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid compute username or password",
headers={"WWW-Authenticate": "Basic"},
)

View File

@ -18,7 +18,11 @@
API routes for compute notifications.
"""
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
import base64
import binascii
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, status, HTTPException
from fastapi.security.utils import get_authorization_scheme_param
from websockets.exceptions import ConnectionClosed, WebSocketException
from gns3server.compute.notification_manager import NotificationManager
@ -37,6 +41,32 @@ async def notification_ws(websocket: WebSocket) -> None:
"""
await websocket.accept()
# handle basic HTTP authentication
invalid_user_credentials_exc = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Basic"},
)
try:
authorization = websocket.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "basic":
raise invalid_user_credentials_exc
try:
data = base64.b64decode(param).decode("ascii")
except (ValueError, UnicodeDecodeError, binascii.Error):
raise invalid_user_credentials_exc
username, separator, password = data.partition(":")
if not separator:
raise invalid_user_credentials_exc
except invalid_user_credentials_exc as e:
websocket_error = {"action": "log.error", "event": {"message": f"Could not authenticate while connecting to "
f"compute WebSocket: {e.detail}"}}
await websocket.send_json(websocket_error)
return await websocket.close(code=1008)
log.info(f"New client {websocket.client.host}:{websocket.client.port} has connected to compute WebSocket")
try:
with NotificationManager.instance().queue() as queue:
@ -48,8 +78,10 @@ async def notification_ws(websocket: WebSocket) -> None:
except WebSocketException as e:
log.warning(f"Error while sending to controller event to WebSocket client: {e}")
finally:
await websocket.close()
try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
if __name__ == "__main__":

View File

@ -19,7 +19,7 @@ API routes for computes.
"""
from fastapi import APIRouter, Depends, Response, status
from typing import List, Union
from typing import List, Union, Optional
from uuid import UUID
from gns3server.controller import Controller
@ -47,12 +47,25 @@ router = APIRouter(responses=responses)
async def create_compute(
compute_create: schemas.ComputeCreate,
computes_repo: ComputesRepository = Depends(get_repository(ComputesRepository)),
connect: Optional[bool] = False
) -> schemas.Compute:
"""
Create a new compute on the controller.
"""
return await ComputesService(computes_repo).create_compute(compute_create)
return await ComputesService(computes_repo).create_compute(compute_create, connect)
@router.post("/{compute_id}/connect", status_code=status.HTTP_204_NO_CONTENT)
async def connect_compute(compute_id: Union[str, UUID]) -> Response:
"""
Connect to compute on the controller.
"""
compute = Controller.instance().get_compute(str(compute_id))
if not compute.connected:
await compute.connect(report_failed_connection=True)
return Response(status_code=status.HTTP_204_NO_CONTENT)
@router.get("/{compute_id}", response_model=schemas.Compute, response_model_exclude_unset=True)

View File

@ -18,8 +18,9 @@ import asyncio
import signal
import os
from fastapi import APIRouter, Depends, Response, status
from fastapi import APIRouter, Depends, Request, Response, status
from fastapi.encoders import jsonable_encoder
from fastapi.routing import Mount
from typing import List
from gns3server.config import Config
@ -41,13 +42,24 @@ router = APIRouter()
"/version",
response_model=schemas.Version,
)
def get_version() -> dict:
def get_version(request: Request) -> dict:
"""
Return the server version number.
"""
# retrieve the controller host information from the mounted
# compute subapp
controller_host = None
for route in request.app.routes:
if isinstance(route, Mount) and route.name == "compute":
controller_host = route.app.state.controller_host
local_server = Config.instance().settings.Server.local
return {"version": __version__, "local": local_server}
return {
"controller_host": controller_host,
"version": __version__,
"local": local_server
}
@router.post(
@ -61,7 +73,6 @@ def check_version(version: schemas.Version) -> dict:
Check if version is the same as the server.
"""
print(version.version)
if version.version != __version__:
raise ControllerError(f"Client version {version.version} is not the same as server version {__version__}")
return {"version": __version__}

View File

@ -16,8 +16,9 @@
import re
from fastapi import Request, Depends, HTTPException, status
from fastapi import Request, Query, Depends, HTTPException, WebSocket, status
from fastapi.security import OAuth2PasswordBearer
from typing import Optional
from gns3server import schemas
from gns3server.db.repositories.users import UsersRepository
@ -76,3 +77,53 @@ async def get_current_active_user(
)
return current_user
async def get_current_active_user_from_websocket(
websocket: WebSocket,
token: str = Query(...),
user_repo: UsersRepository = Depends(get_repository(UsersRepository)),
rbac_repo: RbacRepository = Depends(get_repository(RbacRepository))
) -> Optional[schemas.User]:
await websocket.accept()
try:
username = auth_service.get_username_from_token(token)
user = await user_repo.get_user_by_username(username)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=f"Could not validate credentials for '{username}'"
)
# Super admin is always authorized
if user.is_superadmin:
return user
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=f"'{username}' is not an active user"
)
# remove the prefix (e.g. "/v3") from URL path
path = re.sub(r"^/v[0-9]", "", websocket.url.path)
# there are no HTTP methods for web sockets, assuming "GET"...
authorized = await rbac_repo.check_user_is_authorized(user.user_id, "GET", path)
if not authorized:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=f"User is not authorized '{user.user_id}' on '{path}'",
headers={"WWW-Authenticate": "Bearer"},
)
return user
except HTTPException as e:
websocket_error = {"action": "log.error", "event": {"message": f"Could not authenticate while connecting to "
f"WebSocket: {e.detail}"}}
await websocket.send_json(websocket_error)
await websocket.close(code=1008)

View File

@ -15,13 +15,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Callable, Type
from fastapi import Depends, Request
from fastapi import Depends
from starlette.requests import HTTPConnection
from sqlalchemy.ext.asyncio import AsyncSession
from gns3server.db.repositories.base import BaseRepository
async def get_db_session(request: Request) -> AsyncSession:
async def get_db_session(request: HTTPConnection) -> AsyncSession:
session = AsyncSession(request.app.state._db_engine, expire_on_commit=False)
try:

View File

@ -67,7 +67,7 @@ async def upload_image(
templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)),
current_user: schemas.User = Depends(get_current_active_user),
rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)),
install_appliances: Optional[bool] = True
install_appliances: Optional[bool] = False
) -> schemas.Image:
"""
Upload an image.

View File

@ -198,7 +198,14 @@ async def stream_pcap(request: Request, link: Link = Depends(dep_link)) -> Strea
async def compute_pcap_stream():
try:
async with HTTPClient.request(request.method, pcap_streaming_url, timeout=None, data=body) as response:
async with HTTPClient.request(
request.method,
pcap_streaming_url,
user=compute.user,
password=compute.password,
timeout=None,
data=body
) as response:
async for data in response.content.iter_any():
if not data:
break

View File

@ -18,14 +18,14 @@
API routes for controller notifications.
"""
from fastapi import APIRouter, Depends, Query, WebSocket, WebSocketDisconnect, HTTPException
from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
from fastapi.responses import StreamingResponse
from websockets.exceptions import ConnectionClosed, WebSocketException
from gns3server.services import auth_service
from gns3server.controller import Controller
from gns3server import schemas
from .dependencies.authentication import get_current_active_user
from .dependencies.authentication import get_current_active_user, get_current_active_user_from_websocket
import logging
@ -35,7 +35,7 @@ router = APIRouter()
@router.get("", dependencies=[Depends(get_current_active_user)])
async def http_notification() -> StreamingResponse:
async def controller_http_notifications() -> StreamingResponse:
"""
Receive controller notifications about the controller from HTTP stream.
"""
@ -50,19 +50,16 @@ async def http_notification() -> StreamingResponse:
@router.websocket("/ws")
async def notification_ws(websocket: WebSocket, token: str = Query(None)) -> None:
async def controller_ws_notifications(
websocket: WebSocket,
current_user: schemas.User = Depends(get_current_active_user_from_websocket)
) -> None:
"""
Receive project notifications about the controller from WebSocket.
"""
await websocket.accept()
if token:
try:
username = auth_service.get_username_from_token(token)
except HTTPException:
log.error("Invalid token received")
await websocket.close(code=1008)
return
if current_user is None:
return
log.info(f"New client {websocket.client.host}:{websocket.client.port} has connected to controller WebSocket")
try:
@ -75,4 +72,7 @@ async def notification_ws(websocket: WebSocket, token: str = Query(None)) -> Non
except WebSocketException as e:
log.warning(f"Error while sending to controller event to WebSocket client: {e}")
finally:
await websocket.close()
try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected

View File

@ -51,7 +51,7 @@ from gns3server.db.repositories.rbac import RbacRepository
from gns3server.db.repositories.templates import TemplatesRepository
from gns3server.services.templates import TemplatesService
from .dependencies.authentication import get_current_active_user
from .dependencies.authentication import get_current_active_user, get_current_active_user_from_websocket
from .dependencies.database import get_repository
responses = {404: {"model": schemas.ErrorMessage, "description": "Could not find project"}}
@ -204,17 +204,12 @@ async def load_project(path: str = Body(..., embed=True)) -> schemas.Project:
controller = Controller.instance()
dot_gns3_file = path
if Config.instance().settings.Server.local is False:
log.error(f"Cannot load '{dot_gns3_file}' because the server has not been started with the '--local' parameter")
raise ControllerForbiddenError("Cannot load project when server is not local")
project = await controller.load_project(
dot_gns3_file,
)
project = await controller.load_project(dot_gns3_file)
return project.asdict()
@router.get("/{project_id}/notifications")
async def notification(project_id: UUID) -> StreamingResponse:
async def project_http_notifications(project_id: UUID) -> StreamingResponse:
"""
Receive project notifications about the controller from HTTP stream.
"""
@ -245,14 +240,20 @@ async def notification(project_id: UUID) -> StreamingResponse:
@router.websocket("/{project_id}/notifications/ws")
async def notification_ws(project_id: UUID, websocket: WebSocket) -> None:
async def project_ws_notifications(
project_id: UUID,
websocket: WebSocket,
current_user: schemas.User = Depends(get_current_active_user_from_websocket)
) -> None:
"""
Receive project notifications about the controller from WebSocket.
"""
if current_user is None:
return
controller = Controller.instance()
project = controller.get_project(str(project_id))
await websocket.accept()
log.info(f"New client has connected to the notification stream for project ID '{project.id}' (WebSocket method)")
try:
@ -265,7 +266,10 @@ async def notification_ws(project_id: UUID, websocket: WebSocket) -> None:
except WebSocketException as e:
log.warning(f"Error while sending to project event to WebSocket client: {e}")
finally:
await websocket.close()
try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
if project.auto_close:
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking
# if someone else is not connected

View File

@ -21,8 +21,7 @@ FastAPI app
import time
from fastapi import FastAPI, Request
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from sqlalchemy.exc import SQLAlchemyError
@ -53,22 +52,9 @@ def get_application() -> FastAPI:
title="GNS3 controller API", description="This page describes the public controller API for GNS3", version="v3"
)
origins = [
"http://127.0.0.1",
"http://localhost",
"http://localhost:4200",
"http://127.0.0.1:4200"
"http://127.0.0.1:8080",
"http://localhost:8080",
"http://127.0.0.1:3080",
"http://localhost:3080",
"http://gns3.github.io",
"https://gns3.github.io",
]
application.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_origin_regex=r"http(s)?://(localhost|127.0.0.1)(:\d+)?",
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
@ -78,7 +64,7 @@ def get_application() -> FastAPI:
application.add_event_handler("shutdown", tasks.create_shutdown_handler(application))
application.include_router(index.router, tags=["Index"])
application.include_router(controller.router, prefix="/v3")
application.mount("/v3/compute", compute_api)
application.mount("/v3/compute", compute_api, name="compute")
return application
@ -153,11 +139,12 @@ async def controller_bad_request_error_handler(request: Request, exc: Controller
# make sure the content key is "message", not "detail" per default
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"message": exc.detail},
headers=exc.headers
)

View File

@ -1,68 +0,0 @@
{
"appliance_id": "2def5797-cb7d-429e-b85e-497ff4b81547",
"name": "ParrotOS",
"category": "guest",
"description": " Parrot is a GNU/Linux distribution based on Debian Testing and designed with Security, Development and Privacy in mind. It includes a full portable laboratory for security and digital forensics experts, but it also includes all you need to develop your own software or protect your privacy while surfing the net.",
"vendor_name": "Parrot Project",
"vendor_url": "https://parrotsec.org/",
"documentation_url": "https://docs.parrotsec.org/doku.php",
"product_name": "ParrotOS",
"product_url": "https://parrotsec.org/",
"registry_version": 3,
"status": "stable",
"maintainer": "Brent Stewart",
"maintainer_email": "brent@stewart.tc",
"usage": "Passwords are set during installation.",
"symbol": "parrotlogo.png",
"qemu": {
"adapter_type": "e1000",
"adapters": 1,
"ram": 2048,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "vnc",
"kvm": "require"
},
"images": [
{
"filename": "Parrot-security-4.6_amd64.iso",
"version": "Security Build 4.6",
"md5sum": "ead812edc83119e8bcb4ee9daabdc105",
"filesize": 3788668928,
"download_url": "https://www.parrotsec.org/download-security.php",
"direct_download_url": "https://download.parrotsec.org/parrot/iso/4.6/Parrot-security-4.6_amd64.iso"
},
{
"filename": "Parrot-security-3.11_amd64.iso",
"version": "Security Build 3.11",
"md5sum": "71c94474fb474f682da0844d95f0040b",
"filesize": 3788668928,
"download_url": "https://www.parrotsec.org/download.fx",
"direct_download_url": "https://www.parrotsec.org/download-full.fx"
},
{
"filename": "empty30G.qcow2",
"version": "1.0",
"md5sum": "3411a599e822f2ac6be560a26405821a",
"filesize": 197120,
"download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%30disk/",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty30G.qcow2/download"
}
],
"versions": [
{
"name": "Security Build 4.6",
"images": {
"hda_disk_image": "empty30G.qcow2",
"cdrom_image": "Parrot-security-4.6_amd64.iso"
}
},
{
"name": "Security Build 3.11",
"images": {
"hda_disk_image": "empty30G.qcow2",
"cdrom_image": "Parrot-security-3.11_amd64.iso"
}
}
]
}

View File

@ -0,0 +1,52 @@
{
"appliance_id": "5c09a704-0e38-48ac-8392-52e0c5890cf3",
"name": "AlmaLinux",
"category": "guest",
"description": "An Open Source, community-governed and forever-free enterprise Linux distribution, focused on long-term stability, providing a robust production-grade platform. AlmaLinux OS is 1:1 binary compatible with RHEL and pre-Stream CentOS.",
"vendor_name": "Alma Linux",
"vendor_url": "https://almalinux.org/",
"documentation_url": "https://wiki.almalinux.org/",
"product_name": "Alma Linux",
"product_url": "https://almalinux.org/",
"registry_version": 4,
"status": "stable",
"maintainer": "Da-Geek",
"maintainer_email": "dageek@dageeks-geeks.gg",
"usage": "Username:\talmalinux\nPassword:\talmalinux\nTo become root, use \"sudo -i\".\n",
"port_name_format": "eth{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
"ram": 1536,
"hda_disk_interface": "sata",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "allow"
},
"images": [
{
"filename": "AlmaLinux-8-GenericCloud-8.5-20211119.x86_64.qcow2",
"version": "8.5",
"md5sum": "a64ece809ae06180ac59cfa622d98af0",
"filesize": 561774592,
"download_url": "https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/",
"direct_download_url": "https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-8.5-20211119.x86_64.qcow2"
},
{
"filename": "almalinux-cloud-init-data.iso",
"version": "1.0",
"md5sum": "72fb52af76e9561d125dd99224e2c1d1",
"filesize": 374784,
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/AlmaLinux/almalinux-cloud-init-data.iso"
}
],
"versions": [
{
"name": "8.5",
"images": {
"hda_disk_image": "AlmaLinux-8-GenericCloud-8.5-20211119.x86_64.qcow2",
"cdrom_image": "almalinux-cloud-init-data.iso"
}
}
]
}

View File

@ -11,7 +11,7 @@
"availability": "service-contract",
"maintainer": "Aruba",
"maintainer_email": "vincent.giles@hpe.com",
"usage": "Default username admin has to be set at first login.",
"usage": "Default username: admin, no password. New Password has to be set at first login.",
"symbol": ":/symbols/route_switch_processor.svg",
"first_port_name": "mgmt",
"port_name_format": "1/1/{port1}",
@ -30,6 +30,34 @@
"process_priority": "normal"
},
"images": [
{
"filename": "arubaoscx-disk-image-genericx86-p4-20211206170615.vmdk",
"version": "10.09.0002",
"md5sum": "3c772546482013495e31fc9cb0591b46",
"filesize": 555656704,
"download_url": "https://asp.arubanetworks.com/"
},
{
"filename": "arubaoscx-disk-image-genericx86-p4-20210812172945.vmdk",
"version": "10.08.0001",
"md5sum": "762b139432aef1012d8be5afdfcb286e",
"filesize": 542237696,
"download_url": "https://asp.arubanetworks.com/"
},
{
"filename": "arubaoscx-disk-image-genericx86-p4-20210610000730.vmdk",
"version": "10.07.0010",
"md5sum": "396dc7ad964b7c517e01bc408e3bf84a",
"filesize": 531603968,
"download_url": "https://asp.arubanetworks.com/"
},
{
"filename": "arubaoscx-disk-image-genericx86-p4-20210316185909.vmdk",
"version": "10.06.0110",
"md5sum": "f1ed67d5c7e009e21bfb6a91d9183e8e",
"filesize": 381285376,
"download_url": "https://asp.arubanetworks.com/"
},
{
"filename": "arubaoscx-disk-image-genericx86-p4-20201110192651.vmdk",
"version": "10.06.0001",
@ -53,6 +81,30 @@
}
],
"versions": [
{
"name": "10.09.0002",
"images": {
"hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20211206170615.vmdk"
}
},
{
"name": "10.08.0001",
"images": {
"hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20210812172945.vmdk"
}
},
{
"name": "10.07.0010",
"images": {
"hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20210610000730.vmdk"
}
},
{
"name": "10.06.0110",
"images": {
"hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20210316185909.vmdk"
}
},
{
"name": "10.06.0001",
"images": {

View File

@ -26,6 +26,13 @@
"options": "-nographic"
},
"images": [
{
"filename": "CentOS-7-x86_64-GenericCloud-2111.qcow2",
"version": "7 (2111)",
"md5sum": "730b8662695831670721c8245be61dac",
"filesize": 897384448,
"download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2111.qcow2"
},
{
"filename": "CentOS-7-x86_64-GenericCloud-1809.qcow2",
"version": "7 (1809)",
@ -33,6 +40,13 @@
"filesize": 914948096,
"download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1809.qcow2"
},
{
"filename": "CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2",
"version": "8.4 (2105)",
"md5sum": "032eed270415526546eac07628905a62",
"filesize": 1309652992,
"download_url": "https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2"
},
{
"filename": "centos-cloud-init-data.iso",
"version": "1.0",
@ -42,6 +56,20 @@
}
],
"versions": [
{
"name": "8.4 (2105)",
"images": {
"hda_disk_image": "CentOS-8-GenericCloud-8.4.2105-20210603.0.x86_64.qcow2",
"cdrom_image": "centos-cloud-init-data.iso"
}
},
{
"name": "7 (2111)",
"images": {
"hda_disk_image": "CentOS-7-x86_64-GenericCloud-2111.qcow2",
"cdrom_image": "centos-cloud-init-data.iso"
}
},
{
"name": "7 (1809)",
"images": {

View File

@ -0,0 +1,46 @@
{
"appliance_id": "2d5634dc-ad39-46cf-a2fd-17b291abab91",
"name": "Citrix SD-WAN",
"category": "router",
"description": "A software-defined wide area network (SD-WAN) is a virtual WAN architecture, in which any blend of network transport types — not only multiprotocol label switching (MPLS) but also broadband internet, cellular, and satellite — can be virtualized and bonded then centrally managed in software, to securely connect users to applications and desktops in accordance with policy. Essentially, SD-WAN is software-defined networking (SDN) for the WAN.",
"vendor_name": "Citrix",
"vendor_url": "http://www.citrix.com/",
"documentation_url": "https://docs.citrix.com/en-us/citrix-sd-wan",
"product_name": "Citrix SD-WAN",
"product_url": "https://docs.citrix.com/en-us/citrix-sd-wan",
"registry_version": 4,
"status": "stable",
"maintainer": "Kiel Czu",
"maintainer_email": "kielczu@gmail.com",
"usage": "The image file is large (3GB), make sure you have enough space. Default credentials: admin/ password",
"port_name_format": "1/{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 4,
"ram": 4096,
"cpus": 4,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
"boot_priority": "cd",
"kvm": "require",
"options": "-cpu Nehalem"
},
"images": [
{
"filename": "ctx-sdw-os-11.4.1.27_kvm.qcow2",
"version": "11.4.1.27",
"md5sum": "e57d8fcf8c136cc3fd2359103d885462",
"filesize": 3235315712,
"download_url": "https://www.citrix.com/pl-pl/downloads/citrix-sd-wan/standard-premium-edition/vpx-release-114127.html"
}
],
"versions": [
{
"name": "11.4.1.27",
"images": {
"hda_disk_image": "ctx-sdw-os-11.4.1.27_kvm.qcow2"
}
}
]
}

View File

@ -0,0 +1,46 @@
{
"appliance_id": "9021f581-a54d-48e0-bb84-8286ce6e08f0",
"name": "Citrix SD-WAN Center",
"category": "guest",
"description": "Citrix SD-WAN Center is a centralized management system that enables you to configure, monitor, and analyze all the Citrix SD-WAN appliances on your WAN.",
"vendor_name": "Citrix",
"vendor_url": "http://www.citrix.com/",
"documentation_url": "https://docs.citrix.com/en-us/citrix-sd-wan",
"product_name": "Citrix SD-WAN Center",
"product_url": "https://docs.citrix.com/en-us/citrix-sd-wan",
"registry_version": 4,
"status": "stable",
"maintainer": "Kiel Czu",
"maintainer_email": "kielczu@gmail.com",
"usage": "The image file is large (1.6 GB), make sure you have enough space. Default credentials: admin/ password",
"port_name_format": "1/{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 4,
"ram": 8192,
"cpus": 4,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
"boot_priority": "cd",
"kvm": "require",
"options": "-smbios type=2,product='440BX Desktop Reference Platform'"
},
"images": [
{
"filename": "ctx-sdwc-11.4.1.27.qcow2",
"version": "11.4.1.27",
"md5sum": "d788035384a53a7b73c853383efae49a",
"filesize": 1661861888,
"download_url": "https://www.citrix.com/pl-pl/downloads/citrix-sd-wan/citrix-sd-wan-standard-premium-advance-edition/management-console-release-114127.html"
}
],
"versions": [
{
"name": "11.4.1.27",
"images": {
"hda_disk_image": "ctx-sdwc-11.4.1.27.qcow2"
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"appliance_id": "fb5797d0-512e-4ab2-a588-d4766a441000",
"name": "Debian",
"category": "guest",
"description": "Debian is a GNU/Linux distribution composed of free and open-source software, developed by the community-supported Debian Project.",
"vendor_name": "Debian",
"vendor_url": "https://www.debian.org",
"product_name": "Debian",
"registry_version": 3,
"status": "experimental",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "Username:\tdebian\nPassword:\tdebian\nTo become root, use \"sudo -s\".\n\nNetwork configuration:\n- In \"/etc/network/interfaces\" comment out \"source-directory /run/network/interfaces.d\"\n- Remove \"/etc/network/interfaces.d/50-cloud-init\"\n- Create \"/etc/network/interfaces.d/10-ens4\", for example:\n\nauto ens4\n#iface ens4 inet dhcp\niface ens4 inet static\n address 10.1.1.100/24\n gateway 10.1.1.1\n dns-nameservers 10.1.1.1\n",
"symbol": "linux_guest.svg",
"port_name_format": "ens{port4}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
"ram": 512,
"hda_disk_interface": "scsi",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "allow"
},
"images": [
{
"filename": "debian-11-genericcloud-amd64-20211220-862.qcow2",
"version": "11.2",
"md5sum": "3bdc52b0b3622a72095efdd001780a45",
"filesize": 253231104,
"download_url": "https://cloud.debian.org/images/cloud/bullseye/",
"direct_download_url": "https://cloud.debian.org/images/cloud/bullseye/20211220-862/debian-11-genericcloud-amd64-20211220-862.qcow2"
},
{
"filename": "debian-10-genericcloud-amd64-20211011-792.qcow2",
"version": "10.11",
"md5sum": "ea4de19b17d114b6db813ee64a6b8284",
"filesize": 232980480,
"download_url": "https://cloud.debian.org/images/cloud/buster/",
"direct_download_url": "https://cloud.debian.org/images/cloud/buster/20211011-792/debian-10-genericcloud-amd64-20211011-792.qcow2"
},
{
"filename": "debian-cloud-init-data.iso",
"version": "1.0",
"md5sum": "43f6bf70c178a9d3c270b5c24971e578",
"filesize": 374784,
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/Debian/debian-cloud-init-data.iso"
}
],
"versions": [
{
"name": "11.2",
"images": {
"hda_disk_image": "debian-11-genericcloud-amd64-20211220-862.qcow2",
"cdrom_image": "debian-cloud-init-data.iso"
}
},
{
"name": "10.11",
"images": {
"hda_disk_image": "debian-10-genericcloud-amd64-20211011-792.qcow2",
"cdrom_image": "debian-cloud-init-data.iso"
}
}
]
}

View File

@ -1,45 +0,0 @@
{
"appliance_id": "8f8df56b-605d-447c-94a4-4848e3ae8392",
"name": "Debian 10 Minimal",
"category": "guest",
"description": "Debian 10 Custom, with automatic disk resize and ssh/nmap",
"vendor_name": "Debian",
"vendor_url": "https://debian.org",
"documentation_url": "https://wiki.debian.org",
"product_name": "Debian",
"product_url": "https://debian.org",
"registry_version": 3,
"status": "stable",
"maintainer": "Ramiro Magallanes",
"maintainer_email": "ramiro@gnubit.com",
"usage": "Username: root\nPassword: debian | MD5: 860d5051877bf4246eabc0bcb391b9a1",
"port_name_format": "enp1s{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
"ram": 2048,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "vnc",
"boot_priority": "c",
"kvm": "require",
"options": "-vga virtio"
},
"images": [
{
"filename": "debian10-gns3.qcow2",
"version": "10.2.0",
"md5sum": "860d5051877bf4246eabc0bcb391b9a1",
"filesize": 2685009920,
"download_url": "https://downloads.gnubit.com/gns3-deb10-min/"
}
],
"versions": [
{
"name": "10.2.0",
"images": {
"hda_disk_image": "debian10-gns3.qcow2"
}
}
]
}

View File

@ -0,0 +1,53 @@
{
"appliance_id": "0d505121-14e3-4414-88ab-b7f102ea6176",
"name": "Fedora Cloud Base",
"category": "guest",
"description": "Fedora Official image for self-hosted cloud",
"vendor_name": "The Fedora Project",
"vendor_url": "https://getfedora.org/",
"documentation_url": "https://docs.fedoraproject.org/en-US/docs/",
"product_name": "Fedora Cloud Base",
"product_url": "https://alt.fedoraproject.org/cloud/",
"registry_version": 3,
"status": "stable",
"maintainer": "Da-Geek",
"maintainer_email": "dageek@dageeks-geeks.gg",
"usage": "Username: fedora\nPassword: fedora",
"port_name_format": "Eth{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": "Fedora-Cloud-Base-35-1.2.x86_64.qcow2",
"version": "35-1.2",
"md5sum": "cfa9cdcfb946e5f4cf9dd4d7906008d0",
"filesize": 376897536,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/x86_64/images/Fedora-Cloud-Base-35-1.2.x86_64.qcow2"
},
{
"filename": "fedora-cloud-init-data.iso",
"version": "1.0",
"md5sum": "3d0d6391d3f5ece1180c70b9667c4dca",
"filesize": 374784,
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/fedora-cloud/fedora-cloud-init-data.iso"
}
],
"versions": [
{
"name": "35-1.2",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-35-1.2.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
}
]
}

View File

@ -0,0 +1,45 @@
{
"appliance_id": "c83b42bb-7f8f-4273-94a5-395384898da4",
"name": "LANCOM vRouter",
"category": "router",
"description": "\"The LANCOM vRouter is a software-based router for operation in virtualized environments [..]. With its comprehensive range of functions and the numerous security features based on the operating system LCOS, it offers the best basis for modern infrastructures. Be it as a virtual VPN router (vCPE), as central-site VPN gateway (vGateway), or as WLAN controller (vWLC), [..].\" quote from 'product_url'",
"vendor_name": "LANCOM Systems GmbH",
"vendor_url": "https://www.lancom-systems.com",
"product_name": "vRouter",
"product_url": "https://www.lancom-systems.com/products/routers-vpn-gateways/central-site-vpn-gateways/lancom-vrouter/",
"registry_version": 4,
"status": "experimental",
"availability": "free-to-try",
"maintainer": "hirnpfirsich",
"maintainer_email": "hirnpfirsich@brainpeach.de",
"usage": "The vRouter installs itself on first boot\nAfterwards set the root/administrative password via the console\nETH-0 is the LAN facing interface. If there is already an dhcp server on ETH-0 the vRouter requests an address. Otherwise it will run it's own dhcp server (172.23.56.254)\nConfigure via console/ssh(root@<ip>)/WebGUI(https://<ip>)/LANConfig/...",
"port_name_format": "ETH-{port1}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 5,
"ram": 1024,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "require"
},
"images": [
{
"filename": "LANCOM-VROUTER-installer-10.50.0145-Rel.img",
"version": "10.50.0145-Rel-KVM",
"md5sum": "afa50e257d2703acb3ed3257962b2fb5",
"filesize": 536870912,
"download_url": "https://www.lancom-systems.de/downloads/",
"direct_download_url": "https://www.lancom-systems.de/download/firmware/?id=fece9b54978e2af8f7a161798fff2a16&file=LC-vRouter/LC-vRouter-10.50.0145-Rel-img.zip",
"compression": "zip"
}
],
"versions": [
{
"name": "10.50.0145-Rel-KVM",
"images": {
"hda_disk_image": "LANCOM-VROUTER-installer-10.50.0145-Rel.img"
}
}
]
}

View File

@ -28,200 +28,80 @@
},
"images": [
{
"filename": "chr-7.0beta8.img",
"version": "7.0beta8",
"md5sum": "dbc5b9a1d7cc0e56d5361a0e212dbd96",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.0beta8/chr-7.0beta8.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.47.img",
"version": "6.47",
"md5sum": "39dea5d6a58708eebfa73332456566f4",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.47/chr-6.47.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.46.5.img",
"version": "6.46.5",
"md5sum": "f9ea37f5a5ac3110f8f5de33e24a7749",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.46.5/chr-6.46.5.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.45.8.img",
"version": "6.45.8",
"md5sum": "73cc01e22e0b301dc29416f59ced8a7d",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.45.8/chr-6.45.8.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.45.6.img",
"version": "6.45.6",
"md5sum": "e68db699ba23ac7e4fba95b3075c1c6b",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.45.6/chr-6.45.6.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.44.5.img",
"version": "6.44.5",
"md5sum": "19aa21073c8ea4540daacde69bacda24",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.44.5/chr-6.44.5.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.44.3.img",
"version": "6.44.3",
"md5sum": "c46b33125d536faa24473a519abbb89d",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.44.3/chr-6.44.3.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.44.2.img",
"version": "6.44.2",
"md5sum": "f1ddaa47829e12c1f9f023d8c06b88cc",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.44.2/chr-6.44.2.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.44.img",
"version": "6.44",
"md5sum": "86fdf9f0093b4e8f9e7b1c8019fb37b5",
"filesize": 33621035,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.44/chr-6.44.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.43.8.img",
"version": "6.43.8",
"md5sum": "9437133fc7e77779dc3ff62b98f30dc2",
"filename": "chr-7.1rc7.img",
"version": "7.1rc7",
"md5sum": "04bc0ae1e5fbbda1522135bc57cf6560",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.43.8/chr-6.43.8.img.zip",
"direct_download_url": "https://download.mikrotik.com/routeros/7.1rc7/chr-7.1rc7.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.42.12.img",
"version": "6.42.12",
"md5sum": "8485c606eb38e629fb1f5356d31bbc86",
"filesize": 45537201,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.42.12/chr-6.42.12.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.42.9.img",
"version": "6.42.9",
"md5sum": "1f856978cfc3ceb4b5e18e4e079f2e32",
"filename": "chr-7.1.img",
"version": "7.1",
"md5sum": "41545bc7b55717fe5bb1e489ee39ca45",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.42.9/chr-6.42.9.img.zip",
"direct_download_url": "https://download.mikrotik.com/routeros/7.1/chr-7.1.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.42.img",
"version": "6.42",
"md5sum": "279bb518497b40f41c8585128916a2fb",
"filesize": 134217728,
"filename": "chr-6.49rc2.img",
"version": "6.49rc2",
"md5sum": "e1088f8f64ac3d6ecf2e56ac96261226",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.42/chr-6.42.img.zip",
"direct_download_url": "https://download.mikrotik.com/routeros/6.49rc2/chr-6.49rc2.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.49.1.img",
"version": "6.49.1",
"md5sum": "6c896c4c853de99f2ea77f0f4b102261",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.49.1/chr-6.49.1.img.zip",
"compression": "zip"
},
{
"filename": "chr-6.48.5.img",
"version": "6.48.5",
"md5sum": "d14debd4cd989f16f695b5b075960703",
"filesize": 67108864,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/6.48.5/chr-6.48.5.img.zip",
"compression": "zip"
}
],
"versions": [
{
"name": "7.0beta8",
"name": "7.1rc7",
"images": {
"hda_disk_image": "chr-7.0beta8.img"
"hda_disk_image": "chr-7.1rc7.img"
}
},
{
"name": "6.47",
"name": "7.1",
"images": {
"hda_disk_image": "chr-6.47.img"
"hda_disk_image": "chr-7.1.img"
}
},
{
"name": "6.46.5",
"name": "6.49rc2",
"images": {
"hda_disk_image": "chr-6.46.5.img"
"hda_disk_image": "chr-6.49rc2.img"
}
},
{
"name": "6.45.8",
"name": "6.49.1",
"images": {
"hda_disk_image": "chr-6.45.8.img"
"hda_disk_image": "chr-6.49.1.img"
}
},
{
"name": "6.45.6",
"name": "6.48.5",
"images": {
"hda_disk_image": "chr-6.45.6.img"
}
},
{
"name": "6.44.5",
"images": {
"hda_disk_image": "chr-6.44.5.img"
}
},
{
"name": "6.44.3",
"images": {
"hda_disk_image": "chr-6.44.3.img"
}
},
{
"name": "6.44.2",
"images": {
"hda_disk_image": "chr-6.44.2.img"
}
},
{
"name": "6.44",
"images": {
"hda_disk_image": "chr-6.44.img"
}
},
{
"name": "6.43.8",
"images": {
"hda_disk_image": "chr-6.43.8.img"
}
},
{
"name": "6.42.12",
"images": {
"hda_disk_image": "chr-6.42.12.img"
}
},
{
"name": "6.42.9",
"images": {
"hda_disk_image": "chr-6.42.9.img"
}
},
{
"name": "6.42",
"images": {
"hda_disk_image": "chr-6.42.img"
"hda_disk_image": "chr-6.48.5.img"
}
}
]

View File

@ -23,6 +23,15 @@
"kvm": "allow"
},
"images": [
{
"filename": "openwrt-21.02.1-x86-64-generic-ext4-combined.img",
"version": "21.02.1",
"md5sum": "75896afa8f31bacc57fd84e7b5ff03b0",
"filesize": 126353408,
"download_url": "https://downloads.openwrt.org/releases/21.02.1/targets/x86/64/",
"direct_download_url": "https://downloads.openwrt.org/releases/21.02.1/targets/x86/64/openwrt-21.02.1-x86-64-generic-ext4-combined.img.gz",
"compression": "gzip"
},
{
"filename": "openwrt-21.02.0-x86-64-generic-ext4-combined.img",
"version": "21.02.0",
@ -187,6 +196,12 @@
}
],
"versions": [
{
"name": "21.02.1",
"images": {
"hda_disk_image": "openwrt-21.02.1-x86-64-generic-ext4-combined.img"
}
},
{
"name": "21.02.0",
"images": {

View File

@ -25,6 +25,13 @@
"kvm": "require"
},
"images": [
{
"filename": "OPNsense-21.7.1-OpenSSL-nano-amd64.img",
"version": "21.7.1",
"md5sum": "97f15ffec18202daec2485aa74593236",
"filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/21.7/"
},
{
"filename": "OPNsense-20.7-OpenSSL-nano-amd64.img",
"version": "20.7",
@ -41,6 +48,12 @@
}
],
"versions": [
{
"name": "21.7.1",
"images": {
"hda_disk_image": "OPNsense-21.7.1-OpenSSL-nano-amd64.img"
}
},
{
"name": "20.7",
"images": {

View File

@ -24,6 +24,13 @@
"process_priority": "normal"
},
"images": [
{
"filename": "pfSense-CE-2.5.2-RELEASE-amd64.iso",
"version": "2.5.2",
"md5sum": "8c85a55f6ea0c33d6eba3fb9926c016b",
"filesize": 651773952,
"download_url": "https://www.pfsense.org/download/mirror.php?section=downloads"
},
{
"filename": "pfSense-CE-2.4.5-RELEASE-p1-amd64.iso",
"version": "2.4.5-p1",
@ -55,6 +62,13 @@
}
],
"versions": [
{
"name": "2.5.2",
"images": {
"hda_disk_image": "empty100G.qcow2",
"cdrom_image": "pfSense-CE-2.5.2-RELEASE-amd64.iso"
}
},
{
"name": "2.4.5-p1",
"images": {

View File

@ -13,7 +13,7 @@
"availability": "service-contract",
"maintainer": "Neyder Achahuanco",
"maintainer_email": "neyder@neyder.net",
"usage": "You should download Red Hat Enterprise Linux KVM Guest Image from https://access.redhat.com/downloads/content/479/ver=/rhel---8/8.3/x86_64/product-software attach/customize cloud-init.iso and start.\nusername: cloud-user\npassword: redhat",
"usage": "You should download Red Hat Enterprise Linux KVM Guest Image from https://access.redhat.com/downloads/content/479/ver=/rhel---8/8.5/x86_64/product-software attach/customize cloud-init.iso and start.\nusername: cloud-user\npassword: redhat",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
@ -26,6 +26,20 @@
"options": "-nographic"
},
"images": [
{
"filename": "rhel-8.5-x86_64-kvm.qcow2",
"version": "8.5",
"md5sum": "1efb78dbb2033ba4ac6589a06c95c2d4",
"filesize": 779419648,
"download_url": "https://access.redhat.com/downloads/content/479/ver=/rhel---8/8.5/x86_64/product-software"
},
{
"filename": "rhel-8.4-x86_64-kvm.qcow2",
"version": "8.4",
"md5sum": "db4c3a72857b784dc6e96120351f2894",
"filesize": 727449600,
"download_url": "https://access.redhat.com/downloads/content/479/ver=/rhel---8/8.4/x86_64/product-software"
},
{
"filename": "rhel-8.3-x86_64-kvm.qcow2",
"version": "8.3",
@ -56,6 +70,20 @@
}
],
"versions": [
{
"name": "8.5",
"images": {
"hda_disk_image": "rhel-8.5-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso"
}
},
{
"name": "8.4",
"images": {
"hda_disk_image": "rhel-8.4-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso"
}
},
{
"name": "8.3",
"images": {

View File

@ -7,39 +7,47 @@
"vendor_url": "https://rockylinux.org",
"documentation_url": "https://docs.rockylinux.org",
"product_name": "Rocky Linux",
"registry_version": 4,
"status": "experimental",
"maintainer": "Bernhard Ehlers",
"maintainer_email": "none@b-ehlers.de",
"usage": "Username:\trockylinux\nPassword:\trockylinux\nTo become root, use \"sudo su\".\n\nTo improve performance, increase RAM and vCPUs in the VM settings.",
"registry_version": 3,
"status": "stable",
"maintainer": "Da-Geek",
"maintainer_email": "dageek@dageeks-geeks.gg",
"usage": "Username:\trocky\nPassword:\trocky\nTo become root, use \"sudo -i\".\n\nTo improve performance, increase RAM and vCPUs in the VM settings.",
"symbol": "linux_guest.svg",
"port_name_format": "ens{port4}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
"ram": 1536,
"hda_disk_interface": "sata",
"ram": 1024,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "vnc",
"console_type": "telnet",
"boot_priority": "c",
"kvm": "require",
"options": "-usbdevice tablet"
"options": "-nographic"
},
"images": [
{
"filename": "RockyLinux_8.4_VMG_LinuxVMImages.COM.vmdk",
"version": "8.4",
"md5sum": "3452d5b0fbb4cdcf3ac6fe8de8d0ac08",
"filesize": 5273878528,
"download_url": "https://www.linuxvmimages.com/images/rockylinux-8",
"direct_download_url": "https://sourceforge.net/projects/linuxvmimages/files/VMware/R/rockylinux/8/RockyLinux_8.4_VMM.7z/download",
"compression": "7z"
"filename": "Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2",
"version": "8.5",
"md5sum": "44982ddace75a1dba17942401086d72c",
"filesize": 1502701568,
"download_url": "https://download.rockylinux.org/pub/rocky/8/images/",
"direct_download_url": "https://download.rockylinux.org/pub/rocky/8/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2"
},
{
"filename": "rocky-cloud-init-data.iso",
"version": "1.0",
"md5sum": "33ffda3a81436e305f37fb913edd6d43",
"filesize": 374784,
"download_url": "https://github.com/GNS3/gns3-registry/raw/master/cloud-init/rocky-cloud/rocky-cloud-init-data.iso"
}
],
"versions": [
{
"name": "8.4",
"name": "8.5",
"images": {
"hda_disk_image": "RockyLinux_8.4_VMG_LinuxVMImages.COM.vmdk"
"hda_disk_image": "Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2",
"cdrom_image": "rocky-cloud-init-data.iso"
}
}
]

View File

@ -13,7 +13,7 @@
"symbol": "linux_guest.svg",
"docker": {
"adapters": 1,
"image": "gns3/ubuntu:xenial",
"image": "gns3/ubuntu:focal",
"console_type": "telnet"
}
}

View File

@ -27,12 +27,19 @@
},
"images": [
{
"filename": "vyos-1.3.0-epa1-amd64.iso",
"version": "1.3.0-epa1",
"md5sum": "a2aaa5bd3bc5827909d07a18a9de80a7",
"filename": "vyos-1.3.0-amd64.iso",
"version": "1.3.0",
"md5sum": "2019bd9c5efa6194e2761de678d0073f",
"filesize": 338690048,
"download_url": "https://support.vyos.io/en/downloads/files/vyos-1-3-0-generic-iso-image"
},
{
"filename": "vyos-1.3.0-epa3-amd64.iso",
"version": "1.3.0-epa3",
"md5sum": "1b5de684d8995844e35fa5cec3171811",
"filesize": 331350016,
"download_url": "https://vyos.net/get/snapshots/",
"direct_download_url": "https://s3.amazonaws.com/s3-us.vyos.io/snapshot/vyos-1.3.0-epa1/generic-iso/vyos-1.3.0-epa1-amd64.iso"
"direct_download_url": "https://s3.amazonaws.com/s3-us.vyos.io/snapshot/vyos-1.3.0-epa3/vyos-1.3.0-epa3-amd64.iso"
},
{
"filename": "vyos-1.2.8-amd64.iso",
@ -67,10 +74,17 @@
],
"versions": [
{
"name": "1.3.0-epa1",
"name": "1.3.0",
"images": {
"hda_disk_image": "empty8G.qcow2",
"cdrom_image": "vyos-1.3.0-epa1-amd64.iso"
"cdrom_image": "vyos-1.3.0-amd64.iso"
}
},
{
"name": "1.3.0-epa3",
"images": {
"hda_disk_image": "empty8G.qcow2",
"cdrom_image": "vyos-1.3.0-epa3-amd64.iso"
}
},
{

View File

@ -110,7 +110,7 @@ class Docker(BaseManager):
body = await response.read()
response.close()
if body and len(body):
if response.headers["CONTENT-TYPE"] == "application/json":
if response.headers.get('CONTENT-TYPE') == 'application/json':
body = json.loads(body.decode("utf-8"))
else:
body = body.decode("utf-8")

View File

@ -114,7 +114,7 @@ class DockerVM(BaseNode):
self._extra_volumes = extra_volumes or []
self._memory = memory
self._cpus = cpus
self._permissions_fixed = False
self._permissions_fixed = True
self._display = None
self._closing = False

View File

@ -21,6 +21,7 @@ import uuid
import socket
import shutil
import asyncio
import random
from ..config import Config
from .project import Project
@ -73,10 +74,6 @@ class Controller:
if host == "0.0.0.0":
host = "127.0.0.1"
name = socket.gethostname()
if name == "gns3vm":
name = "Main server"
self._load_controller_settings()
if server_config.enable_ssl:
@ -92,15 +89,16 @@ class Controller:
try:
self._local_server = await self.add_compute(
compute_id="local",
name=name,
name=f"{socket.gethostname()} (controller)",
protocol=protocol,
host=host,
console_host=console_host,
port=port,
user=server_config.user,
password=server_config.password,
user=server_config.compute_username,
password=server_config.compute_password,
force=True,
connect=True,
wait_connection=False,
ssl_context=self._ssl_context,
)
except ControllerError:
@ -112,7 +110,12 @@ class Controller:
if computes:
for c in computes:
try:
await self.add_compute(**c, connect=False)
#FIXME: Task exception was never retrieved
await self.add_compute(
compute_id=str(c.compute_id),
connect=False,
**c.dict(exclude_unset=True, exclude={"compute_id", "created_at", "updated_at"}),
)
except (ControllerError, KeyError):
pass # Skip not available servers at loading
@ -154,8 +157,8 @@ class Controller:
"""
if self._local_server:
self._local_server.user = Config.instance().settings.Server.user
self._local_server.password = Config.instance().settings.Server.password
self._local_server.user = Config.instance().settings.Server.compute_username
self._local_server.password = Config.instance().settings.Server.compute_password
async def stop(self):
@ -340,7 +343,7 @@ class Controller:
os.makedirs(configs_path, exist_ok=True)
return configs_path
async def add_compute(self, compute_id=None, name=None, force=False, connect=True, **kwargs):
async def add_compute(self, compute_id=None, name=None, force=False, connect=True, wait_connection=True, **kwargs):
"""
Add a server to the dictionary of computes controlled by this controller
@ -370,8 +373,11 @@ class Controller:
self._computes[compute.id] = compute
# self.save()
if connect:
# call compute.connect() later to give time to the controller to be fully started
asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(compute.connect()))
if wait_connection:
await compute.connect()
else:
# call compute.connect() later to give time to the controller to be fully started
asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(compute.connect()))
self.notification.controller_emit("compute.created", compute.asdict())
return compute
else:
@ -439,6 +445,16 @@ class Controller:
Returns a compute or raise a 404 error.
"""
if compute_id is None:
computes = list(self._computes.values())
if len(computes) == 1:
# return the only available compute
return computes[0]
else:
# randomly pick a compute until we have proper scalability handling
# https://github.com/GNS3/gns3-server/issues/1676
return random.choice(computes)
try:
return self._computes[compute_id]
except KeyError:
@ -508,6 +524,9 @@ class Controller:
:param load: Load the topology
"""
if not os.path.exists(path):
raise ControllerError(f"'{path}' does not exist on the controller")
topo_data = load_topology(path)
topo_data.pop("topology")
topo_data.pop("version")
@ -518,7 +537,10 @@ class Controller:
project = self._projects[topo_data["project_id"]]
else:
project = await self.add_project(
path=os.path.dirname(path), status="closed", filename=os.path.basename(path), **topo_data
path=os.path.dirname(path),
status="closed",
filename=os.path.basename(path),
**topo_data
)
if load or project.auto_open:
await project.open()

View File

@ -119,6 +119,7 @@ class Compute:
"""
Set authentication parameters
"""
if user is None or len(user.strip()) == 0:
self._user = None
self._password = None
@ -153,6 +154,7 @@ class Compute:
return self._interfaces_cache
async def update(self, **kwargs):
for kw in kwargs:
if kw not in ("user", "password"):
setattr(self, kw, kwargs[kw])
@ -372,7 +374,7 @@ class Compute:
pass
@locking
async def connect(self):
async def connect(self, report_failed_connection=False):
"""
Check if remote server is accessible
"""
@ -382,6 +384,8 @@ class Compute:
log.info(f"Connecting to compute '{self._id}'")
response = await self._run_http_query("GET", "/capabilities")
except ComputeError as e:
if report_failed_connection:
raise
log.warning(f"Cannot connect to compute '{self._id}': {e}")
# Try to reconnect after 5 seconds if server unavailable only if not during tests (otherwise we create a ressource usage bomb)
if not hasattr(sys, "_called_from_test") or not sys._called_from_test:
@ -468,6 +472,8 @@ class Compute:
# FIXME: slow down number of compute events
self._controller.notification.controller_emit("compute.updated", self.asdict())
else:
if action == "log.error":
log.error(event.pop("message"))
await self._controller.notification.dispatch(
action, event, project_id=project_id, compute_id=self.id
)
@ -488,7 +494,7 @@ class Compute:
# Try to reconnect after 1 second if server unavailable only if not during tests (otherwise we create a ressources usage bomb)
from gns3server.api.server import app
if not app.state.exiting and not hasattr(sys, "_called_from_test"):
log.info(f"Reconnecting to to compute '{self._id}' WebSocket '{ws_url}'")
log.info(f"Reconnecting to compute '{self._id}' WebSocket '{ws_url}'")
asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect()))
self._cpu_usage_percent = None
@ -569,7 +575,7 @@ class Compute:
msg = ""
if response.status == 401:
raise ControllerUnauthorizedError(f"Invalid authentication for compute {self.id}")
raise ControllerUnauthorizedError(f"Invalid authentication for compute '{self.name}' [{self.id}]")
elif response.status == 403:
raise ControllerForbiddenError(msg)
elif response.status == 404:

View File

@ -183,7 +183,7 @@ class UDPLink(Link):
self._capture_node = self._choose_capture_side()
data = {"capture_file_name": capture_file_name, "data_link_type": data_link_type}
await self._capture_node["node"].post(
"/adapters/{adapter_number}/ports/{port_number}/start_capture".format(
"/adapters/{adapter_number}/ports/{port_number}/capture/start".format(
adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]
),
data=data,
@ -196,7 +196,7 @@ class UDPLink(Link):
"""
if self._capture_node:
await self._capture_node["node"].post(
"/adapters/{adapter_number}/ports/{port_number}/stop_capture".format(
"/adapters/{adapter_number}/ports/{port_number}/capture/stop".format(
adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]
)
)

View File

@ -59,7 +59,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://959feb527c7441068b1bf80301b6e2c4:efa6d99da4c64faa8a7d929360765b40@o19455.ingest.sentry.io/38482"
DSN = "https://8eb8a1f4730949f9886df2c6fdc27755:795f8ac399d04d24a273fcb35f48b725@o19455.ingest.sentry.io/38482"
_instance = None
def __init__(self):

View File

@ -57,6 +57,12 @@ def create_default_roles(target, connection, **kw):
"path": "/",
"action": "ALLOW"
},
{
"description": "Allow to receive controller notifications",
"methods": ["GET"],
"path": "/notifications",
"action": "ALLOW"
},
{
"description": "Allow to create and list projects",
"methods": ["GET", "POST"],
@ -112,7 +118,7 @@ def add_permissions_to_role(target, connection, **kw):
role_id = result.first().role_id
# add minimum required paths to the "User" role
for path in ("/projects", "/templates", "/computes/*", "/symbols/*"):
for path in ("/notifications", "/projects", "/templates", "/computes/*", "/symbols/*"):
stmt = permissions_table.select().where(permissions_table.c.path == path)
result = connection.execute(stmt)
permission_id = result.first().permission_id

View File

@ -56,8 +56,8 @@ class User(BaseTable):
def create_default_super_admin(target, connection, **kw):
config = Config.instance().settings
default_admin_username = config.Server.default_admin_username
default_admin_password = config.Server.default_admin_password.get_secret_value()
default_admin_username = config.Controller.default_admin_username
default_admin_password = config.Controller.default_admin_password.get_secret_value()
hashed_password = auth_service.hash_password(default_admin_password)
stmt = target.insert().values(
username=default_admin_username,

View File

@ -52,18 +52,14 @@ class ComputesRepository(BaseRepository):
async def create_compute(self, compute_create: schemas.ComputeCreate) -> models.Compute:
password = compute_create.password
if password:
password = password.get_secret_value()
db_compute = models.Compute(
compute_id=compute_create.compute_id,
name=compute_create.name,
protocol=compute_create.protocol.value,
protocol=compute_create.protocol,
host=compute_create.host,
port=compute_create.port,
user=compute_create.user,
password=password,
password=compute_create.password.get_secret_value(),
)
self._db_session.add(db_compute)
await self._db_session.commit()

View File

@ -125,7 +125,7 @@ class UsersRepository(BaseRepository):
async def authenticate_user(self, username: str, password: str) -> Optional[models.User]:
"""
Authenticate an user.
Authenticate user.
"""
user = await self.get_user_by_username(username)

View File

@ -68,9 +68,7 @@ async def get_computes(app: FastAPI) -> List[dict]:
db_computes = await ComputesRepository(db_session).get_computes()
for db_compute in db_computes:
try:
compute = jsonable_encoder(
schemas.Compute.from_orm(db_compute), exclude_unset=True, exclude={"created_at", "updated_at"}
)
compute = schemas.Compute.from_orm(db_compute)
except ValidationError as e:
log.error(f"Could not load compute '{db_compute.compute_id}' from database: {e}")
continue

View File

@ -37,24 +37,31 @@ from .controller.iou_license import IOULicense
from .controller.capabilities import Capabilities
# Controller template schemas
from .controller.templates.vpcs_templates import VPCSTemplate
from .controller.templates.cloud_templates import CloudTemplate
from .controller.templates.iou_templates import IOUTemplate
from .controller.templates.docker_templates import DockerTemplate
from .controller.templates.ethernet_hub_templates import EthernetHubTemplate
from .controller.templates.ethernet_switch_templates import EthernetSwitchTemplate
from .controller.templates.virtualbox_templates import VirtualBoxTemplate
from .controller.templates.vmware_templates import VMwareTemplate
from .controller.templates.qemu_templates import QemuTemplate
from .controller.templates.vpcs_templates import VPCSTemplate, VPCSTemplateUpdate
from .controller.templates.cloud_templates import CloudTemplate, CloudTemplateUpdate
from .controller.templates.iou_templates import IOUTemplate, IOUTemplateUpdate
from .controller.templates.docker_templates import DockerTemplate, DockerTemplateUpdate
from .controller.templates.ethernet_hub_templates import EthernetHubTemplate, EthernetHubTemplateUpdate
from .controller.templates.ethernet_switch_templates import EthernetSwitchTemplate, EthernetSwitchTemplateUpdate
from .controller.templates.virtualbox_templates import VirtualBoxTemplate, VirtualBoxTemplateUpdate
from .controller.templates.vmware_templates import VMwareTemplate, VMwareTemplateUpdate
from .controller.templates.qemu_templates import QemuTemplate, QemuTemplateUpdate
from .controller.templates.dynamips_templates import (
DynamipsTemplate,
C1700DynamipsTemplate,
C1700DynamipsTemplateUpdate,
C2600DynamipsTemplate,
C2600DynamipsTemplateUpdate,
C2691DynamipsTemplate,
C2691DynamipsTemplateUpdate,
C3600DynamipsTemplate,
C3600DynamipsTemplateUpdate,
C3725DynamipsTemplate,
C3725DynamipsTemplateUpdate,
C3745DynamipsTemplate,
C3745DynamipsTemplateUpdate,
C7200DynamipsTemplate,
C7200DynamipsTemplateUpdate
)
# Compute schemas

View File

@ -24,6 +24,8 @@ class ControllerSettings(BaseModel):
jwt_secret_key: str = None
jwt_algorithm: str = "HS256"
jwt_access_token_expire_minutes: int = 1440 # 24 hours
default_admin_username: str = "admin"
default_admin_password: SecretStr = SecretStr("admin")
class Config:
validate_assignment = True
@ -131,11 +133,8 @@ class ServerSettings(BaseModel):
udp_start_port_range: int = Field(10000, gt=0, le=65535)
udp_end_port_range: int = Field(30000, gt=0, le=65535)
ubridge_path: str = "ubridge"
user: str = None
password: SecretStr = None
enable_http_auth: bool = False
default_admin_username: str = "admin"
default_admin_password: SecretStr = SecretStr("admin")
compute_username: str = "admin"
compute_password: SecretStr = SecretStr("")
allowed_interfaces: List[str] = Field(default_factory=list)
default_nat_interface: str = None
allow_remote_console: bool = False
@ -164,14 +163,6 @@ class ServerSettings(BaseModel):
raise ValueError("vnc_console_end_port_range must be > vnc_console_start_port_range")
return v
@validator("enable_http_auth")
def validate_enable_auth(cls, v, values):
if v is True:
if "user" not in values or not values["user"]:
raise ValueError("HTTP authentication is enabled but user is not configured")
return v
@validator("enable_ssl")
def validate_enable_ssl(cls, v, values):

View File

@ -41,9 +41,13 @@ class ComputeBase(BaseModel):
protocol: Protocol
host: str
port: int = Field(..., gt=0, le=65535)
user: Optional[str] = None
user: str = None
password: Optional[SecretStr] = None
name: Optional[str] = None
class Config:
use_enum_values = True
class ComputeCreate(ComputeBase):
"""
@ -51,7 +55,6 @@ class ComputeCreate(ComputeBase):
"""
compute_id: Union[str, uuid.UUID] = None
password: Optional[SecretStr] = None
class Config:
schema_extra = {
@ -102,6 +105,7 @@ class ComputeUpdate(ComputeBase):
protocol: Optional[Protocol] = None
host: Optional[str] = None
port: Optional[int] = Field(None, gt=0, le=65535)
user: Optional[str] = None
password: Optional[SecretStr] = None
class Config:

View File

@ -50,9 +50,6 @@ class TemplateBase(BaseModel):
compute_id: Optional[str] = None
usage: Optional[str] = ""
class Config:
extra = "allow"
class TemplateCreate(TemplateBase):
"""
@ -61,12 +58,15 @@ class TemplateCreate(TemplateBase):
name: str
template_type: NodeType
compute_id: str
class Config:
extra = "allow"
class TemplateUpdate(TemplateBase):
pass
class Config:
extra = "allow"
class Template(DateTimeModelMixin, TemplateBase):
@ -77,9 +77,9 @@ class Template(DateTimeModelMixin, TemplateBase):
symbol: str
builtin: bool
template_type: NodeType
compute_id: Union[str, None]
class Config:
extra = "allow"
orm_mode = True

View File

@ -37,3 +37,8 @@ class CloudTemplate(TemplateBase):
remote_console_port: Optional[int] = Field(23, gt=0, le=65535, description="Remote console TCP port")
remote_console_type: Optional[CloudConsoleType] = Field("none", description="Remote console type")
remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface")
class CloudTemplateUpdate(CloudTemplate):
pass

View File

@ -51,3 +51,8 @@ class DockerTemplate(TemplateBase):
memory: Optional[int] = Field(0, description="Maximum amount of memory the container can use in MB")
cpus: Optional[int] = Field(0, description="Maximum amount of CPU resources the container can use")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class DockerTemplateUpdate(DockerTemplate):
image: Optional[str] = Field(None, description="Docker image name")

View File

@ -77,6 +77,12 @@ class C7200DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C7200DynamipsTemplateUpdate(C7200DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C3725DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(128, description="Amount of RAM in MB")
@ -85,6 +91,12 @@ class C3725DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3725DynamipsTemplateUpdate(C3725DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C3745DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(256, description="Amount of RAM in MB")
@ -93,6 +105,12 @@ class C3745DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3745DynamipsTemplateUpdate(C3745DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C3600ChassisType(str, Enum):
chassis_3620 = "3620"
@ -109,6 +127,12 @@ class C3600DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3600DynamipsTemplateUpdate(C3600DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C2691DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(192, description="Amount of RAM in MB")
@ -117,6 +141,12 @@ class C2691DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C2691DynamipsTemplateUpdate(C2691DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C2600ChassisType(str, Enum):
chassis_2610 = "2610"
@ -139,6 +169,12 @@ class C2600DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C2600DynamipsTemplateUpdate(C2600DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C1700ChassisType(str, Enum):
chassis_1720 = "1720"
@ -155,3 +191,9 @@ class C1700DynamipsTemplate(DynamipsTemplate):
nvram: Optional[int] = Field(128, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(15, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(False, description="Sparse memory feature")
class C1700DynamipsTemplateUpdate(C1700DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")

View File

@ -39,3 +39,8 @@ class EthernetHubTemplate(TemplateBase):
default_name_format: Optional[str] = "Hub{0}"
symbol: Optional[str] = ":/symbols/hub.svg"
ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports")
class EthernetHubTemplateUpdate(EthernetHubTemplate):
pass

View File

@ -50,3 +50,8 @@ class EthernetSwitchTemplate(TemplateBase):
symbol: Optional[str] = ":/symbols/ethernet_switch.svg"
ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports")
console_type: Optional[ConsoleType] = Field("none", description="Console type")
class EthernetSwitchTemplateUpdate(EthernetSwitchTemplate):
pass

View File

@ -40,3 +40,8 @@ class IOUTemplate(TemplateBase):
console_auto_start: Optional[bool] = Field(
False, description="Automatically start the console when the node has started"
)
class IOUTemplateUpdate(IOUTemplate):
path: Optional[str] = Field(None, description="Path of IOU executable")

View File

@ -37,7 +37,7 @@ class QemuTemplate(TemplateBase):
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/qemu_guest.svg"
qemu_path: Optional[str] = Field("", description="Qemu executable path")
platform: Optional[QemuPlatform] = Field("i386", description="Platform to emulate")
platform: Optional[QemuPlatform] = Field("x86_64", description="Platform to emulate")
linked_clone: Optional[bool] = Field(True, description="Whether the VM is a linked clone or not")
ram: Optional[int] = Field(256, description="Amount of RAM in MB")
cpus: Optional[int] = Field(1, ge=1, le=255, description="Number of vCPUs")
@ -85,3 +85,8 @@ class QemuTemplate(TemplateBase):
process_priority: Optional[QemuProcessPriority] = Field("normal", description="Process priority for QEMU")
options: Optional[str] = Field("", description="Additional QEMU options")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class QemuTemplateUpdate(QemuTemplate):
pass

View File

@ -58,3 +58,8 @@ class VirtualBoxTemplate(TemplateBase):
False, description="Automatically start the console when the node has started"
)
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class VirtualBoxTemplateUpdate(VirtualBoxTemplate):
vmname: Optional[str] = Field(None, description="VirtualBox VM name (in VirtualBox itself)")

View File

@ -54,3 +54,8 @@ class VMwareTemplate(TemplateBase):
False, description="Automatically start the console when the node has started"
)
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class VMwareTemplateUpdate(VMwareTemplate):
vmx_path: Optional[str] = Field(None, description="Path to the vmx file")

View File

@ -32,3 +32,8 @@ class VPCSTemplate(TemplateBase):
console_auto_start: Optional[bool] = Field(
False, description="Automatically start the console when the node has started"
)
class VPCSTemplateUpdate(VPCSTemplate):
pass

View File

@ -20,5 +20,6 @@ from typing import Optional
class Version(BaseModel):
controller_host: Optional[str] = Field(None, description="Controller hostname or IP address")
version: str = Field(..., description="Version number")
local: Optional[bool] = Field(None, description="Whether this is a local server or not")

View File

@ -30,6 +30,8 @@ import asyncio
import signal
import functools
import uvicorn
import secrets
import string
from gns3server.controller import Controller
from gns3server.compute.port_manager import PortManager
@ -38,7 +40,7 @@ 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
from pydantic import ValidationError, SecretStr
import logging
@ -199,9 +201,9 @@ class Server:
loop.add_signal_handler(getattr(signal, signal_name), callback)
@staticmethod
def _kill_ghosts(self):
def _kill_ghosts():
"""
Kill process from previous GNS3 session
Kill processes from previous GNS3 session
"""
detect_process = ["vpcs", "ubridge", "dynamips"]
for proc in psutil.process_iter():
@ -214,7 +216,7 @@ class Server:
pass
@staticmethod
def _pid_lock(self, path):
def _pid_lock(path):
"""
Write the file in a file on the system.
Check if the process is not already running.
@ -269,8 +271,15 @@ class Server:
if config.Server.local:
log.warning("Local mode is enabled. Beware, clients will have full control on your filesystem")
if config.Server.enable_http_auth:
log.info(f"HTTP authentication is enabled with username '{config.Server.user}'")
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))
config.Server.compute_password = SecretStr(generated_password)
log.warning(f"Compute authentication is enabled with username '{config.Server.compute_username}' and "
f"a randomly generated password. Please set a password in the config file if this compute "
f"is to be used by an external controller")
else:
log.info(f"Compute authentication is enabled with username '{config.Server.compute_username}'")
# we only support Python 3 version >= 3.6
if sys.version_info < (3, 6, 0):

View File

@ -35,6 +35,7 @@ DEFAULT_JWT_SECRET_KEY = "efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76
class AuthService:
def hash_password(self, password: str) -> str:
return pwd_context.hash(password)

View File

@ -41,17 +41,17 @@ class ComputesService:
db_computes = await self._computes_repo.get_computes()
return db_computes
async def create_compute(self, compute_create: schemas.ComputeCreate) -> models.Compute:
async def create_compute(self, compute_create: schemas.ComputeCreate, connect: bool = False) -> models.Compute:
if await self._computes_repo.get_compute(compute_create.compute_id):
raise ControllerBadRequestError(f"Compute '{compute_create.compute_id}' is already registered")
db_compute = await self._computes_repo.create_compute(compute_create)
await self._controller.add_compute(
compute = await self._controller.add_compute(
compute_id=str(db_compute.compute_id),
connect=False,
connect=connect,
**compute_create.dict(exclude_unset=True, exclude={"compute_id"}),
)
self._controller.notification.controller_emit("compute.created", db_compute.asjson())
self._controller.notification.controller_emit("compute.created", compute.asdict())
return db_compute
async def get_compute(self, compute_id: Union[str, UUID]) -> models.Compute:
@ -70,7 +70,7 @@ class ComputesService:
db_compute = await self._computes_repo.update_compute(compute_id, compute_update)
if not db_compute:
raise ControllerNotFoundError(f"Compute '{compute_id}' not found")
self._controller.notification.controller_emit("compute.updated", db_compute.asjson())
self._controller.notification.controller_emit("compute.updated", compute.asdict())
return db_compute
async def delete_compute(self, compute_id: Union[str, UUID]) -> None:

View File

@ -32,7 +32,7 @@ from gns3server.controller.controller_error import (
ControllerForbiddenError,
)
TEMPLATE_TYPE_TO_SHEMA = {
TEMPLATE_TYPE_TO_SCHEMA = {
"cloud": schemas.CloudTemplate,
"ethernet_hub": schemas.EthernetHubTemplate,
"ethernet_switch": schemas.EthernetSwitchTemplate,
@ -45,7 +45,19 @@ TEMPLATE_TYPE_TO_SHEMA = {
"qemu": schemas.QemuTemplate,
}
DYNAMIPS_PLATFORM_TO_SHEMA = {
TEMPLATE_TYPE_TO_UPDATE_SCHEMA = {
"cloud": schemas.CloudTemplateUpdate,
"ethernet_hub": schemas.EthernetHubTemplateUpdate,
"ethernet_switch": schemas.EthernetSwitchTemplateUpdate,
"docker": schemas.DockerTemplateUpdate,
"vpcs": schemas.VPCSTemplateUpdate,
"virtualbox": schemas.VirtualBoxTemplateUpdate,
"vmware": schemas.VMwareTemplateUpdate,
"iou": schemas.IOUTemplateUpdate,
"qemu": schemas.QemuTemplateUpdate,
}
DYNAMIPS_PLATFORM_TO_SCHEMA = {
"c7200": schemas.C7200DynamipsTemplate,
"c3745": schemas.C3745DynamipsTemplate,
"c3725": schemas.C3725DynamipsTemplate,
@ -55,6 +67,16 @@ DYNAMIPS_PLATFORM_TO_SHEMA = {
"c1700": schemas.C1700DynamipsTemplate,
}
DYNAMIPS_PLATFORM_TO_UPDATE_SCHEMA = {
"c7200": schemas.C7200DynamipsTemplateUpdate,
"c3745": schemas.C3745DynamipsTemplateUpdate,
"c3725": schemas.C3725DynamipsTemplateUpdate,
"c3600": schemas.C3600DynamipsTemplateUpdate,
"c2691": schemas.C2691DynamipsTemplateUpdate,
"c2600": schemas.C2600DynamipsTemplateUpdate,
"c1700": schemas.C1700DynamipsTemplateUpdate,
}
# built-in templates have their compute_id set to None to tell clients to select a compute
BUILTIN_TEMPLATES = [
{
@ -159,8 +181,10 @@ class TemplatesService:
async def _find_image(self, image_path: str):
image = await self._templates_repo.get_image(image_path)
if not image or not os.path.exists(image.path):
raise ControllerNotFoundError(f"Image '{image_path}' could not be found")
if not image:
raise ControllerNotFoundError(f"Image '{image_path}' could not be found in the controller database")
if not os.path.exists(image.path):
raise ControllerNotFoundError(f"Image '{image.path}' could not be found on disk")
return image
async def _find_images(self, template_type: str, settings: dict) -> List[models.Image]:
@ -205,20 +229,18 @@ class TemplatesService:
try:
# get the default template settings
template_settings = jsonable_encoder(template_create, exclude_unset=True)
template_schema = TEMPLATE_TYPE_TO_SHEMA[template_create.template_type]
template_settings_with_defaults = template_schema.parse_obj(template_settings)
settings = template_settings_with_defaults.dict()
create_settings = jsonable_encoder(template_create, exclude_unset=True)
template_schema = TEMPLATE_TYPE_TO_SCHEMA[template_create.template_type]
template_settings = template_schema.parse_obj(create_settings).dict()
if template_create.template_type == "dynamips":
# special case for Dynamips to cover all platform types that contain specific settings
dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SHEMA[settings["platform"]]
dynamips_template_settings_with_defaults = dynamips_template_schema.parse_obj(template_settings)
settings = dynamips_template_settings_with_defaults.dict()
dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SCHEMA[template_settings["platform"]]
template_settings = dynamips_template_schema.parse_obj(create_settings).dict()
except pydantic.ValidationError as e:
raise ControllerBadRequestError(f"JSON schema error received while creating new template: {e}")
images_to_add_to_template = await self._find_images(template_create.template_type, settings)
db_template = await self._templates_repo.create_template(template_create.template_type, settings)
images_to_add_to_template = await self._find_images(template_create.template_type, template_settings)
db_template = await self._templates_repo.create_template(template_create.template_type, template_settings)
for image in images_to_add_to_template:
await self._templates_repo.add_image_to_template(db_template.template_id, image)
template = db_template.asjson()
@ -245,12 +267,22 @@ class TemplatesService:
if self.get_builtin_template(template_id):
raise ControllerForbiddenError(f"Template '{template_id}' cannot be updated because it is built-in")
template_settings = jsonable_encoder(template_update, exclude_unset=True)
db_template = await self._templates_repo.get_template(template_id)
if not db_template:
raise ControllerNotFoundError(f"Template '{template_id}' not found")
try:
# validate the update settings
update_settings = jsonable_encoder(template_update, exclude_unset=True)
if db_template.template_type == "dynamips":
template_schema = DYNAMIPS_PLATFORM_TO_UPDATE_SCHEMA[db_template.platform]
else:
template_schema = TEMPLATE_TYPE_TO_UPDATE_SCHEMA[db_template.template_type]
template_settings = template_schema.parse_obj(update_settings).dict(exclude_unset=True)
except pydantic.ValidationError as e:
raise ControllerBadRequestError(f"JSON schema error received while updating template: {e}")
images_to_add_to_template = await self._find_images(db_template.template_type, template_settings)
if db_template.template_type == "dynamips" and "image" in template_settings:
await self._remove_image(db_template.template_id, db_template.image)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
!function(){"use strict";var e,v={},g={};function n(e){var u=g[e];if(void 0!==u)return u.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e](t,t.exports,n),t.loaded=!0,t.exports}n.m=v,e=[],n.O=function(u,t,a,o){if(!t){var r=1/0;for(i=0;i<e.length;i++){t=e[i][0],a=e[i][1],o=e[i][2];for(var l=!0,f=0;f<t.length;f++)(!1&o||r>=o)&&Object.keys(n.O).every(function(b){return n.O[b](t[f])})?t.splice(f--,1):(l=!1,o<r&&(r=o));if(l){e.splice(i--,1);var s=a();void 0!==s&&(u=s)}}return u}o=o||0;for(var i=e.length;i>0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,a,o]},n.n=function(e){var u=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(u,{a:u}),u},n.d=function(e,u){for(var t in u)n.o(u,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:u[t]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce(function(u,t){return n.f[t](e,u),u},[]))},n.u=function(e){return e+".eace20bb0639f4909f27.js"},n.miniCssF=function(e){return"styles.bf28628fcb2844ad74bd.css"},n.hmd=function(e){return(e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:function(){throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e},n.o=function(e,u){return Object.prototype.hasOwnProperty.call(e,u)},function(){var e={},u="gns3-web-ui:";n.l=function(t,a,o,i){if(e[t])e[t].push(a);else{var r,l;if(void 0!==o)for(var f=document.getElementsByTagName("script"),s=0;s<f.length;s++){var c=f[s];if(c.getAttribute("src")==t||c.getAttribute("data-webpack")==u+o){r=c;break}}r||(l=!0,(r=document.createElement("script")).charset="utf-8",r.timeout=120,n.nc&&r.setAttribute("nonce",n.nc),r.setAttribute("data-webpack",u+o),r.src=n.tu(t)),e[t]=[a];var d=function(h,b){r.onerror=r.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],r.parentNode&&r.parentNode.removeChild(r),_&&_.forEach(function(m){return m(b)}),h)return h(b)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=d.bind(null,r.onerror),r.onload=d.bind(null,r.onload),l&&document.head.appendChild(r)}}}(),n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},function(){var e;n.tu=function(u){return void 0===e&&(e={createScriptURL:function(t){return t}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(u)}}(),n.p="",function(){var e={666:0};n.f.j=function(a,o){var i=n.o(e,a)?e[a]:void 0;if(0!==i)if(i)o.push(i[2]);else if(666!=a){var r=new Promise(function(c,d){i=e[a]=[c,d]});o.push(i[2]=r);var l=n.p+n.u(a),f=new Error;n.l(l,function(c){if(n.o(e,a)&&(0!==(i=e[a])&&(e[a]=void 0),i)){var d=c&&("load"===c.type?"missing":c.type),p=c&&c.target&&c.target.src;f.message="Loading chunk "+a+" failed.\n("+d+": "+p+")",f.name="ChunkLoadError",f.type=d,f.request=p,i[1](f)}},"chunk-"+a,a)}else e[a]=0},n.O.j=function(a){return 0===e[a]};var u=function(a,o){var f,s,i=o[0],r=o[1],l=o[2],c=0;for(f in r)n.o(r,f)&&(n.m[f]=r[f]);if(l)var d=l(n);for(a&&a(o);c<i.length;c++)n.o(e,s=i[c])&&e[s]&&e[s][0](),e[i[c]]=0;return n.O(d)},t=self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[];t.forEach(u.bind(null,0)),t.push=u.bind(null,t.push.bind(t))}()}();

View File

@ -0,0 +1 @@
!function(){"use strict";var e,v={},g={};function n(e){var a=g[e];if(void 0!==a)return a.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e](t,t.exports,n),t.loaded=!0,t.exports}n.m=v,e=[],n.O=function(a,t,u,o){if(!t){var r=1/0;for(i=0;i<e.length;i++){t=e[i][0],u=e[i][1],o=e[i][2];for(var l=!0,f=0;f<t.length;f++)(!1&o||r>=o)&&Object.keys(n.O).every(function(b){return n.O[b](t[f])})?t.splice(f--,1):(l=!1,o<r&&(r=o));if(l){e.splice(i--,1);var s=u();void 0!==s&&(a=s)}}return a}o=o||0;for(var i=e.length;i>0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,u,o]},n.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(a,{a:a}),a},n.d=function(e,a){for(var t in a)n.o(a,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce(function(a,t){return n.f[t](e,a),a},[]))},n.u=function(e){return e+".288b4de0ead3b7b9276b.js"},n.miniCssF=function(e){return"styles.f8555f2eecf8cf87f666.css"},n.hmd=function(e){return(e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:function(){throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e},n.o=function(e,a){return Object.prototype.hasOwnProperty.call(e,a)},function(){var e={},a="gns3-web-ui:";n.l=function(t,u,o,i){if(e[t])e[t].push(u);else{var r,l;if(void 0!==o)for(var f=document.getElementsByTagName("script"),s=0;s<f.length;s++){var c=f[s];if(c.getAttribute("src")==t||c.getAttribute("data-webpack")==a+o){r=c;break}}r||(l=!0,(r=document.createElement("script")).charset="utf-8",r.timeout=120,n.nc&&r.setAttribute("nonce",n.nc),r.setAttribute("data-webpack",a+o),r.src=n.tu(t)),e[t]=[u];var d=function(h,b){r.onerror=r.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],r.parentNode&&r.parentNode.removeChild(r),_&&_.forEach(function(m){return m(b)}),h)return h(b)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=d.bind(null,r.onerror),r.onload=d.bind(null,r.onload),l&&document.head.appendChild(r)}}}(),n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},function(){var e;n.tu=function(a){return void 0===e&&(e={createScriptURL:function(t){return t}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(a)}}(),n.p="",function(){var e={666:0};n.f.j=function(u,o){var i=n.o(e,u)?e[u]:void 0;if(0!==i)if(i)o.push(i[2]);else if(666!=u){var r=new Promise(function(c,d){i=e[u]=[c,d]});o.push(i[2]=r);var l=n.p+n.u(u),f=new Error;n.l(l,function(c){if(n.o(e,u)&&(0!==(i=e[u])&&(e[u]=void 0),i)){var d=c&&("load"===c.type?"missing":c.type),p=c&&c.target&&c.target.src;f.message="Loading chunk "+u+" failed.\n("+d+": "+p+")",f.name="ChunkLoadError",f.type=d,f.request=p,i[1](f)}},"chunk-"+u,u)}else e[u]=0},n.O.j=function(u){return 0===e[u]};var a=function(u,o){var f,s,i=o[0],r=o[1],l=o[2],c=0;for(f in r)n.o(r,f)&&(n.m[f]=r[f]);if(l)var d=l(n);for(u&&u(o);c<i.length;c++)n.o(e,s=i[c])&&e[s]&&e[s][0](),e[i[c]]=0;return n.O(d)},t=self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[];t.forEach(a.bind(null,0)),t.push=a.bind(null,t.push.bind(t))}()}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -52,7 +52,7 @@ class HTTPClient:
if not password:
password = ""
try:
basic_auth = aiohttp.BasicAuth(user, password, "utf-8")
basic_auth = aiohttp.BasicAuth(user, password.get_secret_value(), "utf-8")
except ValueError as e:
log.error(f"Basic authentication set-up error: {e}")

View File

@ -4,17 +4,9 @@ Wants=network-online.target
After=network.target network-online.target
[Service]
Type=forking
User=gns3
Group=gns3
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3
ExecStart=/usr/local/bin/gns3server --log /var/log/gns3/gns3.log \
--pid /var/run/gns3/gns3.pid --daemon
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-abort
PIDFile=/var/run/gns3/gns3.pid
ExecStart=/usr/bin/gns3server
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target

View File

@ -1,17 +1,19 @@
uvicorn==0.15.0
fastapi==0.70.0
websockets==10.0 ; python_version >= "3.7"
websockets==9.1 ; python_version < "3.7"
uvicorn==0.17.0
fastapi==0.72.0
python-multipart==0.0.5
aiohttp==3.7.4.post0
aiofiles==0.7.0
Jinja2==3.0.2
sentry-sdk==1.4.3
psutil==5.8.0
async-timeout==3.0.1
websockets==10.1 ; python_version >= "3.7"
websockets==9.1 ; python_version < "3.7"
aiohttp==3.8.1 ; python_version >= "3.7"
aiohttp==3.7.4.post0 ; python_version < "3.7"
async-timeout==4.0.2 ; python_version >= "3.7"
async-timeout==3.0.1 ; python_version < "3.7"
aiofiles==0.8.0
Jinja2==3.0.3
sentry-sdk==1.5.2
psutil==5.9.0
distro==1.6.0
py-cpuinfo==8.0.0
sqlalchemy==1.4.26
sqlalchemy==1.4.29
aiosqlite===0.17.0
passlib[bcrypt]==1.7.4
python-jose==3.3.0

3
scripts/gns3_server.conf Normal file
View File

@ -0,0 +1,3 @@
[Server]
compute_username = gns3
compute_password = gns3

View File

@ -30,9 +30,9 @@ pytestmark = pytest.mark.asyncio
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
async def test_get(app: FastAPI, client: AsyncClient, windows_platform) -> None:
async def test_get(app: FastAPI, compute_client: AsyncClient, windows_platform) -> None:
response = await client.get(app.url_path_for("get_capabilities"))
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__,
@ -44,9 +44,9 @@ async def test_get(app: FastAPI, client: AsyncClient, windows_platform) -> None:
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
async def test_get_on_gns3vm(app: FastAPI, client: AsyncClient, on_gns3vm) -> None:
async def test_get_on_gns3vm(app: FastAPI, compute_client: AsyncClient, on_gns3vm) -> None:
response = await client.get(app.url_path_for("get_capabilities"))
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__,

View File

@ -28,115 +28,115 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function")
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, on_gns3vm) -> dict:
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> dict:
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await client.post(app.url_path_for("create_cloud", project_id=compute_project.id),
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, client: AsyncClient, compute_project: Project) -> None:
async def test_cloud_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await client.post(app.url_path_for("create_cloud", project_id=compute_project.id),
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_get_cloud(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.get(app.url_path_for("get_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
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("create_cloud_nio",
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 client.post(url, json=params)
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
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("create_cloud_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
params["filters"] = {}
url = app.url_path_for("create_cloud_nio",
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 client.put(url, json=params)
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
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("create_cloud_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
url = app.url_path_for("delete_cloud_nio",
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 client.delete(url)
response = await compute_client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_cloud_delete(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_cloud_delete(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.delete(app.url_path_for("delete_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
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, client: AsyncClient, vm: dict) -> None:
async def test_cloud_update(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
response = await client.put(app.url_path_for("update_cloud", project_id=vm["project_id"], node_id=vm["node_id"]),
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, client: AsyncClient, vm: dict) -> None:
async def test_cloud_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"capture_file_name": "test.pcap",
@ -144,7 +144,7 @@ async def test_cloud_start_capture(app: FastAPI, client: AsyncClient, vm: dict)
}
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock:
response = await client.post(app.url_path_for("start_cloud_capture",
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",
@ -155,10 +155,10 @@ async def test_cloud_start_capture(app: FastAPI, client: AsyncClient, vm: dict)
assert "test.pcap" in response.json()["pcap_file_path"]
async def test_cloud_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_cloud_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock:
response = await client.post(app.url_path_for("stop_cloud_capture",
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",

View File

@ -27,27 +27,33 @@ from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
async def test_udp_allocation(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_udp_allocation(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
response = await client.post(app.url_path_for("allocate_udp_port", project_id=compute_project.id), json={})
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, client: AsyncClient) -> None:
async def test_interfaces(app: FastAPI, compute_client: AsyncClient) -> None:
response = await client.get(app.url_path_for("network_interfaces"))
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, client: AsyncClient) -> None:
async def test_version_output(app: FastAPI, compute_client: AsyncClient) -> None:
response = await client.get(app.url_path_for("compute_version"))
response = await compute_client.get(app.url_path_for("compute:compute_version"))
assert response.status_code == status.HTTP_200_OK
assert response.json() == {'local': True, 'version': __version__}
async def test_compute_authentication(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
# @pytest.mark.asyncio
# async def test_debug_output(compute_api):
#
@ -55,7 +61,7 @@ async def test_version_output(app: FastAPI, client: AsyncClient) -> None:
# assert response.status_code == 200
async def test_statistics_output(app: FastAPI, client: AsyncClient) -> None:
async def test_statistics_output(app: FastAPI, compute_client: AsyncClient) -> None:
response = await client.get(app.url_path_for("compute_statistics"))
response = await compute_client.get(app.url_path_for("compute:compute_statistics"))
assert response.status_code == status.HTTP_200_OK

View File

@ -57,22 +57,22 @@ def base_params() -> dict:
@pytest.fixture
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
async def vm(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 client.post(app.url_path_for("create_docker_node", project_id=compute_project.id),
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, client: AsyncClient, compute_project: Project, base_params: dict) -> None:
async def test_docker_create(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 client.post(app.url_path_for("create_docker_node", project_id=compute_project.id),
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"] == "PC TEST 1"
@ -85,68 +85,68 @@ async def test_docker_create(app: FastAPI, client: AsyncClient, compute_project:
assert response.json()["extra_hosts"] == "test:127.0.0.1"
async def test_docker_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock:
response = await client.post(app.url_path_for("start_docker_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("stop_docker_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("reload_docker_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.delete(app.url_path_for("delete_docker_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("pause_docker_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("unpause_docker_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_docker_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -154,17 +154,17 @@ async def test_docker_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict
"rport": 4343,
"rhost": "127.0.0.1"}
url = app.url_path_for("create_docker_node_nio",
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 client.post(url, json=params)
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, client: AsyncClient, vm: dict) -> None:
async def test_docker_update_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -173,37 +173,37 @@ async def test_docker_update_nio(app: FastAPI, client: AsyncClient, vm: dict) ->
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_docker_node_nio",
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 client.post(url, json=params)
response = await compute_client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
url = app.url_path_for("update_docker_node_nio",
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 client.put(url, json=params)
response = await compute_client.put(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
async def test_docker_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("delete_docker_node_nio",
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 client.delete(url)
response = await compute_client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_docker_update(app: FastAPI, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_docker_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
params = {
"name": "test",
@ -214,7 +214,7 @@ async def test_docker_update(app: FastAPI, client: AsyncClient, vm: dict, free_c
}
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock:
response = await client.put(app.url_path_for("update_docker_node",
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)
@ -227,9 +227,9 @@ async def test_docker_update(app: FastAPI, client: AsyncClient, vm: dict, free_c
assert response.json()["extra_hosts"] == "test:127.0.0.1"
async def test_docker_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("start_docker_node_capture",
url = app.url_path_for("compute:start_docker_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -238,15 +238,15 @@ async def test_docker_start_capture(app: FastAPI, client: AsyncClient, vm: dict)
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 client.post(url, json=params)
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(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("stop_docker_node_capture",
url = app.url_path_for("compute:stop_docker_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -254,22 +254,22 @@ async def test_docker_stop_capture(app: FastAPI, client: AsyncClient, vm: dict)
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 client.post(url)
response = await compute_client.post(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called
async def test_docker_duplicate(app: FastAPI, client: AsyncClient, vm: dict, base_params: dict) -> None:
async def test_docker_duplicate(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 client.post(app.url_path_for("create_docker_node",
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 client.post(app.url_path_for("duplicate_docker_node",
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

View File

@ -162,10 +162,10 @@ def fake_file(tmpdir) -> str:
return path
async def test_images(app: FastAPI, client: AsyncClient, tmpdir, fake_image: str, fake_file: str) -> None:
async def test_images(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 client.get(app.url_path_for("get_dynamips_images"))
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",
@ -173,9 +173,9 @@ async def test_images(app: FastAPI, client: AsyncClient, tmpdir, fake_image: str
"md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}]
async def test_upload_image(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_upload_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
response = await client.post(app.url_path_for("upload_dynamips_image", filename="test2"), content=b"TEST")
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:
@ -186,36 +186,36 @@ async def test_upload_image(app: FastAPI, client: AsyncClient, images_dir: str)
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
async def test_upload_image_forbidden_location(app: FastAPI, client: AsyncClient) -> None:
async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient) -> None:
file_path = "%2e%2e/hello"
response = await client.post(app.url_path_for("upload_dynamips_image", filename=file_path), content=b"TEST")
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, client: AsyncClient, images_dir: str) -> None:
async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
response = await client.post(app.url_path_for("upload_dynamips_image", filename="test3"), content=b"TEST")
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 client.get(app.url_path_for("download_dynamips_image", filename="test3"))
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, client: AsyncClient, tmpdir) -> None:
async def test_download_image_forbidden(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 client.get(app.url_path_for("download_dynamips_image", filename=file_path))
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(not sys.platform.startswith("win") and os.getuid() == 0, reason="Root can delete any image")
async def test_upload_image_permission_denied(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_upload_image_permission_denied(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)
response = await client.post(app.url_path_for("upload_dynamips_image", filename="test2"), content=b"TEST")
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

View File

@ -51,9 +51,9 @@ def base_params(tmpdir, fake_iou_bin) -> dict:
@pytest.fixture
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=base_params)
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()
@ -65,9 +65,9 @@ def startup_config_file(compute_project: Project, vm: dict) -> str:
return os.path.join(directory, "startup-config.cfg")
async def test_iou_create(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> None:
async def test_iou_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None:
response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=base_params)
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"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id
@ -79,7 +79,7 @@ async def test_iou_create(app: FastAPI, client: AsyncClient, compute_project: Pr
async def test_iou_create_with_params(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
base_params: dict) -> None:
@ -92,7 +92,7 @@ async def test_iou_create_with_params(app: FastAPI,
params["startup_config_content"] = "hostname test"
params["use_default_iou_values"] = False
response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=params)
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"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id
@ -109,7 +109,7 @@ async def test_iou_create_with_params(app: FastAPI,
async def test_iou_create_startup_config_already_exist(
app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
base_params: dict) -> None:
"""We don't erase a startup-config if already exist at project creation"""
@ -123,16 +123,16 @@ async def test_iou_create_startup_config_already_exist(
params["node_id"] = node_id
params["startup_config_content"] = "hostname test"
response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=params)
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"
async def test_iou_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_iou_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.get(app.url_path_for("get_iou_node", project_id=vm["project_id"], node_id=vm["node_id"]))
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"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id
@ -143,58 +143,58 @@ async def test_iou_get(app: FastAPI, client: AsyncClient, compute_project: Proje
assert response.json()["l1_keepalives"] is False
async def test_iou_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_start(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 client.post(app.url_path_for("start_iou_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_iou_start_with_iourc(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 client.post(app.url_path_for("start_iou_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_iou_stop(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 client.post(app.url_path_for("stop_iou_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_iou_reload(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 client.post(app.url_path_for("reload_iou_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_iou_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock:
response = await client.delete(app.url_path_for("delete_iou_node",
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, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_iou_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
params = {
"name": "test",
@ -207,7 +207,7 @@ async def test_iou_update(app: FastAPI, client: AsyncClient, vm: dict, free_cons
"use_default_iou_values": True,
}
response = await client.put(app.url_path_for("update_iou_node",
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
@ -221,70 +221,70 @@ async def test_iou_update(app: FastAPI, client: AsyncClient, vm: dict, free_cons
assert response.json()["use_default_iou_values"] is True
async def test_iou_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_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("create_iou_node_nio",
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 client.post(url, json=params)
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, client: AsyncClient, vm: dict) -> None:
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("create_iou_node_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
params["filters"] = {}
url = app.url_path_for("update_iou_node_nio",
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 client.put(url, json=params)
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, client: AsyncClient, vm: dict, ethernet_device: str) -> None:
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("create_iou_node_nio",
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 client.post(url, json=params)
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,
client: AsyncClient,
compute_client: AsyncClient,
vm: dict,
ethernet_device: str) -> None:
@ -293,36 +293,36 @@ async def test_iou_nio_create_ethernet_different_port(app: FastAPI,
"ethernet_device": ethernet_device
}
url = app.url_path_for("create_iou_node_nio",
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 client.post(url, json=params)
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, client: AsyncClient, vm: dict, ethernet_device: str) -> None:
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("create_iou_node_nio",
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 client.post(url, json=params)
response = await compute_client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_tap"
async def test_iou_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -331,32 +331,32 @@ async def test_iou_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> No
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_iou_node_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
url = app.url_path_for("delete_iou_node_nio",
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 client.delete(url)
response = await compute_client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_iou_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_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("start_iou_node_capture",
url = app.url_path_for("compute:start_iou_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -364,15 +364,15 @@ async def test_iou_start_capture(app: FastAPI, client: AsyncClient, vm: dict) ->
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 client.post(url, json=params)
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_iou_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("stop_iou_node_capture",
url = app.url_path_for("compute:stop_iou_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -380,7 +380,7 @@ async def test_iou_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) ->
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 client.post(url)
response = await compute_client.post(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called
@ -390,21 +390,21 @@ async def test_iou_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) ->
#
# with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"):
# with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"):
# response = await 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)
# 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_images(app: FastAPI, client: AsyncClient, fake_iou_bin: str) -> None:
async def test_images(app: FastAPI, compute_client: AsyncClient, fake_iou_bin: str) -> None:
response = await client.get(app.url_path_for("get_iou_images"))
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, client: AsyncClient, tmpdir) -> None:
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 client.post(app.url_path_for("upload_iou_image", filename="test2"), content=b"TEST")
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
with open(str(tmpdir / "test2")) as f:
@ -415,38 +415,38 @@ async def test_upload_image(app: FastAPI, client: AsyncClient, tmpdir) -> None:
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
async def test_upload_image_forbidden_location(app: FastAPI, client: AsyncClient) -> None:
async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient) -> None:
file_path = "%2e%2e/hello"
response = await client.post(app.url_path_for("upload_dynamips_image", filename=file_path), content=b"TEST")
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, client: AsyncClient, images_dir: str) -> None:
async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
response = await client.post(app.url_path_for("upload_dynamips_image", filename="test3"), content=b"TEST")
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 client.get(app.url_path_for("download_dynamips_image", filename="test3"))
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, client: AsyncClient, tmpdir) -> None:
async def test_download_image_forbidden(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 client.get(app.url_path_for("download_iou_image", filename=file_path))
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(app: FastAPI, client: AsyncClient, vm: dict, base_params: dict) -> None:
async def test_iou_duplicate(app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None:
# create destination node first
response = await client.post(app.url_path_for("create_iou_node", project_id=vm["project_id"]), json=base_params)
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 client.post(app.url_path_for("duplicate_iou_node",
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

View File

@ -27,35 +27,35 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function")
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, ubridge_path: str, on_gns3vm) -> dict:
async def vm(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 client.post(app.url_path_for("create_nat_node", project_id=compute_project.id),
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, client: AsyncClient, compute_project: Project, on_gns3vm) -> None:
async def test_nat_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> None:
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
response = await client.post(app.url_path_for("create_nat_node", project_id=compute_project.id),
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_nat_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.get(app.url_path_for("get_nat_node", project_id=vm["project_id"], node_id=vm["node_id"]))
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, client: AsyncClient, vm: dict) -> None:
async def test_nat_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -64,19 +64,19 @@ async def test_nat_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_nat_node_nio",
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 client.post(url, json=params)
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, client: AsyncClient, vm: dict) -> None:
async def test_nat_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -85,26 +85,26 @@ async def test_nat_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict) -
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_nat_node_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
params["filters"] = {}
url = app.url_path_for("update_nat_node_nio",
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 client.put(url, json=params)
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, client: AsyncClient, vm: dict) -> None:
async def test_nat_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -113,72 +113,72 @@ async def test_nat_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> No
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_nat_node_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
url = app.url_path_for("delete_nat_node_nio",
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 client.delete(url)
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, client: AsyncClient, vm: dict) -> None:
async def test_nat_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
response = await client.delete(app.url_path_for("delete_nat_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_nat_update(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
response = await client.put(app.url_path_for("update_nat_node",
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, client: AsyncClient, vm: dict) -> None:
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("start_nat_node_capture",
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 client.post(url, json=params)
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(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("stop_nat_node_capture",
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 client.post(url)
response = await compute_client.post(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called
@ -188,5 +188,5 @@ async def test_nat_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) ->
#
# with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"):
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"):
# response = await 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)
# 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

View File

@ -42,40 +42,40 @@ def base_params(tmpdir) -> dict:
return params
async def test_create_project_with_path(app: FastAPI, client: AsyncClient, base_params: dict) -> None:
async def test_create_project_with_path(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
with patch("gns3server.compute.project.Project.is_local", return_value=True):
response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
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"]
async def test_create_project_with_path_and_empty_variables(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
base_params: dict) -> None:
base_params["variables"] = None
with patch("gns3server.compute.project.Project.is_local", return_value=True):
response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
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"]
async def test_create_project_without_dir(app: FastAPI, client: AsyncClient, base_params: dict) -> None:
async def test_create_project_without_dir(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
del base_params["path"]
response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
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, client: AsyncClient, base_params: dict) -> None:
async def test_show_project(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
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 client.get(app.url_path_for("get_compute_project", project_id=base_params["project_id"]))
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
@ -84,60 +84,60 @@ async def test_show_project(app: FastAPI, client: AsyncClient, base_params: dict
assert response.json()["variables"] is None
async def test_show_project_invalid_uuid(app: FastAPI, client: AsyncClient) -> None:
async def test_show_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None:
response = await client.get(app.url_path_for("get_compute_project",
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, client: AsyncClient) -> dict:
async def test_list_projects(app: FastAPI, compute_client: AsyncClient) -> dict:
ProjectManager.instance()._projects = {}
params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await client.post(app.url_path_for("create_compute_project"), json=params)
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 client.post(app.url_path_for("create_compute_project"), json=params)
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 client.get(app.url_path_for("get_compute_projects"))
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, client: AsyncClient, compute_project: Project) -> None:
async def test_delete_project(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock:
response = await client.delete(app.url_path_for("delete_compute_project", project_id=compute_project.id))
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, client: AsyncClient, base_params: dict) -> None:
async def test_update_project(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None:
response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
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 client.put(app.url_path_for("update_compute_project", project_id=base_params["project_id"]),
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, client: AsyncClient) -> None:
async def test_delete_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None:
response = await client.delete(app.url_path_for("delete_compute_project", project_id=str(uuid.uuid4())))
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, client: AsyncClient, compute_project: Project) -> None:
async def test_close_project(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
response = await client.post(app.url_path_for("close_compute_project", project_id=compute_project.id))
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
@ -147,18 +147,18 @@ async def test_close_project(app: FastAPI, client: AsyncClient, compute_project:
#
# ProjectHandler._notifications_listening = {compute_project.id: 2}
# with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
# response = await client.post("/projects/{project_id}/close".format(project_id=compute_project.id))
# 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, client: AsyncClient) -> None:
async def test_close_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None:
response = await client.post(app.url_path_for("close_compute_project", project_id=str(uuid.uuid4())))
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, client: AsyncClient, config, tmpdir) -> None:
async def test_get_file(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")
@ -166,27 +166,27 @@ async def test_get_file(app: FastAPI, client: AsyncClient, config, tmpdir) -> No
with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world")
response = await client.get(app.url_path_for("get_compute_project_file", project_id=project.id, file_path="hello"))
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 client.get(app.url_path_for("get_compute_project_file", project_id=project.id, file_path="false"))
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 client.get(app.url_path_for("get_compute_project_file",
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, client: AsyncClient, config, tmpdir) -> None:
async def test_get_file_forbidden_location(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 client.get(
response = await compute_client.get(
app.url_path_for(
"get_compute_project_file",
"compute:get_compute_project_file",
project_id=project.id,
file_path=file_path
)
@ -194,12 +194,12 @@ async def test_get_file_forbidden_location(app: FastAPI, client: AsyncClient, co
assert response.status_code == status.HTTP_403_FORBIDDEN
async def test_write_file(app: FastAPI, client: AsyncClient, config, tmpdir) -> None:
async def test_write_file(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")
response = await client.post(app.url_path_for("write_compute_project_file",
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
@ -207,19 +207,19 @@ async def test_write_file(app: FastAPI, client: AsyncClient, config, tmpdir) ->
with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world"
response = await client.post(app.url_path_for("write_compute_project_file",
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, client: AsyncClient, config, tmpdir) -> None:
async def test_write_file_forbidden_location(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 = "%2e%2e/hello"
response = await client.post(app.url_path_for("write_compute_project_file",
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

View File

@ -64,20 +64,20 @@ def base_params(tmpdir, fake_qemu_bin) -> dict:
@pytest.fixture
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> None:
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None:
response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=base_params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params)
assert response.status_code == status.HTTP_201_CREATED
return response.json()
async def test_qemu_create(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_bin: str) -> None:
response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=base_params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id
@ -86,14 +86,14 @@ async def test_qemu_create(app: FastAPI,
async def test_qemu_create_platform(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_bin: str):
base_params["qemu_path"] = None
base_params["platform"] = "x86_64"
response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=base_params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id
@ -103,7 +103,7 @@ async def test_qemu_create_platform(app: FastAPI,
@pytest.mark.asyncio
async def test_qemu_create_with_params(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_vm: str):
@ -111,7 +111,7 @@ async def test_qemu_create_with_params(app: FastAPI,
params = base_params
params["ram"] = 1024
params["hda_disk_image"] = "linux载.img"
response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_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
@ -122,26 +122,26 @@ async def test_qemu_create_with_params(app: FastAPI,
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
async def test_qemu_create_with_project_file(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_vm: str) -> None:
response = await client.post(app.url_path_for("write_compute_project_file",
response = await compute_client.post(app.url_path_for("compute:write_compute_project_file",
project_id=compute_project.id,
file_path="hello.img"), content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT
params = base_params
params["hda_disk_image"] = "hello.img"
response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["hda_disk_image"] == "hello.img"
assert response.json()["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7"
async def test_qemu_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict):
async def test_qemu_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict):
response = await client.get(app.url_path_for("get_qemu_node", project_id=vm["project_id"], node_id=vm["node_id"]))
response = await compute_client.get(app.url_path_for("compute:get_qemu_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
@ -151,60 +151,60 @@ async def test_qemu_get(app: FastAPI, client: AsyncClient, compute_project: Proj
vm["node_id"])
async def test_qemu_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock:
response = await client.post(app.url_path_for("start_qemu_node",
response = await compute_client.post(app.url_path_for("compute:start_qemu_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_qemu_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock:
response = await client.post(app.url_path_for("stop_qemu_node",
response = await compute_client.post(app.url_path_for("compute:stop_qemu_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_qemu_reload(app: FastAPI, client: AsyncClient, vm) -> None:
async def test_qemu_reload(app: FastAPI, compute_client: AsyncClient, vm) -> None:
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock:
response = await client.post(app.url_path_for("reload_qemu_node",
response = await compute_client.post(app.url_path_for("compute:reload_qemu_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_qemu_suspend(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_suspend(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock:
response = await client.post(app.url_path_for("suspend_qemu_node",
response = await compute_client.post(app.url_path_for("compute:suspend_qemu_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_qemu_resume(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_resume(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock:
response = await client.post(app.url_path_for("resume_qemu_node",
response = await compute_client.post(app.url_path_for("compute:resume_qemu_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_qemu_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock:
response = await client.delete(app.url_path_for("delete_qemu_node",
response = await compute_client.delete(app.url_path_for("compute:delete_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called
@ -212,7 +212,7 @@ async def test_qemu_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_update(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
vm: dict,
free_console_port: int,
fake_qemu_vm: str) -> None:
@ -224,7 +224,7 @@ async def test_qemu_update(app: FastAPI,
"hdb_disk_image": "linux载.img"
}
response = await client.put(app.url_path_for("update_qemu_node",
response = await compute_client.put(app.url_path_for("compute:update_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_200_OK
@ -234,7 +234,7 @@ async def test_qemu_update(app: FastAPI,
assert response.json()["ram"] == 1024
async def test_qemu_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -244,21 +244,21 @@ async def test_qemu_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict)
}
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"):
await client.put(app.url_path_for("update_qemu_node",
await compute_client.put(app.url_path_for("compute:update_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2})
url = app.url_path_for("create_qemu_node_nio",
url = app.url_path_for("compute:create_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.post(url, json=params)
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_qemu_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -267,31 +267,31 @@ async def test_qemu_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict)
"rhost": "127.0.0.1"
}
await client.put(app.url_path_for("update_qemu_node",
await compute_client.put(app.url_path_for("compute:update_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2})
url = app.url_path_for("create_qemu_node_nio",
url = app.url_path_for("compute:create_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
await client.post(url, json=params)
await compute_client.post(url, json=params)
params["filters"] = {}
url = app.url_path_for("update_qemu_node_nio",
url = app.url_path_for("compute:update_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.put(url, json=params)
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_qemu_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -301,39 +301,39 @@ async def test_qemu_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> N
}
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"):
await client.put(app.url_path_for("update_qemu_node",
await compute_client.put(app.url_path_for("compute:update_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2})
url = app.url_path_for("create_qemu_node_nio",
url = app.url_path_for("compute:create_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
await client.post(url, json=params)
await compute_client.post(url, json=params)
url = app.url_path_for("delete_qemu_node_nio",
url = app.url_path_for("compute:delete_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.delete(url)
response = await compute_client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_qemu_list_binaries(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_list_binaries(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
ret = [{"path": "/tmp/1", "version": "2.2.0"},
{"path": "/tmp/2", "version": "2.1.0"}]
with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock:
response = await client.get(app.url_path_for("get_qemu_binaries"))
response = await compute_client.get(app.url_path_for("compute:get_qemu_binaries"))
assert mock.called_with(None)
assert response.status_code == status.HTTP_200_OK
assert response.json() == ret
# async def test_qemu_list_binaries_filter(app: FastAPI, client: AsyncClient, vm: dict) -> None:
# async def test_qemu_list_binaries_filter(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
#
# ret = [
# {"path": "/tmp/x86_64", "version": "2.2.0"},
@ -342,25 +342,25 @@ async def test_qemu_list_binaries(app: FastAPI, client: AsyncClient, vm: dict) -
# ]
#
# with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock:
# response = await client.get(app.url_path_for("get_qemu_binaries"),
# response = await compute_client.get(app.url_path_for("compute:get_qemu_binaries"),
# json={"archs": ["i386"]})
# assert response.status_code == status.HTTP_200_OK
# assert mock.called_with(["i386"])
# assert response.json() == ret
async def test_images(app: FastAPI, client: AsyncClient, fake_qemu_vm) -> None:
async def test_images(app: FastAPI, compute_client: AsyncClient, fake_qemu_vm) -> None:
response = await client.get(app.url_path_for("get_qemu_images"))
response = await compute_client.get(app.url_path_for("compute:get_qemu_images"))
assert response.status_code == status.HTTP_200_OK
assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} in response.json()
async def test_upload_image(app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
async def test_upload_image(app: FastAPI, compute_client: AsyncClient, tmpdir: str) -> None:
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await client.post(app.url_path_for("upload_qemu_image",
response = await compute_client.post(app.url_path_for("compute:upload_qemu_image",
filename="test2使"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT
@ -372,11 +372,11 @@ async def test_upload_image(app: FastAPI, client: AsyncClient, tmpdir: str) -> N
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
async def test_upload_image_ova(app: FastAPI, client: AsyncClient, tmpdir:str) -> None:
async def test_upload_image_ova(app: FastAPI, compute_client: AsyncClient, tmpdir:str) -> None:
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await client.post(app.url_path_for("upload_qemu_image",
response = await compute_client.post(app.url_path_for("compute:upload_qemu_image",
filename="test2.ova/test2.vmdk"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT
@ -388,42 +388,42 @@ async def test_upload_image_ova(app: FastAPI, client: AsyncClient, tmpdir:str) -
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
async def test_upload_image_forbidden_location(app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient, tmpdir: str) -> None:
response = await client.post(app.url_path_for("upload_qemu_image",
response = await compute_client.post(app.url_path_for("compute:upload_qemu_image",
filename="/qemu/images/../../test2"), content=b"TEST")
assert response.status_code == status.HTTP_403_FORBIDDEN
async def test_download_image(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
response = await client.post(app.url_path_for("upload_qemu_image", filename="test3"), content=b"TEST")
response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", filename="test3"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT
response = await client.get(app.url_path_for("download_qemu_image", filename="test3"))
response = await compute_client.get(app.url_path_for("compute:download_qemu_image", filename="test3"))
assert response.status_code == status.HTTP_200_OK
async def test_download_image_forbidden_location(app: FastAPI, client: AsyncClient, tmpdir) -> None:
async def test_download_image_forbidden_location(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 client.get(app.url_path_for("download_qemu_image", filename=file_path))
response = await compute_client.get(app.url_path_for("compute:download_qemu_image", filename=file_path))
assert response.status_code == status.HTTP_403_FORBIDDEN
@pytest.mark.skipif(not sys.platform.startswith("win") and os.getuid() == 0, reason="Root can delete any image")
async def test_upload_image_permission_denied(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_upload_image_permission_denied(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None:
with open(os.path.join(images_dir, "QEMU", "test2.tmp"), "w+") as f:
f.write("")
os.chmod(os.path.join(images_dir, "QEMU", "test2.tmp"), 0)
response = await client.post(app.url_path_for("upload_qemu_image", filename="test2"), content=b"TEST")
response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", filename="test2"), content=b"TEST")
assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio
async def test_create_img_relative(app: FastAPI, client: AsyncClient):
async def test_create_img_relative(app: FastAPI, compute_client: AsyncClient):
params = {
"qemu_img": "/tmp/qemu-img",
@ -436,11 +436,11 @@ async def test_create_img_relative(app: FastAPI, client: AsyncClient):
"size": 100
}
with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = await client.post(app.url_path_for("create_qemu_image"), json=params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient, config) -> None:
async def test_create_img_absolute_non_local(app: FastAPI, compute_client: AsyncClient, config) -> None:
config.settings.Server.local = False
params = {
@ -454,11 +454,11 @@ async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient,
"size": 100
}
with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = await client.post(app.url_path_for("create_qemu_image"), json=params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params)
assert response.status_code == 403
async def test_create_img_absolute_local(app: FastAPI, client: AsyncClient, config) -> None:
async def test_create_img_absolute_local(app: FastAPI, compute_client: AsyncClient, config) -> None:
params = {
"qemu_img": "/tmp/qemu-img",
@ -471,43 +471,43 @@ async def test_create_img_absolute_local(app: FastAPI, client: AsyncClient, conf
"size": 100
}
with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = await client.post(app.url_path_for("create_qemu_image"), json=params)
response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_capabilities(app: FastAPI, client: AsyncClient) -> None:
async def test_capabilities(app: FastAPI, compute_client: AsyncClient) -> None:
with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]):
response = await client.get(app.url_path_for("get_qemu_capabilities"))
response = await compute_client.get(app.url_path_for("compute:get_qemu_capabilities"))
assert response.json()["kvm"] == ["x86_64"]
async def test_qemu_duplicate(app: FastAPI,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
vm: dict,
base_params: dict) -> None:
# create destination node first
response = await client.post(app.url_path_for("create_qemu_node",
response = await compute_client.post(app.url_path_for("compute:create_qemu_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 client.post(app.url_path_for("duplicate_qemu_node",
response = await compute_client.post(app.url_path_for("compute:duplicate_qemu_node",
project_id=vm["project_id"], node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio
async def test_qemu_start_capture(app: FastAPI, client: AsyncClient, vm):
async def test_qemu_start_capture(app: FastAPI, compute_client: AsyncClient, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
url = app.url_path_for("start_qemu_node_capture",
url = app.url_path_for("compute:start_qemu_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -515,16 +515,16 @@ async def test_qemu_start_capture(app: FastAPI, client: AsyncClient, vm):
with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as mock:
response = await client.post(url, json=params)
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_qemu_stop_capture(app: FastAPI, client: AsyncClient, vm):
async def test_qemu_stop_capture(app: FastAPI, compute_client: AsyncClient, vm):
url = app.url_path_for("stop_qemu_node_capture",
url = app.url_path_for("compute:stop_qemu_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -532,15 +532,15 @@ async def test_qemu_stop_capture(app: FastAPI, client: AsyncClient, vm):
with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as mock:
response = await client.post(url)
response = await compute_client.post(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called
# @pytest.mark.asyncio
# async def test_qemu_pcap(app: FastAPI, client: AsyncClient, vm, compute_project):
# async def test_qemu_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project):
#
# with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"):
# with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"):
# response = await client.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# response = await compute_client.get("/projects/{project_id}/qemu/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

View File

@ -28,7 +28,7 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function")
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
vboxmanage_path = "/fake/VboxManage"
params = {
@ -38,7 +38,7 @@ async def vm(app: FastAPI, client: AsyncClient, compute_project: Project) -> Non
}
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock:
response = await client.post(app.url_path_for("create_virtualbox_node", project_id=compute_project.id),
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
@ -47,7 +47,7 @@ async def vm(app: FastAPI, client: AsyncClient, compute_project: Project) -> Non
return response.json()
async def test_vbox_create(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_vbox_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
params = {
"name": "VM1",
@ -56,16 +56,16 @@ async def test_vbox_create(app: FastAPI, client: AsyncClient, compute_project: P
}
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True):
response = await client.post(app.url_path_for("create_virtualbox_node", project_id=compute_project.id),
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(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vbox_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.get(app.url_path_for("get_virtualbox_node",
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
@ -73,58 +73,58 @@ async def test_vbox_get(app: FastAPI, client: AsyncClient, compute_project: Proj
assert response.json()["project_id"] == compute_project.id
async def test_vbox_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("start_virtualbox_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("stop_virtualbox_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("suspend_virtualbox_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("resume_virtualbox_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("reload_virtualbox_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_vbox_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -133,14 +133,14 @@ async def test_vbox_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict)
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_virtualbox_node_nio",
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 client.post(url, json=params)
response = await compute_client.post(url, json=params)
assert mock.called
args, kwgars = mock.call_args
assert args[0] == 0
@ -150,7 +150,7 @@ async def test_vbox_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict)
# @pytest.mark.asyncio
# async def test_vbox_nio_update_udp(app: FastAPI, client: AsyncClient, vm):
# async def test_vbox_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm):
#
# params = {
# "type": "nio_udp",
@ -162,22 +162,22 @@ async def test_vbox_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict)
#
# 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 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)
# 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, client: AsyncClient, vm: dict) -> None:
async def test_vbox_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("delete_virtualbox_node_nio",
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 client.delete(url)
response = await compute_client.delete(url)
assert mock.called
args, kwgars = mock.call_args
assert args[0] == 0
@ -185,14 +185,14 @@ async def test_vbox_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> N
@pytest.mark.asyncio
async def test_vbox_update(app: FastAPI, client: AsyncClient, vm, free_console_port):
async def test_vbox_update(app: FastAPI, compute_client: AsyncClient, vm, free_console_port):
params = {
"name": "test",
"console": free_console_port
}
response = await client.put(app.url_path_for("update_virtualbox_node",
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
@ -201,14 +201,14 @@ async def test_vbox_update(app: FastAPI, client: AsyncClient, vm, free_console_p
@pytest.mark.asyncio
async def test_virtualbox_start_capture(app: FastAPI, client: AsyncClient, vm):
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("start_virtualbox_node_capture",
url = app.url_path_for("compute:start_virtualbox_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -216,16 +216,16 @@ async def test_virtualbox_start_capture(app: FastAPI, client: AsyncClient, vm):
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 client.post(url, json=params)
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(app: FastAPI, client: AsyncClient, vm):
async def test_virtualbox_stop_capture(app: FastAPI, compute_client: AsyncClient, vm):
url = app.url_path_for("stop_virtualbox_node_capture",
url = app.url_path_for("compute:stop_virtualbox_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -233,15 +233,15 @@ async def test_virtualbox_stop_capture(app: FastAPI, client: AsyncClient, vm):
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 client.post(url)
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(app: FastAPI, client: AsyncClient, vm, compute_project):
# 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 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)
# 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

View File

@ -28,7 +28,7 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function")
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, vmx_path: str) -> dict:
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> dict:
params = {
"name": "VMTEST",
@ -37,7 +37,7 @@ async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, vmx_pa
}
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock:
response = await client.post(app.url_path_for("create_vmware_node", project_id=compute_project.id),
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
@ -56,7 +56,7 @@ def vmx_path(tmpdir: str) -> str:
return path
async def test_vmware_create(app: FastAPI, client: AsyncClient, compute_project: Project, vmx_path: str) -> None:
async def test_vmware_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> None:
params = {
"name": "VM1",
@ -65,72 +65,72 @@ async def test_vmware_create(app: FastAPI, client: AsyncClient, compute_project:
}
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True):
response = await client.post(app.url_path_for("create_vmware_node", project_id=compute_project.id),
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(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vmware_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.get(app.url_path_for("get_vmware_node", project_id=vm["project_id"], node_id=vm["node_id"]))
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("start_vmware_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("stop_vmware_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("suspend_vmware_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("resume_vmware_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("reload_vmware_node",
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, client: AsyncClient, vm: dict) -> None:
async def test_vmware_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -139,14 +139,14 @@ async def test_vmware_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_vmware_node_nio",
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 client.post(url, json=params)
response = await compute_client.post(url, json=params)
assert mock.called
args, kwgars = mock.call_args
assert args[0] == 0
@ -156,7 +156,7 @@ async def test_vmware_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict
# @pytest.mark.asyncio
# async def test_vmware_nio_update_udp(app: FastAPI, client: AsyncClient, vm):
# async def test_vmware_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm):
#
# params = {
# "type": "nio_udp",
@ -169,21 +169,21 @@ async def test_vmware_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict
# 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 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)
# 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, client: AsyncClient, vm: dict) -> None:
async def test_vmware_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("delete_vmware_node_nio",
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 client.delete(url)
response = await compute_client.delete(url)
assert mock.called
args, kwgars = mock.call_args
assert args[0] == 0
@ -191,14 +191,14 @@ async def test_vmware_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) ->
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_vmware_update(app: FastAPI, client: AsyncClient, vm: dict, free_console_port: int) -> None:
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 client.put(app.url_path_for("update_vmware_node",
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
@ -206,14 +206,14 @@ async def test_vmware_update(app: FastAPI, client: AsyncClient, vm: dict, free_c
assert response.json()["console"] == free_console_port
async def test_vmware_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
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("start_vmware_node_capture",
url = app.url_path_for("compute:start_vmware_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -222,15 +222,15 @@ async def test_vmware_start_capture(app: FastAPI, client: AsyncClient, vm: dict)
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 client.post(url, json=params)
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(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("stop_vmware_node_capture",
url = app.url_path_for("compute:stop_vmware_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -238,15 +238,15 @@ async def test_vmware_stop_capture(app: FastAPI, client: AsyncClient, vm: dict)
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 client.post(url)
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(app: FastAPI, client: AsyncClient, vm, compute_project):
# 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 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)
# 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

View File

@ -28,47 +28,47 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture
async def vm(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
params = {"name": "PC TEST 1"}
response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
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, client: AsyncClient, compute_project: Project) -> None:
async def test_vpcs_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None:
params = {"name": "PC TEST 1"}
response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vpcs_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None:
response = await client.get(app.url_path_for("get_vpcs_node", project_id=vm["project_id"], node_id=vm["node_id"]))
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, client: AsyncClient, compute_project: Project) -> None:
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 client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
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,
client: AsyncClient,
compute_client: AsyncClient,
compute_project: Project,
free_console_port: int) -> None:
@ -77,14 +77,14 @@ async def test_vpcs_create_port(app: FastAPI,
"console": free_console_port
}
response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
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, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -93,19 +93,19 @@ async def test_vpcs_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict)
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_vpcs_node_nio",
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 client.post(url, json=params)
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, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -114,28 +114,28 @@ async def test_vpcs_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict)
"rhost": "127.0.0.1"
}
url = app.url_path_for("create_vpcs_node_nio",
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 client.post(url, json=params)
response = await compute_client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
params["filters"] = {}
url = app.url_path_for("update_vpcs_node_nio",
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 client.put(url, json=params)
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, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
params = {
"type": "nio_udp",
@ -145,78 +145,78 @@ async def test_vpcs_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> N
}
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"):
url = app.url_path_for("create_vpcs_node_nio",
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 client.post(url, json=params)
await compute_client.post(url, json=params)
url = app.url_path_for("delete_vpcs_node_nio",
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 client.delete(url)
response = await compute_client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_vpcs_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("start_vpcs_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("stop_vpcs_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.post(app.url_path_for("reload_vpcs_node",
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, client: AsyncClient, vm: dict) -> None:
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 client.delete(app.url_path_for("delete_vpcs_node",
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, client: AsyncClient, compute_project: Project, vm: dict) -> None:
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 client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
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 client.post(app.url_path_for("duplicate_vpcs_node",
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, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_vpcs_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None:
console_port = free_console_port
params = {
@ -224,7 +224,7 @@ async def test_vpcs_update(app: FastAPI, client: AsyncClient, vm: dict, free_con
"console": console_port
}
response = await client.put(app.url_path_for("update_vpcs_node",
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
@ -232,14 +232,14 @@ async def test_vpcs_update(app: FastAPI, client: AsyncClient, vm: dict, free_con
assert response.json()["console"] == console_port
async def test_vpcs_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
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("start_vpcs_node_capture",
url = app.url_path_for("compute:start_vpcs_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -247,15 +247,15 @@ async def test_vpcs_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -
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 client.post(url, json=params)
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(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None:
url = app.url_path_for("stop_vpcs_node_capture",
url = app.url_path_for("compute:stop_vpcs_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
@ -263,15 +263,15 @@ async def test_vpcs_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) ->
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 client.post(url)
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(app: FastAPI, client: AsyncClient, vm, compute_project: Project):
# 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 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)
# 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

View File

@ -190,24 +190,3 @@ class TestComputeFeatures:
# response = await client.post(app.url_path_for("autoidlepc", compute_id=compute_id) + "/auto_idlepc", json=params)
# assert mock.called
# assert response.status_code == status.HTTP_200_OK
# FIXME
# @pytest.mark.asyncio
# async def test_compute_endpoint(controller_api):
#
# params = {
# "compute_id": "my_compute",
# "protocol": "http",
# "host": "localhost",
# "port": 84,
# "user": "julien",
# "password": "secure"
# }
#
# response = await controller_api.post("/computes", params)
# assert response.status_code == 201
#
# response = await controller_api.get("/computes/endpoint/my_compute/qemu/images")
# assert response.status_code == 200
# assert response.json['endpoint'] == 'http://localhost:84/v2/compute/qemu/images'

View File

@ -273,7 +273,7 @@ class TestImageRoutes:
image_data = f.read()
response = await client.post(
app.url_path_for("upload_image", image_path=image_name),
params={"image_type": "qemu"},
params={"image_type": "qemu", "install_appliances": "true"},
content=image_data)
assert response.status_code == status.HTTP_201_CREATED

View File

@ -92,7 +92,7 @@ class TestPermissionRoutes:
response = await client.get(app.url_path_for("get_permissions"))
assert response.status_code == status.HTTP_200_OK
assert len(response.json()) == 10 # 5 default permissions + 5 custom permissions
assert len(response.json()) == 11 # 6 default permissions + 5 custom permissions
async def test_update_permission(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession, project: Project) -> None:
@ -132,4 +132,4 @@ class TestPermissionRoutes:
rbac_repo = RbacRepository(db_session)
permissions_in_db = await rbac_repo.get_permissions()
assert len(permissions_in_db) == 9 # 5 default permissions + 4 custom permissions
assert len(permissions_in_db) == 10 # 6 default permissions + 4 custom permissions

View File

@ -142,7 +142,7 @@ class TestRolesPermissionsRoutes:
)
assert response.status_code == status.HTTP_204_NO_CONTENT
permissions = await rbac_repo.get_role_permissions(role_in_db.role_id)
assert len(permissions) == 5 # 4 default permissions + 1 custom permission
assert len(permissions) == 6 # 5 default permissions + 1 custom permission
async def test_get_role_permissions(
self,
@ -160,7 +160,7 @@ class TestRolesPermissionsRoutes:
role_id=role_in_db.role_id)
)
assert response.status_code == status.HTTP_200_OK
assert len(response.json()) == 5 # 4 default permissions + 1 custom permission
assert len(response.json()) == 6 # 5 default permissions + 1 custom permission
async def test_remove_role_from_group(
self,
@ -182,4 +182,4 @@ class TestRolesPermissionsRoutes:
)
assert response.status_code == status.HTTP_204_NO_CONTENT
permissions = await rbac_repo.get_role_permissions(role_in_db.role_id)
assert len(permissions) == 4 # 4 default permissions
assert len(permissions) == 5 # 5 default permissions

View File

@ -29,7 +29,7 @@ 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() == {'local': True, 'version': __version__}
assert response.json() == {'controller_host': '127.0.0.1', 'local': True, 'version': __version__}
async def test_version_input(app: FastAPI, client: AsyncClient) -> None:

View File

@ -1015,6 +1015,7 @@ async def test_stop(vm):
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="running"):
with asyncio_patch("gns3server.compute.docker.Docker.query") as mock_query:
vm._permissions_fixed = False
await vm.stop()
mock_query.assert_called_with("POST", "containers/e90e34656842/stop", params={"t": 5})
assert mock.stop.called

View File

@ -6,6 +6,7 @@ import sys
import os
import uuid
import configparser
import base64
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
@ -180,6 +181,18 @@ async def client(base_client: AsyncClient) -> AsyncClient:
return base_client
@pytest.fixture
async def compute_client(base_client: AsyncClient) -> AsyncClient:
# default compute username is 'admin'
base64_credentials = base64.b64encode(b"admin:").decode("ascii")
base_client.headers = {
**base_client.headers,
"Authorization": f"Basic {base64_credentials}",
}
return base_client
@pytest.fixture
def controller_config_path(tmpdir):
@ -394,7 +407,6 @@ def run_around_tests(monkeypatch, config, port_manager):#port_manager, controlle
config.settings.Server.ubridge_path = os.path.join(tmppath, 'bin', 'ubridge')
config.settings.Server.local = True
config.settings.Server.enable_http_auth = False
# Prevent executions of the VM if we forgot to mock something
config.settings.VirtualBox.vboxmanage_path = tmppath

Some files were not shown because too many files have changed in this diff Show More