1
0
mirror of https://github.com/GNS3/gns3-server synced 2025-02-15 09:32:00 +00:00

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 # 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 ## 2.2.26 08/10/2021
* Release web UI 2.2.26 * Release web UI 2.2.26

View File

@ -1,21 +1,37 @@
FROM python:3.6-alpine3.11 FROM ubuntu:focal
WORKDIR /gns3server WORKDIR /gns3server
RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y \
locales \
locales-all
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONBUFFERED 1
COPY ./requirements.txt /gns3server/requirements.txt COPY ./requirements.txt /gns3server/requirements.txt
RUN set -eux \ RUN DEBIAN_FRONTEND=noninteractive apt install -y \
&& apk add --no-cache --virtual .build-deps build-base \ locales \
gcc libc-dev musl-dev linux-headers python3-dev \ software-properties-common \
vpcs qemu libvirt ubridge \ python3-pip \
&& pip install --no-cache-dir --upgrade pip setuptools wheel \ python3-all \
&& pip install --no-cache-dir -r /gns3server/requirements.txt 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 COPY . /gns3server
RUN mkdir -p ~/.config/GNS3/3.0/
RUN cp scripts/gns3_server.conf ~/.config/GNS3/3.0/
RUN python3 setup.py install 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 * Gentoo: https://packages.gentoo.org/package/net-misc/gns3-server
* Alpine: https://pkgs.alpinelinux.org/package/v3.10/community/x86_64/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) 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] [Server]
; What protocol the server uses (http or https) ; 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 executable location, default: search in PATH
;ubridge_path = ubridge ;ubridge_path = ubridge
; Option to enable HTTP authentication. ; Username for compute HTTP authentication.
enable_http_auth = False compute_username = gns3
; Username for HTTP authentication. ; Password for compute HTTP authentication.
user = gns3 compute_password = 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"
; Only allow these interfaces to be used by GNS3, for the Cloud node for example (Linux/OSX only) ; 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 ; 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 is virbr0 on Linux (requires libvirt) and vmnet8 for other platforms (requires VMware)
default_nat_interface = vmnet10 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]
; VPCS executable location, default: search in PATH ; VPCS executable location, default: search in PATH
;vpcs_path = vpcs ;vpcs_path = vpcs

View File

@ -1,8 +1,8 @@
-r requirements.txt -r requirements.txt
pytest==6.2.4 pytest==6.2.5
flake8==3.9.2 flake8==4.0.1
pytest-timeout==1.4.2 pytest-timeout==2.0.1
pytest-asyncio==0.15.1 pytest-asyncio==0.16.0
requests==2.26.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/>. # 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 fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.exceptions import HTTPException as StarletteHTTPException
from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
from gns3server.compute.error import ImageMissingError, NodeError from gns3server.compute.error import ImageMissingError, NodeError
from gns3server.compute.ubridge.ubridge_error import UbridgeError from gns3server.compute.ubridge.ubridge_error import UbridgeError
from .dependencies.authentication import compute_authentication
from gns3server.compute.compute_error import ( from gns3server.compute.compute_error import (
ComputeError, ComputeError,
ComputeNotFoundError, ComputeNotFoundError,
@ -49,13 +51,15 @@ from . import virtualbox_nodes
from . import vmware_nodes from . import vmware_nodes
from . import vpcs_nodes from . import vpcs_nodes
compute_api = FastAPI( compute_api = FastAPI(
title="GNS3 compute API", title="GNS3 compute API",
dependencies=[Depends(compute_authentication)],
description="This page describes the private compute API for GNS3. PLEASE DO NOT USE DIRECTLY!", description="This page describes the private compute API for GNS3. PLEASE DO NOT USE DIRECTLY!",
version="v3", version="v3",
) )
compute_api.state.controller_host = None
@compute_api.exception_handler(ComputeError) @compute_api.exception_handler(ComputeError)
async def controller_error_handler(request: Request, exc: ComputeError): async def controller_error_handler(request: Request, exc: ComputeError):

View File

@ -21,7 +21,7 @@ API routes for capabilities
import sys import sys
import psutil import psutil
from fastapi import APIRouter from fastapi import APIRouter, Request
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.compute import MODULES from gns3server.compute import MODULES
@ -32,12 +32,15 @@ router = APIRouter()
@router.get("/capabilities", response_model=schemas.Capabilities) @router.get("/capabilities", response_model=schemas.Capabilities)
def get_capabilities() -> dict: def get_capabilities(request: Request) -> dict:
node_types = [] node_types = []
for module in MODULES: for module in MODULES:
node_types.extend(module.node_types()) node_types.extend(module.node_types())
# record the controller hostname or IP address
request.app.state.controller_host = request.client.host
return { return {
"version": __version__, "version": __version__,
"platform": sys.platform, "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. 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 websockets.exceptions import ConnectionClosed, WebSocketException
from gns3server.compute.notification_manager import NotificationManager from gns3server.compute.notification_manager import NotificationManager
@ -37,6 +41,32 @@ async def notification_ws(websocket: WebSocket) -> None:
""" """
await websocket.accept() 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") log.info(f"New client {websocket.client.host}:{websocket.client.port} has connected to compute WebSocket")
try: try:
with NotificationManager.instance().queue() as queue: with NotificationManager.instance().queue() as queue:
@ -48,8 +78,10 @@ async def notification_ws(websocket: WebSocket) -> None:
except WebSocketException as e: except WebSocketException as e:
log.warning(f"Error while sending to controller event to WebSocket client: {e}") log.warning(f"Error while sending to controller event to WebSocket client: {e}")
finally: finally:
await websocket.close() try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -19,7 +19,7 @@ API routes for computes.
""" """
from fastapi import APIRouter, Depends, Response, status from fastapi import APIRouter, Depends, Response, status
from typing import List, Union from typing import List, Union, Optional
from uuid import UUID from uuid import UUID
from gns3server.controller import Controller from gns3server.controller import Controller
@ -47,12 +47,25 @@ router = APIRouter(responses=responses)
async def create_compute( async def create_compute(
compute_create: schemas.ComputeCreate, compute_create: schemas.ComputeCreate,
computes_repo: ComputesRepository = Depends(get_repository(ComputesRepository)), computes_repo: ComputesRepository = Depends(get_repository(ComputesRepository)),
connect: Optional[bool] = False
) -> schemas.Compute: ) -> schemas.Compute:
""" """
Create a new compute on the controller. 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) @router.get("/{compute_id}", response_model=schemas.Compute, response_model_exclude_unset=True)

View File

@ -18,8 +18,9 @@ import asyncio
import signal import signal
import os 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.encoders import jsonable_encoder
from fastapi.routing import Mount
from typing import List from typing import List
from gns3server.config import Config from gns3server.config import Config
@ -41,13 +42,24 @@ router = APIRouter()
"/version", "/version",
response_model=schemas.Version, response_model=schemas.Version,
) )
def get_version() -> dict: def get_version(request: Request) -> dict:
""" """
Return the server version number. 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 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( @router.post(
@ -61,7 +73,6 @@ def check_version(version: schemas.Version) -> dict:
Check if version is the same as the server. Check if version is the same as the server.
""" """
print(version.version)
if version.version != __version__: if version.version != __version__:
raise ControllerError(f"Client version {version.version} is not the same as server version {__version__}") raise ControllerError(f"Client version {version.version} is not the same as server version {__version__}")
return {"version": __version__} return {"version": __version__}

View File

@ -16,8 +16,9 @@
import re import re
from fastapi import Request, Depends, HTTPException, status from fastapi import Request, Query, Depends, HTTPException, WebSocket, status
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from typing import Optional
from gns3server import schemas from gns3server import schemas
from gns3server.db.repositories.users import UsersRepository from gns3server.db.repositories.users import UsersRepository
@ -76,3 +77,53 @@ async def get_current_active_user(
) )
return current_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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Callable, Type 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 sqlalchemy.ext.asyncio import AsyncSession
from gns3server.db.repositories.base import BaseRepository 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) session = AsyncSession(request.app.state._db_engine, expire_on_commit=False)
try: try:

View File

@ -67,7 +67,7 @@ async def upload_image(
templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)), templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)),
current_user: schemas.User = Depends(get_current_active_user), current_user: schemas.User = Depends(get_current_active_user),
rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)), rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)),
install_appliances: Optional[bool] = True install_appliances: Optional[bool] = False
) -> schemas.Image: ) -> schemas.Image:
""" """
Upload an 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(): async def compute_pcap_stream():
try: 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(): async for data in response.content.iter_any():
if not data: if not data:
break break

View File

@ -18,14 +18,14 @@
API routes for controller notifications. 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 fastapi.responses import StreamingResponse
from websockets.exceptions import ConnectionClosed, WebSocketException from websockets.exceptions import ConnectionClosed, WebSocketException
from gns3server.services import auth_service
from gns3server.controller import Controller 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 import logging
@ -35,7 +35,7 @@ router = APIRouter()
@router.get("", dependencies=[Depends(get_current_active_user)]) @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. Receive controller notifications about the controller from HTTP stream.
""" """
@ -50,19 +50,16 @@ async def http_notification() -> StreamingResponse:
@router.websocket("/ws") @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. Receive project notifications about the controller from WebSocket.
""" """
await websocket.accept()
if token: if current_user is None:
try: return
username = auth_service.get_username_from_token(token)
except HTTPException:
log.error("Invalid token received")
await websocket.close(code=1008)
return
log.info(f"New client {websocket.client.host}:{websocket.client.port} has connected to controller WebSocket") log.info(f"New client {websocket.client.host}:{websocket.client.port} has connected to controller WebSocket")
try: try:
@ -75,4 +72,7 @@ async def notification_ws(websocket: WebSocket, token: str = Query(None)) -> Non
except WebSocketException as e: except WebSocketException as e:
log.warning(f"Error while sending to controller event to WebSocket client: {e}") log.warning(f"Error while sending to controller event to WebSocket client: {e}")
finally: 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.db.repositories.templates import TemplatesRepository
from gns3server.services.templates import TemplatesService 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 from .dependencies.database import get_repository
responses = {404: {"model": schemas.ErrorMessage, "description": "Could not find project"}} 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() controller = Controller.instance()
dot_gns3_file = path dot_gns3_file = path
if Config.instance().settings.Server.local is False: project = await controller.load_project(dot_gns3_file)
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,
)
return project.asdict() return project.asdict()
@router.get("/{project_id}/notifications") @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. 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") @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. Receive project notifications about the controller from WebSocket.
""" """
if current_user is None:
return
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(str(project_id)) 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)") log.info(f"New client has connected to the notification stream for project ID '{project.id}' (WebSocket method)")
try: try:
@ -265,7 +266,10 @@ async def notification_ws(project_id: UUID, websocket: WebSocket) -> None:
except WebSocketException as e: except WebSocketException as e:
log.warning(f"Error while sending to project event to WebSocket client: {e}") log.warning(f"Error while sending to project event to WebSocket client: {e}")
finally: finally:
await websocket.close() try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
if project.auto_close: if project.auto_close:
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking # To avoid trouble with client connecting disconnecting we sleep few seconds before checking
# if someone else is not connected # if someone else is not connected

View File

@ -21,8 +21,7 @@ FastAPI app
import time import time
from fastapi import FastAPI, Request from fastapi import FastAPI, Request, HTTPException
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from sqlalchemy.exc import SQLAlchemyError 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" 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( application.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=origins, allow_origin_regex=r"http(s)?://(localhost|127.0.0.1)(:\d+)?",
allow_credentials=True, allow_credentials=True,
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],
@ -78,7 +64,7 @@ def get_application() -> FastAPI:
application.add_event_handler("shutdown", tasks.create_shutdown_handler(application)) application.add_event_handler("shutdown", tasks.create_shutdown_handler(application))
application.include_router(index.router, tags=["Index"]) application.include_router(index.router, tags=["Index"])
application.include_router(controller.router, prefix="/v3") application.include_router(controller.router, prefix="/v3")
application.mount("/v3/compute", compute_api) application.mount("/v3/compute", compute_api, name="compute")
return application 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 # make sure the content key is "message", not "detail" per default
@app.exception_handler(StarletteHTTPException) @app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException): async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse( return JSONResponse(
status_code=exc.status_code, status_code=exc.status_code,
content={"message": exc.detail}, 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", "availability": "service-contract",
"maintainer": "Aruba", "maintainer": "Aruba",
"maintainer_email": "vincent.giles@hpe.com", "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", "symbol": ":/symbols/route_switch_processor.svg",
"first_port_name": "mgmt", "first_port_name": "mgmt",
"port_name_format": "1/1/{port1}", "port_name_format": "1/1/{port1}",
@ -30,6 +30,34 @@
"process_priority": "normal" "process_priority": "normal"
}, },
"images": [ "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", "filename": "arubaoscx-disk-image-genericx86-p4-20201110192651.vmdk",
"version": "10.06.0001", "version": "10.06.0001",
@ -53,6 +81,30 @@
} }
], ],
"versions": [ "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", "name": "10.06.0001",
"images": { "images": {

View File

@ -26,6 +26,13 @@
"options": "-nographic" "options": "-nographic"
}, },
"images": [ "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", "filename": "CentOS-7-x86_64-GenericCloud-1809.qcow2",
"version": "7 (1809)", "version": "7 (1809)",
@ -33,6 +40,13 @@
"filesize": 914948096, "filesize": 914948096,
"download_url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1809.qcow2" "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", "filename": "centos-cloud-init-data.iso",
"version": "1.0", "version": "1.0",
@ -42,6 +56,20 @@
} }
], ],
"versions": [ "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)", "name": "7 (1809)",
"images": { "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": [ "images": [
{ {
"filename": "chr-7.0beta8.img", "filename": "chr-7.1rc7.img",
"version": "7.0beta8", "version": "7.1rc7",
"md5sum": "dbc5b9a1d7cc0e56d5361a0e212dbd96", "md5sum": "04bc0ae1e5fbbda1522135bc57cf6560",
"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",
"filesize": 134217728, "filesize": 134217728,
"download_url": "http://www.mikrotik.com/download", "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" "compression": "zip"
}, },
{ {
"filename": "chr-6.42.12.img", "filename": "chr-7.1.img",
"version": "6.42.12", "version": "7.1",
"md5sum": "8485c606eb38e629fb1f5356d31bbc86", "md5sum": "41545bc7b55717fe5bb1e489ee39ca45",
"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",
"filesize": 134217728, "filesize": 134217728,
"download_url": "http://www.mikrotik.com/download", "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" "compression": "zip"
}, },
{ {
"filename": "chr-6.42.img", "filename": "chr-6.49rc2.img",
"version": "6.42", "version": "6.49rc2",
"md5sum": "279bb518497b40f41c8585128916a2fb", "md5sum": "e1088f8f64ac3d6ecf2e56ac96261226",
"filesize": 134217728, "filesize": 67108864,
"download_url": "http://www.mikrotik.com/download", "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" "compression": "zip"
} }
], ],
"versions": [ "versions": [
{ {
"name": "7.0beta8", "name": "7.1rc7",
"images": { "images": {
"hda_disk_image": "chr-7.0beta8.img" "hda_disk_image": "chr-7.1rc7.img"
} }
}, },
{ {
"name": "6.47", "name": "7.1",
"images": { "images": {
"hda_disk_image": "chr-6.47.img" "hda_disk_image": "chr-7.1.img"
} }
}, },
{ {
"name": "6.46.5", "name": "6.49rc2",
"images": { "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": { "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": { "images": {
"hda_disk_image": "chr-6.45.6.img" "hda_disk_image": "chr-6.48.5.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"
} }
} }
] ]

View File

@ -23,6 +23,15 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "openwrt-21.02.0-x86-64-generic-ext4-combined.img",
"version": "21.02.0", "version": "21.02.0",
@ -187,6 +196,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "21.02.1",
"images": {
"hda_disk_image": "openwrt-21.02.1-x86-64-generic-ext4-combined.img"
}
},
{ {
"name": "21.02.0", "name": "21.02.0",
"images": { "images": {

View File

@ -25,6 +25,13 @@
"kvm": "require" "kvm": "require"
}, },
"images": [ "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", "filename": "OPNsense-20.7-OpenSSL-nano-amd64.img",
"version": "20.7", "version": "20.7",
@ -41,6 +48,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "21.7.1",
"images": {
"hda_disk_image": "OPNsense-21.7.1-OpenSSL-nano-amd64.img"
}
},
{ {
"name": "20.7", "name": "20.7",
"images": { "images": {

View File

@ -24,6 +24,13 @@
"process_priority": "normal" "process_priority": "normal"
}, },
"images": [ "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", "filename": "pfSense-CE-2.4.5-RELEASE-p1-amd64.iso",
"version": "2.4.5-p1", "version": "2.4.5-p1",
@ -55,6 +62,13 @@
} }
], ],
"versions": [ "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", "name": "2.4.5-p1",
"images": { "images": {

View File

@ -13,7 +13,7 @@
"availability": "service-contract", "availability": "service-contract",
"maintainer": "Neyder Achahuanco", "maintainer": "Neyder Achahuanco",
"maintainer_email": "neyder@neyder.net", "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": { "qemu": {
"adapter_type": "virtio-net-pci", "adapter_type": "virtio-net-pci",
"adapters": 1, "adapters": 1,
@ -26,6 +26,20 @@
"options": "-nographic" "options": "-nographic"
}, },
"images": [ "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", "filename": "rhel-8.3-x86_64-kvm.qcow2",
"version": "8.3", "version": "8.3",
@ -56,6 +70,20 @@
} }
], ],
"versions": [ "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", "name": "8.3",
"images": { "images": {

View File

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

View File

@ -27,12 +27,19 @@
}, },
"images": [ "images": [
{ {
"filename": "vyos-1.3.0-epa1-amd64.iso", "filename": "vyos-1.3.0-amd64.iso",
"version": "1.3.0-epa1", "version": "1.3.0",
"md5sum": "a2aaa5bd3bc5827909d07a18a9de80a7", "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, "filesize": 331350016,
"download_url": "https://vyos.net/get/snapshots/", "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", "filename": "vyos-1.2.8-amd64.iso",
@ -67,10 +74,17 @@
], ],
"versions": [ "versions": [
{ {
"name": "1.3.0-epa1", "name": "1.3.0",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "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() body = await response.read()
response.close() response.close()
if body and len(body): 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")) body = json.loads(body.decode("utf-8"))
else: else:
body = body.decode("utf-8") body = body.decode("utf-8")

View File

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

View File

@ -21,6 +21,7 @@ import uuid
import socket import socket
import shutil import shutil
import asyncio import asyncio
import random
from ..config import Config from ..config import Config
from .project import Project from .project import Project
@ -73,10 +74,6 @@ class Controller:
if host == "0.0.0.0": if host == "0.0.0.0":
host = "127.0.0.1" host = "127.0.0.1"
name = socket.gethostname()
if name == "gns3vm":
name = "Main server"
self._load_controller_settings() self._load_controller_settings()
if server_config.enable_ssl: if server_config.enable_ssl:
@ -92,15 +89,16 @@ class Controller:
try: try:
self._local_server = await self.add_compute( self._local_server = await self.add_compute(
compute_id="local", compute_id="local",
name=name, name=f"{socket.gethostname()} (controller)",
protocol=protocol, protocol=protocol,
host=host, host=host,
console_host=console_host, console_host=console_host,
port=port, port=port,
user=server_config.user, user=server_config.compute_username,
password=server_config.password, password=server_config.compute_password,
force=True, force=True,
connect=True, connect=True,
wait_connection=False,
ssl_context=self._ssl_context, ssl_context=self._ssl_context,
) )
except ControllerError: except ControllerError:
@ -112,7 +110,12 @@ class Controller:
if computes: if computes:
for c in computes: for c in computes:
try: 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): except (ControllerError, KeyError):
pass # Skip not available servers at loading pass # Skip not available servers at loading
@ -154,8 +157,8 @@ class Controller:
""" """
if self._local_server: if self._local_server:
self._local_server.user = Config.instance().settings.Server.user self._local_server.user = Config.instance().settings.Server.compute_username
self._local_server.password = Config.instance().settings.Server.password self._local_server.password = Config.instance().settings.Server.compute_password
async def stop(self): async def stop(self):
@ -340,7 +343,7 @@ class Controller:
os.makedirs(configs_path, exist_ok=True) os.makedirs(configs_path, exist_ok=True)
return configs_path 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 Add a server to the dictionary of computes controlled by this controller
@ -370,8 +373,11 @@ class Controller:
self._computes[compute.id] = compute self._computes[compute.id] = compute
# self.save() # self.save()
if connect: if connect:
# call compute.connect() later to give time to the controller to be fully started if wait_connection:
asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(compute.connect())) 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()) self.notification.controller_emit("compute.created", compute.asdict())
return compute return compute
else: else:
@ -439,6 +445,16 @@ class Controller:
Returns a compute or raise a 404 error. 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: try:
return self._computes[compute_id] return self._computes[compute_id]
except KeyError: except KeyError:
@ -508,6 +524,9 @@ class Controller:
:param load: Load the topology :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 = load_topology(path)
topo_data.pop("topology") topo_data.pop("topology")
topo_data.pop("version") topo_data.pop("version")
@ -518,7 +537,10 @@ class Controller:
project = self._projects[topo_data["project_id"]] project = self._projects[topo_data["project_id"]]
else: else:
project = await self.add_project( 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: if load or project.auto_open:
await project.open() await project.open()

View File

@ -119,6 +119,7 @@ class Compute:
""" """
Set authentication parameters Set authentication parameters
""" """
if user is None or len(user.strip()) == 0: if user is None or len(user.strip()) == 0:
self._user = None self._user = None
self._password = None self._password = None
@ -153,6 +154,7 @@ class Compute:
return self._interfaces_cache return self._interfaces_cache
async def update(self, **kwargs): async def update(self, **kwargs):
for kw in kwargs: for kw in kwargs:
if kw not in ("user", "password"): if kw not in ("user", "password"):
setattr(self, kw, kwargs[kw]) setattr(self, kw, kwargs[kw])
@ -372,7 +374,7 @@ class Compute:
pass pass
@locking @locking
async def connect(self): async def connect(self, report_failed_connection=False):
""" """
Check if remote server is accessible Check if remote server is accessible
""" """
@ -382,6 +384,8 @@ class Compute:
log.info(f"Connecting to compute '{self._id}'") log.info(f"Connecting to compute '{self._id}'")
response = await self._run_http_query("GET", "/capabilities") response = await self._run_http_query("GET", "/capabilities")
except ComputeError as e: except ComputeError as e:
if report_failed_connection:
raise
log.warning(f"Cannot connect to compute '{self._id}': {e}") 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) # 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: 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 # FIXME: slow down number of compute events
self._controller.notification.controller_emit("compute.updated", self.asdict()) self._controller.notification.controller_emit("compute.updated", self.asdict())
else: else:
if action == "log.error":
log.error(event.pop("message"))
await self._controller.notification.dispatch( await self._controller.notification.dispatch(
action, event, project_id=project_id, compute_id=self.id 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) # 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 from gns3server.api.server import app
if not app.state.exiting and not hasattr(sys, "_called_from_test"): 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())) asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect()))
self._cpu_usage_percent = None self._cpu_usage_percent = None
@ -569,7 +575,7 @@ class Compute:
msg = "" msg = ""
if response.status == 401: 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: elif response.status == 403:
raise ControllerForbiddenError(msg) raise ControllerForbiddenError(msg)
elif response.status == 404: elif response.status == 404:

View File

@ -183,7 +183,7 @@ class UDPLink(Link):
self._capture_node = self._choose_capture_side() self._capture_node = self._choose_capture_side()
data = {"capture_file_name": capture_file_name, "data_link_type": data_link_type} data = {"capture_file_name": capture_file_name, "data_link_type": data_link_type}
await self._capture_node["node"].post( 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"] adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]
), ),
data=data, data=data,
@ -196,7 +196,7 @@ class UDPLink(Link):
""" """
if self._capture_node: if self._capture_node:
await self._capture_node["node"].post( 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"] 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 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 _instance = None
def __init__(self): def __init__(self):

View File

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

View File

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

View File

@ -52,18 +52,14 @@ class ComputesRepository(BaseRepository):
async def create_compute(self, compute_create: schemas.ComputeCreate) -> models.Compute: 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( db_compute = models.Compute(
compute_id=compute_create.compute_id, compute_id=compute_create.compute_id,
name=compute_create.name, name=compute_create.name,
protocol=compute_create.protocol.value, protocol=compute_create.protocol,
host=compute_create.host, host=compute_create.host,
port=compute_create.port, port=compute_create.port,
user=compute_create.user, user=compute_create.user,
password=password, password=compute_create.password.get_secret_value(),
) )
self._db_session.add(db_compute) self._db_session.add(db_compute)
await self._db_session.commit() 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]: 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) 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() db_computes = await ComputesRepository(db_session).get_computes()
for db_compute in db_computes: for db_compute in db_computes:
try: try:
compute = jsonable_encoder( compute = schemas.Compute.from_orm(db_compute)
schemas.Compute.from_orm(db_compute), exclude_unset=True, exclude={"created_at", "updated_at"}
)
except ValidationError as e: except ValidationError as e:
log.error(f"Could not load compute '{db_compute.compute_id}' from database: {e}") log.error(f"Could not load compute '{db_compute.compute_id}' from database: {e}")
continue continue

View File

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

View File

@ -24,6 +24,8 @@ class ControllerSettings(BaseModel):
jwt_secret_key: str = None jwt_secret_key: str = None
jwt_algorithm: str = "HS256" jwt_algorithm: str = "HS256"
jwt_access_token_expire_minutes: int = 1440 # 24 hours jwt_access_token_expire_minutes: int = 1440 # 24 hours
default_admin_username: str = "admin"
default_admin_password: SecretStr = SecretStr("admin")
class Config: class Config:
validate_assignment = True validate_assignment = True
@ -131,11 +133,8 @@ class ServerSettings(BaseModel):
udp_start_port_range: int = Field(10000, gt=0, le=65535) udp_start_port_range: int = Field(10000, gt=0, le=65535)
udp_end_port_range: int = Field(30000, gt=0, le=65535) udp_end_port_range: int = Field(30000, gt=0, le=65535)
ubridge_path: str = "ubridge" ubridge_path: str = "ubridge"
user: str = None compute_username: str = "admin"
password: SecretStr = None compute_password: SecretStr = SecretStr("")
enable_http_auth: bool = False
default_admin_username: str = "admin"
default_admin_password: SecretStr = SecretStr("admin")
allowed_interfaces: List[str] = Field(default_factory=list) allowed_interfaces: List[str] = Field(default_factory=list)
default_nat_interface: str = None default_nat_interface: str = None
allow_remote_console: bool = False 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") raise ValueError("vnc_console_end_port_range must be > vnc_console_start_port_range")
return v 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") @validator("enable_ssl")
def validate_enable_ssl(cls, v, values): def validate_enable_ssl(cls, v, values):

View File

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

View File

@ -50,9 +50,6 @@ class TemplateBase(BaseModel):
compute_id: Optional[str] = None compute_id: Optional[str] = None
usage: Optional[str] = "" usage: Optional[str] = ""
class Config:
extra = "allow"
class TemplateCreate(TemplateBase): class TemplateCreate(TemplateBase):
""" """
@ -61,12 +58,15 @@ class TemplateCreate(TemplateBase):
name: str name: str
template_type: NodeType template_type: NodeType
compute_id: str
class Config:
extra = "allow"
class TemplateUpdate(TemplateBase): class TemplateUpdate(TemplateBase):
pass class Config:
extra = "allow"
class Template(DateTimeModelMixin, TemplateBase): class Template(DateTimeModelMixin, TemplateBase):
@ -77,9 +77,9 @@ class Template(DateTimeModelMixin, TemplateBase):
symbol: str symbol: str
builtin: bool builtin: bool
template_type: NodeType template_type: NodeType
compute_id: Union[str, None]
class Config: class Config:
extra = "allow"
orm_mode = True 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_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_type: Optional[CloudConsoleType] = Field("none", description="Remote console type")
remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface") 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") 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") 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") 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") 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): class C3725DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(128, description="Amount of RAM in MB") 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") 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): class C3745DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(256, description="Amount of RAM in MB") 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") 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): class C3600ChassisType(str, Enum):
chassis_3620 = "3620" chassis_3620 = "3620"
@ -109,6 +127,12 @@ class C3600DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") 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): class C2691DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(192, description="Amount of RAM in MB") 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") 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): class C2600ChassisType(str, Enum):
chassis_2610 = "2610" chassis_2610 = "2610"
@ -139,6 +169,12 @@ class C2600DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") 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): class C1700ChassisType(str, Enum):
chassis_1720 = "1720" chassis_1720 = "1720"
@ -155,3 +191,9 @@ class C1700DynamipsTemplate(DynamipsTemplate):
nvram: Optional[int] = Field(128, description="Amount of NVRAM in KB") 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") iomem: Optional[int] = Field(15, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(False, description="Sparse memory feature") 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}" default_name_format: Optional[str] = "Hub{0}"
symbol: Optional[str] = ":/symbols/hub.svg" symbol: Optional[str] = ":/symbols/hub.svg"
ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports") 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" symbol: Optional[str] = ":/symbols/ethernet_switch.svg"
ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports") ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports")
console_type: Optional[ConsoleType] = Field("none", description="Console type") 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( console_auto_start: Optional[bool] = Field(
False, description="Automatically start the console when the node has started" 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}" default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/qemu_guest.svg" symbol: Optional[str] = ":/symbols/qemu_guest.svg"
qemu_path: Optional[str] = Field("", description="Qemu executable path") 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") 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") ram: Optional[int] = Field(256, description="Amount of RAM in MB")
cpus: Optional[int] = Field(1, ge=1, le=255, description="Number of vCPUs") 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") process_priority: Optional[QemuProcessPriority] = Field("normal", description="Process priority for QEMU")
options: Optional[str] = Field("", description="Additional QEMU options") options: Optional[str] = Field("", description="Additional QEMU options")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") 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" False, description="Automatically start the console when the node has started"
) )
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") 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" False, description="Automatically start the console when the node has started"
) )
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") 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( console_auto_start: Optional[bool] = Field(
False, description="Automatically start the console when the node has started" 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): class Version(BaseModel):
controller_host: Optional[str] = Field(None, description="Controller hostname or IP address")
version: str = Field(..., description="Version number") version: str = Field(..., description="Version number")
local: Optional[bool] = Field(None, description="Whether this is a local server or not") 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 signal
import functools import functools
import uvicorn import uvicorn
import secrets
import string
from gns3server.controller import Controller from gns3server.controller import Controller
from gns3server.compute.port_manager import PortManager from gns3server.compute.port_manager import PortManager
@ -38,7 +40,7 @@ from gns3server.version import __version__
from gns3server.config import Config from gns3server.config import Config
from gns3server.crash_report import CrashReport from gns3server.crash_report import CrashReport
from gns3server.api.server import app from gns3server.api.server import app
from pydantic import ValidationError from pydantic import ValidationError, SecretStr
import logging import logging
@ -199,9 +201,9 @@ class Server:
loop.add_signal_handler(getattr(signal, signal_name), callback) loop.add_signal_handler(getattr(signal, signal_name), callback)
@staticmethod @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"] detect_process = ["vpcs", "ubridge", "dynamips"]
for proc in psutil.process_iter(): for proc in psutil.process_iter():
@ -214,7 +216,7 @@ class Server:
pass pass
@staticmethod @staticmethod
def _pid_lock(self, path): def _pid_lock(path):
""" """
Write the file in a file on the system. Write the file in a file on the system.
Check if the process is not already running. Check if the process is not already running.
@ -269,8 +271,15 @@ class Server:
if config.Server.local: if config.Server.local:
log.warning("Local mode is enabled. Beware, clients will have full control on your filesystem") log.warning("Local mode is enabled. Beware, clients will have full control on your filesystem")
if config.Server.enable_http_auth: if not config.Server.compute_password.get_secret_value():
log.info(f"HTTP authentication is enabled with username '{config.Server.user}'") 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 # we only support Python 3 version >= 3.6
if sys.version_info < (3, 6, 0): if sys.version_info < (3, 6, 0):

View File

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

View File

@ -41,17 +41,17 @@ class ComputesService:
db_computes = await self._computes_repo.get_computes() db_computes = await self._computes_repo.get_computes()
return db_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): if await self._computes_repo.get_compute(compute_create.compute_id):
raise ControllerBadRequestError(f"Compute '{compute_create.compute_id}' is already registered") raise ControllerBadRequestError(f"Compute '{compute_create.compute_id}' is already registered")
db_compute = await self._computes_repo.create_compute(compute_create) 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), compute_id=str(db_compute.compute_id),
connect=False, connect=connect,
**compute_create.dict(exclude_unset=True, exclude={"compute_id"}), **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 return db_compute
async def get_compute(self, compute_id: Union[str, UUID]) -> models.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) db_compute = await self._computes_repo.update_compute(compute_id, compute_update)
if not db_compute: if not db_compute:
raise ControllerNotFoundError(f"Compute '{compute_id}' not found") 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 return db_compute
async def delete_compute(self, compute_id: Union[str, UUID]) -> None: async def delete_compute(self, compute_id: Union[str, UUID]) -> None:

View File

@ -32,7 +32,7 @@ from gns3server.controller.controller_error import (
ControllerForbiddenError, ControllerForbiddenError,
) )
TEMPLATE_TYPE_TO_SHEMA = { TEMPLATE_TYPE_TO_SCHEMA = {
"cloud": schemas.CloudTemplate, "cloud": schemas.CloudTemplate,
"ethernet_hub": schemas.EthernetHubTemplate, "ethernet_hub": schemas.EthernetHubTemplate,
"ethernet_switch": schemas.EthernetSwitchTemplate, "ethernet_switch": schemas.EthernetSwitchTemplate,
@ -45,7 +45,19 @@ TEMPLATE_TYPE_TO_SHEMA = {
"qemu": schemas.QemuTemplate, "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, "c7200": schemas.C7200DynamipsTemplate,
"c3745": schemas.C3745DynamipsTemplate, "c3745": schemas.C3745DynamipsTemplate,
"c3725": schemas.C3725DynamipsTemplate, "c3725": schemas.C3725DynamipsTemplate,
@ -55,6 +67,16 @@ DYNAMIPS_PLATFORM_TO_SHEMA = {
"c1700": schemas.C1700DynamipsTemplate, "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 # built-in templates have their compute_id set to None to tell clients to select a compute
BUILTIN_TEMPLATES = [ BUILTIN_TEMPLATES = [
{ {
@ -159,8 +181,10 @@ class TemplatesService:
async def _find_image(self, image_path: str): async def _find_image(self, image_path: str):
image = await self._templates_repo.get_image(image_path) image = await self._templates_repo.get_image(image_path)
if not image or not os.path.exists(image.path): if not image:
raise ControllerNotFoundError(f"Image '{image_path}' could not be found") 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 return image
async def _find_images(self, template_type: str, settings: dict) -> List[models.Image]: async def _find_images(self, template_type: str, settings: dict) -> List[models.Image]:
@ -205,20 +229,18 @@ class TemplatesService:
try: try:
# get the default template settings # get the default template settings
template_settings = jsonable_encoder(template_create, exclude_unset=True) create_settings = jsonable_encoder(template_create, exclude_unset=True)
template_schema = TEMPLATE_TYPE_TO_SHEMA[template_create.template_type] template_schema = TEMPLATE_TYPE_TO_SCHEMA[template_create.template_type]
template_settings_with_defaults = template_schema.parse_obj(template_settings) template_settings = template_schema.parse_obj(create_settings).dict()
settings = template_settings_with_defaults.dict()
if template_create.template_type == "dynamips": if template_create.template_type == "dynamips":
# special case for Dynamips to cover all platform types that contain specific settings # special case for Dynamips to cover all platform types that contain specific settings
dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SHEMA[settings["platform"]] dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SCHEMA[template_settings["platform"]]
dynamips_template_settings_with_defaults = dynamips_template_schema.parse_obj(template_settings) template_settings = dynamips_template_schema.parse_obj(create_settings).dict()
settings = dynamips_template_settings_with_defaults.dict()
except pydantic.ValidationError as e: except pydantic.ValidationError as e:
raise ControllerBadRequestError(f"JSON schema error received while creating new template: {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) 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, settings) db_template = await self._templates_repo.create_template(template_create.template_type, template_settings)
for image in images_to_add_to_template: for image in images_to_add_to_template:
await self._templates_repo.add_image_to_template(db_template.template_id, image) await self._templates_repo.add_image_to_template(db_template.template_id, image)
template = db_template.asjson() template = db_template.asjson()
@ -245,12 +267,22 @@ class TemplatesService:
if self.get_builtin_template(template_id): if self.get_builtin_template(template_id):
raise ControllerForbiddenError(f"Template '{template_id}' cannot be updated because it is built-in") 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) db_template = await self._templates_repo.get_template(template_id)
if not db_template: if not db_template:
raise ControllerNotFoundError(f"Template '{template_id}' not found") 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) 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: if db_template.template_type == "dynamips" and "image" in template_settings:
await self._remove_image(db_template.template_id, db_template.image) 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: if not password:
password = "" password = ""
try: try:
basic_auth = aiohttp.BasicAuth(user, password, "utf-8") basic_auth = aiohttp.BasicAuth(user, password.get_secret_value(), "utf-8")
except ValueError as e: except ValueError as e:
log.error(f"Basic authentication set-up error: {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 After=network.target network-online.target
[Service] [Service]
Type=forking
User=gns3 User=gns3
Group=gns3 Group=gns3
PermissionsStartOnly=true ExecStart=/usr/bin/gns3server
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
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

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

View File

@ -28,115 +28,115 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @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"): 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"}) json={"name": "Cloud 1"})
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
return response.json() 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"): 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"}) json={"name": "Cloud 1"})
assert response.status_code == 201 assert response.status_code == 201
assert response.json()["name"] == "Cloud 1" assert response.json()["name"] == "Cloud 1"
assert response.json()["project_id"] == compute_project.id 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.status_code == status.HTTP_200_OK
assert response.json()["name"] == "Cloud 1" assert response.json()["name"] == "Cloud 1"
assert response.json()["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json()["status"] == "started" 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", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
await client.post(url, json=params) await compute_client.post(url, json=params)
params["filters"] = {} params["filters"] = {}
url = app.url_path_for("create_cloud_nio", url = app.url_path_for("compute:create_cloud_nio",
project_id=vm["project_id"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): 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 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 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"}) json={"name": "test"})
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test" 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 = { params = {
"capture_file_name": "test.pcap", "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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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"] 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",

View File

@ -27,27 +27,33 @@ from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio 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.status_code == status.HTTP_201_CREATED
assert response.json()['udp_port'] is not None 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 response.status_code == status.HTTP_200_OK
assert isinstance(response.json(), list) 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.status_code == status.HTTP_200_OK
assert response.json() == {'local': True, 'version': __version__} 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 # @pytest.mark.asyncio
# async def test_debug_output(compute_api): # 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 # 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 assert response.status_code == status.HTTP_200_OK

View File

@ -57,22 +57,22 @@ def base_params() -> dict:
@pytest.fixture @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.list_images", return_value=[{"image": "nginx"}]):
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): 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"): 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) json=base_params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
return response.json() 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.list_images", return_value=[{"image": "nginx"}]):
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): 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) json=base_params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" 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" 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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 = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -154,17 +154,17 @@ async def test_docker_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -173,37 +173,37 @@ async def test_docker_update_nio(app: FastAPI, client: AsyncClient, vm: dict) ->
"rhost": "127.0.0.1" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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.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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"): 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 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"): 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 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 = { params = {
"name": "test", "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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) 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" 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} 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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called 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 # create destination node first
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): 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.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) project_id=vm["project_id"]), json=base_params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json()["node_id"]} 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED

View File

@ -162,10 +162,10 @@ def fake_file(tmpdir) -> str:
return path 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)): 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.status_code == status.HTTP_200_OK
assert response.json() == [{"filename": "7200.bin", assert response.json() == [{"filename": "7200.bin",
"path": "7200.bin", "path": "7200.bin",
@ -173,9 +173,9 @@ async def test_images(app: FastAPI, client: AsyncClient, tmpdir, fake_image: str
"md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}] "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 assert response.status_code == status.HTTP_204_NO_CONTENT
with open(os.path.join(images_dir, "IOS", "test2")) as f: 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" 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" 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 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 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 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" 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 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") @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) os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True)
with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f: with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0) 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 assert response.status_code == status.HTTP_409_CONFLICT

View File

@ -51,9 +51,9 @@ def base_params(tmpdir, fake_iou_bin) -> dict:
@pytest.fixture @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 assert response.status_code == status.HTTP_201_CREATED
return response.json() 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") 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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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, async def test_iou_create_with_params(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
base_params: dict) -> None: base_params: dict) -> None:
@ -92,7 +92,7 @@ async def test_iou_create_with_params(app: FastAPI,
params["startup_config_content"] = "hostname test" params["startup_config_content"] = "hostname test"
params["use_default_iou_values"] = False 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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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( async def test_iou_create_startup_config_already_exist(
app: FastAPI, app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
base_params: dict) -> None: base_params: dict) -> None:
"""We don't erase a startup-config if already exist at project creation""" """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["node_id"] = node_id
params["startup_config_content"] = "hostname test" 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 assert response.status_code == status.HTTP_201_CREATED
with open(startup_config_file(compute_project, response.json())) as f: with open(startup_config_file(compute_project, response.json())) as f:
assert f.read() == "echo hello" 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.status_code == status.HTTP_200_OK
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json={}) node_id=vm["node_id"]), json={})
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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"} params = {"iourc_content": "test"}
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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 = { params = {
"name": "test", "name": "test",
@ -207,7 +207,7 @@ async def test_iou_update(app: FastAPI, client: AsyncClient, vm: dict, free_cons
"use_default_iou_values": True, "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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_200_OK 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 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", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_number="0") port_number="0")
await client.post(url, json=params) await compute_client.post(url, json=params)
params["filters"] = {} 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_ethernet", "type": "nio_ethernet",
"ethernet_device": ethernet_device "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_ethernet" assert response.json()["type"] == "nio_ethernet"
assert response.json()["ethernet_device"] == ethernet_device assert response.json()["ethernet_device"] == ethernet_device
async def test_iou_nio_create_ethernet_different_port(app: FastAPI, async def test_iou_nio_create_ethernet_different_port(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
vm: dict, vm: dict,
ethernet_device: str) -> None: ethernet_device: str) -> None:
@ -293,36 +293,36 @@ async def test_iou_nio_create_ethernet_different_port(app: FastAPI,
"ethernet_device": ethernet_device "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="3") 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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_ethernet" assert response.json()["type"] == "nio_ethernet"
assert response.json()["ethernet_device"] == ethernet_device 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 = { params = {
"type": "nio_tap", "type": "nio_tap",
"tap_device": ethernet_device "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_number="0") port_number="0")
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): 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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_tap" 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_number="0") 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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 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 = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called 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_vm.IOUVM.get_nio"):
# with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"): # 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 # 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.status_code == status.HTTP_200_OK
assert response.json() == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}] 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)): 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 assert response.status_code == status.HTTP_204_NO_CONTENT
with open(str(tmpdir / "test2")) as f: 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" 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" 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 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 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 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" 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 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 # 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 assert response.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json()["node_id"]} 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED

View File

@ -27,35 +27,35 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @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"): 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"}) json={"name": "Nat 1"})
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
return response.json() 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"): 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"}) json={"name": "Nat 1"})
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "Nat 1" assert response.json()["name"] == "Nat 1"
assert response.json()["project_id"] == compute_project.id 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.status_code == status.HTTP_200_OK
assert response.json()["name"] == "Nat 1" assert response.json()["name"] == "Nat 1"
assert response.json()["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json()["status"] == "started" 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): 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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
await client.post(url, json=params) await compute_client.post(url, json=params)
params["filters"] = {} 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock: 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 mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert response.status_code == status.HTTP_204_NO_CONTENT 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json={"name": "test"}) node_id=vm["node_id"]), json={"name": "test"})
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test" 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 = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock: 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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called 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.nodes.nat.Nat.get_nio"):
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"): # 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 # assert response.status_code == status.HTTP_200_OK

View File

@ -42,40 +42,40 @@ def base_params(tmpdir) -> dict:
return params 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): 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.status_code == status.HTTP_201_CREATED
assert response.json()["project_id"] == base_params["project_id"] assert response.json()["project_id"] == base_params["project_id"]
async def test_create_project_with_path_and_empty_variables(app: FastAPI, async def test_create_project_with_path_and_empty_variables(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
base_params: dict) -> None: base_params: dict) -> None:
base_params["variables"] = None base_params["variables"] = None
with patch("gns3server.compute.project.Project.is_local", return_value=True): 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.status_code == status.HTTP_201_CREATED
assert response.json()["project_id"] == base_params["project_id"] 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"] 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.status_code == status.HTTP_201_CREATED
assert response.json()["project_id"] == base_params["project_id"] assert response.json()["project_id"] == base_params["project_id"]
assert response.json()["name"] == base_params["name"] 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 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()) #print(response.json().keys())
#assert len(response.json().keys()) == 3 #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 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")) project_id="50010203-0405-0607-0809-0a0b0c0d0e42"))
assert response.status_code == status.HTTP_404_NOT_FOUND 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 = {} ProjectManager.instance()._projects = {}
params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"} 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 assert response.status_code == status.HTTP_201_CREATED
params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"} 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 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 response.status_code == status.HTTP_200_OK
assert len(response.json()) == 2 assert len(response.json()) == 2
assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json()] 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called 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 assert response.status_code == status.HTTP_201_CREATED
params = {"variables": [{"name": "TEST1", "value": "VAL1"}]} 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) json=params)
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
assert response.json()["variables"] == [{"name": "TEST1", "value": "VAL1"}] 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called 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} # ProjectHandler._notifications_listening = {compute_project.id: 2}
# with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: # 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 response.status_code == status.HTTP_204_NO_CONTENT
# assert not mock.called # 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 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) config.settings.Server.projects_path = str(tmpdir)
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") 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: with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world") 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.status_code == status.HTTP_200_OK
assert response.content == b"world" 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 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, project_id=project.id,
file_path="../hello")) file_path="../hello"))
assert response.status_code == status.HTTP_404_NOT_FOUND 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) config.settings.Server.projects_path = str(tmpdir)
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") 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" 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( app.url_path_for(
"get_compute_project_file", "compute:get_compute_project_file",
project_id=project.id, project_id=project.id,
file_path=file_path 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 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) config.settings.Server.projects_path = str(tmpdir)
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") 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, project_id=project.id,
file_path="hello"), content=b"world") file_path="hello"), content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT 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: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" 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, project_id=project.id,
file_path="../hello")) file_path="../hello"))
assert response.status_code == status.HTTP_404_NOT_FOUND 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) config.settings.Server.projects_path = str(tmpdir)
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
file_path = "%2e%2e/hello" 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, project_id=project.id,
file_path=file_path), content=b"world") file_path=file_path), content=b"world")
assert response.status_code == status.HTTP_403_FORBIDDEN assert response.status_code == status.HTTP_403_FORBIDDEN

View File

@ -64,20 +64,20 @@ def base_params(tmpdir, fake_qemu_bin) -> dict:
@pytest.fixture @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 assert response.status_code == status.HTTP_201_CREATED
return response.json() return response.json()
async def test_qemu_create(app: FastAPI, async def test_qemu_create(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
base_params: dict, base_params: dict,
fake_qemu_bin: str) -> None: 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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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, async def test_qemu_create_platform(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
base_params: dict, base_params: dict,
fake_qemu_bin: str): fake_qemu_bin: str):
base_params["qemu_path"] = None base_params["qemu_path"] = None
base_params["platform"] = "x86_64" 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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
@ -103,7 +103,7 @@ async def test_qemu_create_platform(app: FastAPI,
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_qemu_create_with_params(app: FastAPI, async def test_qemu_create_with_params(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
base_params: dict, base_params: dict,
fake_qemu_vm: str): fake_qemu_vm: str):
@ -111,7 +111,7 @@ async def test_qemu_create_with_params(app: FastAPI,
params = base_params params = base_params
params["ram"] = 1024 params["ram"] = 1024
params["hda_disk_image"] = "linux载.img" 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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
async def test_qemu_create_with_project_file(app: FastAPI, async def test_qemu_create_with_project_file(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
base_params: dict, base_params: dict,
fake_qemu_vm: str) -> None: 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, project_id=compute_project.id,
file_path="hello.img"), content=b"world") file_path="hello.img"), content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT assert response.status_code == status.HTTP_204_NO_CONTENT
params = base_params params = base_params
params["hda_disk_image"] = "hello.img" 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.status_code == status.HTTP_201_CREATED
assert response.json()["hda_disk_image"] == "hello.img" assert response.json()["hda_disk_image"] == "hello.img"
assert response.json()["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7" 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.status_code == status.HTTP_200_OK
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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"]) 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called 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, async def test_qemu_update(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
vm: dict, vm: dict,
free_console_port: int, free_console_port: int,
fake_qemu_vm: str) -> None: fake_qemu_vm: str) -> None:
@ -224,7 +224,7 @@ async def test_qemu_update(app: FastAPI,
"hdb_disk_image": "linux载.img" "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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
@ -234,7 +234,7 @@ async def test_qemu_update(app: FastAPI,
assert response.json()["ram"] == 1024 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 = { params = {
"type": "nio_udp", "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"): 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2}) 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2}) 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_number="0") port_number="0")
await client.post(url, json=params) await compute_client.post(url, json=params)
params["filters"] = {} 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "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"): 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2}) 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_number="0") 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="1", adapter_number="1",
port_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 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"}, ret = [{"path": "/tmp/1", "version": "2.2.0"},
{"path": "/tmp/2", "version": "2.1.0"}] {"path": "/tmp/2", "version": "2.1.0"}]
with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock: 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 mock.called_with(None)
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
assert response.json() == ret 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 = [ # ret = [
# {"path": "/tmp/x86_64", "version": "2.2.0"}, # {"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: # 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"]}) # json={"archs": ["i386"]})
# assert response.status_code == status.HTTP_200_OK # assert response.status_code == status.HTTP_200_OK
# assert mock.called_with(["i386"]) # assert mock.called_with(["i386"])
# assert response.json() == ret # 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 response.status_code == status.HTTP_200_OK
assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} in response.json() 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)): 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") filename="test2使"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT 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" 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)): 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") filename="test2.ova/test2.vmdk"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT 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" 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") filename="/qemu/images/../../test2"), content=b"TEST")
assert response.status_code == status.HTTP_403_FORBIDDEN 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 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 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" 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 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") @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: with open(os.path.join(images_dir, "QEMU", "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(os.path.join(images_dir, "QEMU", "test2.tmp"), 0) 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 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio @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 = { params = {
"qemu_img": "/tmp/qemu-img", "qemu_img": "/tmp/qemu-img",
@ -436,11 +436,11 @@ async def test_create_img_relative(app: FastAPI, client: AsyncClient):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): 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 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 config.settings.Server.local = False
params = { params = {
@ -454,11 +454,11 @@ async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient,
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): 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 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 = { params = {
"qemu_img": "/tmp/qemu-img", "qemu_img": "/tmp/qemu-img",
@ -471,43 +471,43 @@ async def test_create_img_absolute_local(app: FastAPI, client: AsyncClient, conf
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): 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 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"]): 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"] assert response.json()["kvm"] == ["x86_64"]
async def test_qemu_duplicate(app: FastAPI, async def test_qemu_duplicate(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
vm: dict, vm: dict,
base_params: dict) -> None: base_params: dict) -> None:
# create destination node first # 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) project_id=vm["project_id"]), json=base_params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json()["node_id"]} 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) project_id=vm["project_id"], node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio @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 = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio @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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @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_vm.QemuVM.get_nio"):
# with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"): # 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 # assert response.status_code == status.HTTP_200_OK

View File

@ -28,7 +28,7 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @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" vboxmanage_path = "/fake/VboxManage"
params = { 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: 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) json=params)
assert mock.called assert mock.called
assert response.status_code == status.HTTP_201_CREATED 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() 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 = { params = {
"name": "VM1", "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): 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) json=params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "VM1" assert response.json()["name"] == "VM1"
assert response.json()["project_id"] == compute_project.id 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert response.status_code == status.HTTP_200_OK 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 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: 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 assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -150,7 +150,7 @@ async def test_vbox_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict)
# @pytest.mark.asyncio # @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 = { # params = {
# "type": "nio_udp", # "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.ethernet_adapters'):
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'): # 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.status_code == status.HTTP_201_CREATED
# assert response.json()["type"] == "nio_udp" # 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: 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 assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -185,14 +185,14 @@ async def test_vbox_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> N
@pytest.mark.asyncio @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 = { params = {
"name": "test", "name": "test",
"console": free_console_port "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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_200_OK 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 @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 = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio @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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @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_vm.VirtualBoxVM.get_nio"):
# with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"): # 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 # assert response.status_code == status.HTTP_200_OK

View File

@ -28,7 +28,7 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @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 = { params = {
"name": "VMTEST", "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: 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) json=params)
assert mock.called assert mock.called
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
@ -56,7 +56,7 @@ def vmx_path(tmpdir: str) -> str:
return path 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 = { params = {
"name": "VM1", "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): 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) json=params)
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "VM1" assert response.json()["name"] == "VM1"
assert response.json()["project_id"] == compute_project.id 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.status_code == status.HTTP_200_OK
assert response.json()["name"] == "VMTEST" assert response.json()["name"] == "VMTEST"
assert response.json()["project_id"] == compute_project.id 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock: 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 assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -156,7 +156,7 @@ async def test_vmware_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict
# @pytest.mark.asyncio # @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 = { # params = {
# "type": "nio_udp", # "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._ubridge_send'):
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'): # with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'):
# with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock: # 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.status_code == status.HTTP_201_CREATED
# assert response.json()["type"] == "nio_udp" # 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock: 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 assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 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 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 = { params = {
"name": "test", "name": "test",
"console": free_console_port "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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_200_OK 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 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 = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @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_vm.VMwareVM.get_nio"):
# with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"): # 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 # assert response.status_code == status.HTTP_200_OK

View File

@ -28,47 +28,47 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture @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"} 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.status_code == status.HTTP_201_CREATED
return response.json() 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"} 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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id 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.status_code == status.HTTP_200_OK
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json()["status"] == "stopped" 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 = { params = {
"name": "PC TEST 1", "name": "PC TEST 1",
"startup_script": "ip 192.168.1.2\necho TEST" "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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
async def test_vpcs_create_port(app: FastAPI, async def test_vpcs_create_port(app: FastAPI,
client: AsyncClient, compute_client: AsyncClient,
compute_project: Project, compute_project: Project,
free_console_port: int) -> None: free_console_port: int) -> None:
@ -77,14 +77,14 @@ async def test_vpcs_create_port(app: FastAPI,
"console": free_console_port "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.status_code == status.HTTP_201_CREATED
assert response.json()["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json()["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json()["console"] == free_console_port 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): 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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "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" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_number="0") port_number="0")
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): 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.status_code == status.HTTP_201_CREATED
params["filters"] = {} 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp" 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 = { params = {
"type": "nio_udp", "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"): 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", adapter_number="0",
port_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 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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: 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"], project_id=vm["project_id"],
node_id=vm["node_id"])) node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == status.HTTP_204_NO_CONTENT 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 # create destination node first
params = {"name": "PC TEST 1"} 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.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json()["node_id"]} 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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED 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 console_port = free_console_port
params = { params = {
@ -224,7 +224,7 @@ async def test_vpcs_update(app: FastAPI, client: AsyncClient, vm: dict, free_con
"console": console_port "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"], project_id=vm["project_id"],
node_id=vm["node_id"]), json=params) node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_200_OK 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 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 = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.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 response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json()["pcap_file_path"] 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"], project_id=vm["project_id"],
node_id=vm["node_id"], node_id=vm["node_id"],
adapter_number="0", 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 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: 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 response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @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_vm.VPCSVM.get_nio"):
# with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"): # 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 # 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) # response = await client.post(app.url_path_for("autoidlepc", compute_id=compute_id) + "/auto_idlepc", json=params)
# assert mock.called # assert mock.called
# assert response.status_code == status.HTTP_200_OK # 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() image_data = f.read()
response = await client.post( response = await client.post(
app.url_path_for("upload_image", image_path=image_name), app.url_path_for("upload_image", image_path=image_name),
params={"image_type": "qemu"}, params={"image_type": "qemu", "install_appliances": "true"},
content=image_data) content=image_data)
assert response.status_code == status.HTTP_201_CREATED 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")) response = await client.get(app.url_path_for("get_permissions"))
assert response.status_code == status.HTTP_200_OK 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: 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) rbac_repo = RbacRepository(db_session)
permissions_in_db = await rbac_repo.get_permissions() 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 assert response.status_code == status.HTTP_204_NO_CONTENT
permissions = await rbac_repo.get_role_permissions(role_in_db.role_id) 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( async def test_get_role_permissions(
self, self,
@ -160,7 +160,7 @@ class TestRolesPermissionsRoutes:
role_id=role_in_db.role_id) role_id=role_in_db.role_id)
) )
assert response.status_code == status.HTTP_200_OK 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( async def test_remove_role_from_group(
self, self,
@ -182,4 +182,4 @@ class TestRolesPermissionsRoutes:
) )
assert response.status_code == status.HTTP_204_NO_CONTENT assert response.status_code == status.HTTP_204_NO_CONTENT
permissions = await rbac_repo.get_role_permissions(role_in_db.role_id) 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")) response = await client.get(app.url_path_for("get_version"))
assert response.status_code == status.HTTP_200_OK 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: 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.DockerVM._get_container_state", return_value="running"):
with asyncio_patch("gns3server.compute.docker.Docker.query") as mock_query: with asyncio_patch("gns3server.compute.docker.Docker.query") as mock_query:
vm._permissions_fixed = False
await vm.stop() await vm.stop()
mock_query.assert_called_with("POST", "containers/e90e34656842/stop", params={"t": 5}) mock_query.assert_called_with("POST", "containers/e90e34656842/stop", params={"t": 5})
assert mock.stop.called assert mock.stop.called

View File

@ -6,6 +6,7 @@ import sys
import os import os
import uuid import uuid
import configparser import configparser
import base64
from fastapi import FastAPI from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
@ -180,6 +181,18 @@ async def client(base_client: AsyncClient) -> AsyncClient:
return base_client 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 @pytest.fixture
def controller_config_path(tmpdir): 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.ubridge_path = os.path.join(tmppath, 'bin', 'ubridge')
config.settings.Server.local = True config.settings.Server.local = True
config.settings.Server.enable_http_auth = False
# Prevent executions of the VM if we forgot to mock something # Prevent executions of the VM if we forgot to mock something
config.settings.VirtualBox.vboxmanage_path = tmppath config.settings.VirtualBox.vboxmanage_path = tmppath

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