diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 10fb15d8..1399dac6 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
- python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
+ python-version: ["3.7", "3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
diff --git a/CHANGELOG b/CHANGELOG
index cc0857d2..b453d4f8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,17 @@
# Change Log
+## 2.2.31 26/02/2022
+
+* Install setuptools v59.6.0 when using Python 3.6
+
+## 2.2.30 25/02/2022
+
+* Support GNS3 variables in Docker environment variables. Fixes #2033
+* Release web UI 2.2.30
+* Set setuptools to v60.6.0
+* qemu_vm.py Linked node test.
+* Fix dead link in README.rst Fixes #2022
+
## 2.2.29 08/01/2022
* Release web UI 2.2.29
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..b12ddd1f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,134 @@
+# GNS3 server repository
+
+[](https://github.com/psf/black)
+[](https://github.com/GNS3/gns3-server/actions?query=workflow%3Atesting+branch%3A3.0)
+[](https://pypi.python.org/pypi/gns3-server)
+[](https://snyk.io/test/github/GNS3/gns3-server)
+
+The GNS3 server manages emulators and other virtualization software such as Dynamips, Qemu/KVM, Docker, VPCS, VirtualBox and VMware Workstation.
+Clients like the [GNS3 GUI](https://github.com/GNS3/gns3-gui/) and the [GNS3 Web UI](https://github.com/GNS3/gns3-web-ui>) control the server using a HTTP REST API.
+
+## Installation
+
+These instructions are for using GNS3, please see below for development.
+
+### Windows & macOS
+
+Please use our [Windows installer or DMG package](https://gns3.com/software/download) to install the stable build along with the GNS3 VM.
+Note that as of GNS3 version above 3.0, you must run the server using the GNS3 VM or on a Linux system (remote, cloud or virtual machine).
+
+### Linux
+
+#### Ubuntu based distributions
+
+We build and test packages for actively supported Ubuntu versions.
+Other distros based on Ubuntu, like Mint, should also be supported.
+
+Packages can be installed from our Personal Package Archives (PPA) repository:
+
+```shell
+sudo apt update
+sudo apt install software-properties-common
+sudo add-apt-repository ppa:gns3/ppa
+sudo apt update
+sudo apt install gns3-gui gns3-server
+```
+
+#### Other Linux distributions
+
+GNS3 is often packaged for other distributions by third-parties:
+
+* [Gentoo](https://packages.gentoo.org/package/net-misc/gns3-server)
+* [Alpine](https://pkgs.alpinelinux.org/package/v3.10/community/x86_64/gns3-server)
+* [NixOS](https://search.nixos.org/packages?channel=21.11&from=0&size=50&sort=relevance&type=packages&query=gns3-server)
+
+#### PyPi
+
+You may use PyPi in case no package is provided, or you would like to do a manual installation:
+
+* https://pypi.org/project/gns3-server/
+* https://pypi.org/project/gns3-gui/
+
+```shell
+python3 -m pip install gns3-gui
+python3 -m pip install gns3-server
+```
+
+The downside of this method is you will have to manually install all dependencies (see below).
+
+Please see our [documentation](https://docs.gns3.com/docs/getting-started/installation/linux) for more details.
+
+### Software dependencies
+
+In addition to Python dependencies, other software may be required, recommended or optional.
+
+* [uBridge](https://github.com/GNS3/ubridge/) is required, it interconnects the nodes.
+* [Dynamips](https://github.com/GNS3/dynamips/) is required for running IOS routers (using real IOS images) as well as the internal switches and hubs.
+* [VPCS](https://github.com/GNS3/vpcs/) is recommended, it is a builtin node simulating a very simple computer to perform connectivity tests using ping, traceroute etc.
+* Qemu is strongly recommended as most node types are based on Qemu, for example Cisco IOSv and Arista vEOS.
+* libvirt is recommended as it's needed for the NAT cloud.
+* Docker is optional, some nodes are based on Docker.
+* mtools is recommended to support data transfer to/from QEMU VMs using virtual disks.
+* i386-libraries of libc and libcrypto are optional, they are only needed to run IOU based nodes.
+
+Note that Docker needs the script program (`bsdutils` or `util-linux` package), when running a Docker VM and a static busybox during installation (python3 setup.py install / pip3 install / package creation).
+
+## Development
+
+### Setting up
+
+These commands will install the server as well as all Python dependencies:
+
+```shell
+git clone https://github.com/GNS3/gns3-server
+cd gns3-server
+git checkout 3.0
+python3 -m venv venv-gns3server
+source venv-gns3server/bin/activate
+python3 setup.py install
+python3 -m gns3server --local
+```
+
+You will have to manually install other software dependencies (see above), for Dynamips, VPCS and uBridge the easiest is to install from our PPA.
+
+### Docker container
+
+Alternatively, you can run the GNS3 server in a container
+
+```shell
+bash scripts/docker_dev_server.sh
+```
+
+### Running tests
+
+First, install the development dependencies:
+
+```shell
+python3 -m pip install -r dev-requirements.txt
+```
+
+Then run the tests using pytest:
+
+```shell
+python3 -m pytest -vv tests/
+```
+
+### API documentation
+
+The API documentation can be accessed when running the server locally:
+
+* On `http://IP:PORT/docs` to see with Swagger UI (i.e. `http://localhost:3080/docs`)
+* On `http://IP:PORT/redoc` to see with ReDoc (i.e. `http://localhost:3080/redoc`)
+
+The documentation can also be viewed [online](http://apiv3.gns3.net) however it may not be the most up-to-date version since it needs manually synchronization with the current code. Also, you cannot use this to interact with a GNS3 server.
+
+### Branches
+
+#### master
+
+master is the next stable release, you can test it in your day-to -day activities.
+Bug fixes or small improvements pull requests go here.
+
+3.x development brand for the next major release.
+
+**Never** use this branch for production. Pull requests for major new features go here.
\ No newline at end of file
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 2f5ebb78..00000000
--- a/README.rst
+++ /dev/null
@@ -1,246 +0,0 @@
-GNS3-server
-===========
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
-
-.. image:: https://github.com/GNS3/gns3-server/workflows/testing/badge.svg?branch=3.0
- :target: https://github.com/GNS3/gns3-server/actions?query=workflow%3Atesting+branch%3A3.0
-
-.. image:: https://img.shields.io/pypi/v/gns3-server.svg
- :target: https://pypi.python.org/pypi/gns3-server
-
-.. image:: https://snyk.io/test/github/GNS3/gns3-server/badge.svg
- :target: https://snyk.io/test/github/GNS3/gns3-server
-
-This is the GNS3 server repository.
-
-The GNS3 server manages emulators such as Dynamips, VirtualBox or Qemu/KVM.
-Clients like the `GNS3 GUI `_ and the `GNS3 Web UI `_ control the server using a HTTP REST API.
-
-Software dependencies
----------------------
-
-In addition of Python dependencies listed in a section below, other software may be required, recommended or optional.
-
-* `uBridge `_ is required, it interconnects the nodes.
-* `Dynamips `_ is required for running IOS routers (using real IOS images) as well as the internal switches and hubs.
-* `VPCS `_ is recommended, it is a builtin node simulating a very simple computer to perform connectitivy tests using ping, traceroute etc.
-* Qemu is strongly recommended on Linux, as most node types are based on Qemu, for example Cisco IOSv and Arista vEOS.
-* libvirt is recommended (Linux only), as it's needed for the NAT cloud.
-* Docker is optional (Linux only), some nodes are based on Docker.
-* mtools is recommended to support data transfer to/from QEMU VMs using virtual disks.
-* i386-libraries of libc and libcrypto are optional (Linux only), they are only needed to run IOU based nodes.
-
-Docker support
-**************
-
-Docker support needs the script program (`bsdutils` or `util-linux` package), when running a docker VM and a static busybox during installation (python3 setup.py install / pip3 install / package creation).
-
-Branches
---------
-
-master
-******
-master is the next stable release, you can test it in your day to day activities.
-Bug fixes or small improvements pull requests go here.
-
-2.x (2.3 for example)
-*********************
-Next major release
-
-*Never* use this branch for production. Pull requests for major new features go here.
-
-Linux
------
-
-GNS3 is perhaps packaged for your distribution:
-
-* Gentoo: https://packages.gentoo.org/package/net-misc/gns3-server
-* Alpine: https://pkgs.alpinelinux.org/package/v3.10/community/x86_64/gns3-server
-* NixOS: https://search.nixos.org/packages?channel=21.11&from=0&size=50&sort=relevance&type=packages&query=gns3-server
-
-
-Linux (Debian based)
---------------------
-
-The following instructions have been tested with Ubuntu and Mint.
-You must be connected to the Internet in order to install the dependencies.
-
-Dependencies:
-
-- Python 3.6, setuptools and the ones listed `here `_
-
-The following commands will install some of these dependencies:
-
-.. code:: bash
-
- sudo apt-get install python3-setuptools
-
-Finally these commands will install the server as well as the rest of the dependencies:
-
-.. code:: bash
-
- cd gns3-server-master
- python3 -m venv venv-gns3server
- source venv-gns3server/bin/activate
- sudo python3 setup.py install
- python3 -m gns3server --local
-
-To run tests use:
-
-.. code:: bash
-
- python3 -m pytest tests
-
-
-Docker container
-****************
-
-For development you can run the GNS3 server in a container
-
-.. code:: bash
-
- bash scripts/docker_dev_server.sh
-
-
-Run as daemon (Unix only)
-**************************
-
-You will find init sample scripts for various systems
-inside the init directory.
-
-Usefull options:
-
-* --daemon: start process as a daemon
-* --log logfile: store output in a logfile
-* --pid pidfile: store the pid of the running process in a file and prevent double execution
-
-All init scripts require the creation of a GNS3 user. You can change it to another user.
-
-.. code:: bash
-
- sudo adduser gns3
-
-upstart
--------
-
-For ubuntu < 15.04
-
-You need to copy init/gns3.conf.upstart to /etc/init/gns3.conf
-
-.. code:: bash
-
- sudo chown root /etc/init/gns3.conf
- sudo service gns3 start
-
-
-systemd
--------
-
-You need to copy init/gns3.service.systemd to /lib/systemd/system/gns3.service
-
-.. code:: bash
-
- sudo chown root /lib/systemd/system/gns3.service
- sudo systemctl start gns3
-
-Windows
--------
-
-
-Please use our `all-in-one installer `_ to install the stable build.
-
-If you install via source you need to first install:
-
-- Python (3.3 or above) - https://www.python.org/downloads/windows/
-- Pywin32 - https://sourceforge.net/projects/pywin32/
-
-Then you can call
-
-.. code:: bash
-
- python setup.py install
-
-to install the remaining dependencies.
-
-To run the tests, you also need to call
-
-.. code:: bash
-
- pip install pytest pytest-capturelog
-
-before actually running the tests with
-
-.. code:: bash
-
- python setup.py test
-
-or with
-
-.. code:: bash
-
- py.test -v
-
-Mac OS X
---------
-
-Please use our DMG package for a simple installation.
-
-If you want to test the current git version or contribute to the project,
-you can follow these instructions with virtualenwrapper: http://virtualenvwrapper.readthedocs.org/
-and homebrew: http://brew.sh/.
-
-.. code:: bash
-
- brew install python3
- mkvirtualenv gns3-server --python=/usr/local/bin/python3.5
- python3 setup.py install
- gns3server
-
-SSL
----
-
-If you want enable SSL support on GNS3 you can generate a self signed certificate:
-
-.. code:: bash
-
- bash gns3server/cert_utils/create_cert.sh
-
-This command will put the files in ~/.config/GNS3/ssl
-
-After you can start the server in SSL mode with:
-
-.. code:: bash
-
- python gns3server/main.py --certfile ~/.config/GNS3/ssl/server.cert --certkey ~/.config/GNS3/ssl/server.key --ssl
-
-
-Or in your gns3_server.conf by adding in the Server section:
-
-.. code:: ini
-
- [Server]
- certfile=/Users/noplay/.config/GNS3/ssl/server.cert
- certkey=/Users/noplay/.config/GNS3/ssl/server.key
- ssl=True
-
-Running tests
-*************
-
-Just run:
-
-.. code:: bash
-
- py.test -vv
-
-If you want test coverage:
-
-.. code:: bash
-
- py.test --cov-report term-missing --cov=gns3server
-
-Security issues
-----------------
-Please contact us using contact form available here:
-http://docs.gns3.com/1ON9JBXSeR7Nt2-Qum2o3ZX0GU86BZwlmNSUgvmqNWGY/index.html
diff --git a/dev-requirements.txt b/dev-requirements.txt
index fda609f0..f02ff2a2 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,6 @@
-r requirements.txt
-pytest==6.2.5
+pytest==7.0.0
flake8==4.0.1
pytest-timeout==2.0.1
pytest-asyncio==0.16.0
diff --git a/gns3server/api/routes/compute/dynamips_nodes.py b/gns3server/api/routes/compute/dynamips_nodes.py
index 981dd282..96f3d67a 100644
--- a/gns3server/api/routes/compute/dynamips_nodes.py
+++ b/gns3server/api/routes/compute/dynamips_nodes.py
@@ -230,16 +230,6 @@ async def start_capture(
"""
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
-
- if sys.platform.startswith("win"):
- # FIXME: Dynamips (Cygwin actually) doesn't like non ascii paths on Windows
- try:
- pcap_file_path.encode("ascii")
- except UnicodeEncodeError:
- raise DynamipsError(
- f"The capture file path '{pcap_file_path}' must only contain ASCII (English) characters"
- )
-
await node.start_capture(adapter_number, port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
diff --git a/gns3server/api/routes/controller/dependencies/database.py b/gns3server/api/routes/controller/dependencies/database.py
index fe2226c7..2e859106 100644
--- a/gns3server/api/routes/controller/dependencies/database.py
+++ b/gns3server/api/routes/controller/dependencies/database.py
@@ -24,11 +24,11 @@ from gns3server.db.repositories.base import BaseRepository
async def get_db_session(request: HTTPConnection) -> AsyncSession:
- session = AsyncSession(request.app.state._db_engine, expire_on_commit=False)
- try:
- yield session
- finally:
- await session.close()
+ async with AsyncSession(request.app.state._db_engine, expire_on_commit=False) as session:
+ try:
+ yield session
+ finally:
+ await session.close()
def get_repository(repo: Type[BaseRepository]) -> Callable:
diff --git a/gns3server/api/routes/controller/images.py b/gns3server/api/routes/controller/images.py
index ccd15b76..0ad05921 100644
--- a/gns3server/api/routes/controller/images.py
+++ b/gns3server/api/routes/controller/images.py
@@ -23,11 +23,13 @@ import logging
import urllib.parse
from fastapi import APIRouter, Request, Response, Depends, status
+from starlette.requests import ClientDisconnect
from sqlalchemy.orm.exc import MultipleResultsFound
from typing import List, Optional
from gns3server import schemas
-from gns3server.utils.images import InvalidImageError, default_images_directory, write_image
+from gns3server.config import Config
+from gns3server.utils.images import InvalidImageError, write_image
from gns3server.db.repositories.images import ImagesRepository
from gns3server.db.repositories.templates import TemplatesRepository
from gns3server.db.repositories.rbac import RbacRepository
@@ -62,7 +64,6 @@ async def get_images(
async def upload_image(
image_path: str,
request: Request,
- image_type: schemas.ImageType = schemas.ImageType.qemu,
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)),
current_user: schemas.User = Depends(get_current_active_user),
@@ -72,24 +73,26 @@ async def upload_image(
"""
Upload an image.
- Example: curl -X POST http://host:port/v3/images/upload/my_image_name.qcow2?image_type=qemu \
+ Example: curl -X POST http://host:port/v3/images/upload/my_image_name.qcow2 \
-H 'Authorization: Bearer ' --data-binary @"/path/to/image.qcow2"
"""
image_path = urllib.parse.unquote(image_path)
image_dir, image_name = os.path.split(image_path)
- directory = default_images_directory(image_type)
- full_path = os.path.abspath(os.path.join(directory, image_dir, image_name))
- if os.path.commonprefix([directory, full_path]) != directory:
+ # check if the path is within the default images directory
+ base_images_directory = os.path.expanduser(Config.instance().settings.Server.images_path)
+ full_path = os.path.abspath(os.path.join(base_images_directory, image_dir, image_name))
+ if os.path.commonprefix([base_images_directory, full_path]) != base_images_directory:
raise ControllerForbiddenError(f"Cannot write image, '{image_path}' is forbidden")
+ print(image_path)
if await images_repo.get_image(image_path):
raise ControllerBadRequestError(f"Image '{image_path}' already exists")
try:
- image = await write_image(image_name, image_type, full_path, request.stream(), images_repo)
- except (OSError, InvalidImageError) as e:
- raise ControllerError(f"Could not save {image_type} image '{image_path}': {e}")
+ image = await write_image(image_path, full_path, request.stream(), images_repo)
+ except (OSError, InvalidImageError, ClientDisconnect) as e:
+ raise ControllerError(f"Could not save image '{image_path}': {e}")
if install_appliances:
# attempt to automatically create templates based on image checksum
@@ -100,7 +103,7 @@ async def upload_image(
templates_repo,
rbac_repo,
current_user,
- directory
+ os.path.dirname(image.path)
)
return image
diff --git a/gns3server/compute/__init__.py b/gns3server/compute/__init__.py
index afd48650..30cf3c49 100644
--- a/gns3server/compute/__init__.py
+++ b/gns3server/compute/__init__.py
@@ -32,10 +32,7 @@ if (
or os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1"
):
# IOU & Docker only runs on Linux but test suite works on UNIX platform
- if not sys.platform.startswith("win"):
- from .docker import Docker
-
- MODULES.append(Docker)
- from .iou import IOU
-
- MODULES.append(IOU)
+ from .docker import Docker
+ from .iou import IOU
+ MODULES.append(Docker)
+ MODULES.append(IOU)
diff --git a/gns3server/compute/base_manager.py b/gns3server/compute/base_manager.py
index 1d8c78e7..5833a811 100644
--- a/gns3server/compute/base_manager.py
+++ b/gns3server/compute/base_manager.py
@@ -301,10 +301,6 @@ class BaseManager:
:returns: True or False
"""
- if sys.platform.startswith("win"):
- # do not check anything on Windows
- return True
-
if sys.platform.startswith("darwin"):
if os.stat(executable).st_uid == 0:
return True
@@ -425,11 +421,10 @@ class BaseManager:
valid_directory_prefices.append(extra_dir)
# Windows path should not be send to a unix server
- if not sys.platform.startswith("win"):
- if re.match(r"^[A-Z]:", path) is not None:
- raise NodeError(
- f"'{path}' is not allowed on this remote server. Please only use a file from '{img_directory}'"
- )
+ if re.match(r"^[A-Z]:", path) is not None:
+ raise NodeError(
+ f"'{path}' is not allowed on this remote server. Please only use a file from '{img_directory}'"
+ )
if not os.path.isabs(orig_path):
diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py
index cfa147a8..fcc31893 100644
--- a/gns3server/compute/base_node.py
+++ b/gns3server/compute/base_node.py
@@ -892,34 +892,6 @@ class BaseNode:
await self._ubridge_send(
'bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface)
)
- elif sys.platform.startswith("win"):
- # on Windows we use Winpcap/Npcap
- windows_interfaces = interfaces()
- npf_id = None
- source_mac = None
- for interface in windows_interfaces:
- # Winpcap/Npcap uses a NPF ID to identify an interface on Windows
- if "netcard" in interface and ethernet_interface in interface["netcard"]:
- npf_id = interface["id"]
- source_mac = interface["mac_address"]
- elif ethernet_interface in interface["name"]:
- npf_id = interface["id"]
- source_mac = interface["mac_address"]
- if npf_id:
- await self._ubridge_send(
- 'bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=npf_id)
- )
- else:
- raise NodeError(f"Could not find NPF id for interface {ethernet_interface}")
-
- if block_host_traffic:
- if source_mac:
- await self._ubridge_send(
- 'bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac)
- )
- log.info(f"PCAP filter applied on '{ethernet_interface}' for source MAC {source_mac}")
- else:
- log.warning(f"Could not block host network traffic on {ethernet_interface} (no MAC address found)")
else:
# on other platforms we just rely on the pcap library
await self._ubridge_send(
diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py
index 098ffcff..43d3bcc1 100644
--- a/gns3server/compute/builtin/nodes/cloud.py
+++ b/gns3server/compute/builtin/nodes/cloud.py
@@ -310,31 +310,27 @@ class Cloud(BaseNode):
"uBridge requires root access or the capability to interact with Ethernet and TAP adapters"
)
- if sys.platform.startswith("win"):
- await self._add_ubridge_ethernet_connection(bridge_name, port_info["interface"])
-
- else:
- if port_info["type"] == "ethernet":
- network_interfaces = [interface["name"] for interface in self._interfaces()]
- if not port_info["interface"] in network_interfaces:
- raise NodeError(
- f"Interface '{port_info['interface']}' could not be found on this system, please update '{self.name}'"
- )
-
- if sys.platform.startswith("linux"):
- await self._add_linux_ethernet(port_info, bridge_name)
- elif sys.platform.startswith("darwin"):
- await self._add_osx_ethernet(port_info, bridge_name)
- else:
- await self._add_windows_ethernet(port_info, bridge_name)
-
- elif port_info["type"] == "tap":
- await self._ubridge_send(
- 'bridge add_nio_tap {name} "{interface}"'.format(
- name=bridge_name, interface=port_info["interface"]
- )
+ if port_info["type"] == "ethernet":
+ network_interfaces = [interface["name"] for interface in self._interfaces()]
+ if not port_info["interface"] in network_interfaces:
+ raise NodeError(
+ f"Interface '{port_info['interface']}' could not be found on this system, please update '{self.name}'"
)
+ if sys.platform.startswith("linux"):
+ await self._add_linux_ethernet(port_info, bridge_name)
+ elif sys.platform.startswith("darwin"):
+ await self._add_osx_ethernet(port_info, bridge_name)
+ else:
+ await self._add_windows_ethernet(port_info, bridge_name)
+
+ elif port_info["type"] == "tap":
+ await self._ubridge_send(
+ 'bridge add_nio_tap {name} "{interface}"'.format(
+ name=bridge_name, interface=port_info["interface"]
+ )
+ )
+
elif port_info["type"] == "udp":
await self._ubridge_send(
"bridge add_nio_udp {name} {lport} {rhost} {rport}".format(
diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py
index b5cafd3c..e48d8563 100644
--- a/gns3server/compute/docker/docker_vm.py
+++ b/gns3server/compute/docker/docker_vm.py
@@ -446,6 +446,12 @@ class DockerVM(BaseNode):
continue
if not e.startswith("GNS3_"):
formatted = self._format_env(variables, e)
+ vm_name = self._name.replace(",", ",,")
+ project_path = self.project.path.replace(",", ",,")
+ formatted = formatted.replace("%vm-name%", '"' + vm_name.replace('"', '\\"') + '"')
+ formatted = formatted.replace("%vm-id%", self._id)
+ formatted = formatted.replace("%project-id%", self.project.id)
+ formatted = formatted.replace("%project-path%", '"' + project_path.replace('"', '\\"') + '"')
params["Env"].append(formatted)
if self._console_type == "vnc":
diff --git a/gns3server/compute/dynamips/__init__.py b/gns3server/compute/dynamips/__init__.py
index b9670b62..f4b56d05 100644
--- a/gns3server/compute/dynamips/__init__.py
+++ b/gns3server/compute/dynamips/__init__.py
@@ -348,23 +348,10 @@ class Dynamips(BaseManager):
nio.suspend = nio_settings.get("suspend", False)
elif nio_settings["type"] == "nio_generic_ethernet":
ethernet_device = nio_settings["ethernet_device"]
- if sys.platform.startswith("win"):
- # replace the interface name by the GUID on Windows
- windows_interfaces = interfaces()
- npf_interface = None
- for interface in windows_interfaces:
- if interface["name"] == ethernet_device:
- npf_interface = interface["id"]
- if not npf_interface:
- raise DynamipsError(f"Could not find interface {ethernet_device} on this host")
- else:
- ethernet_device = npf_interface
if not is_interface_up(ethernet_device):
raise DynamipsError(f"Ethernet interface {ethernet_device} is down")
nio = NIOGenericEthernet(node.hypervisor, ethernet_device)
elif nio_settings["type"] == "nio_linux_ethernet":
- if sys.platform.startswith("win"):
- raise DynamipsError("This NIO type is not supported on Windows")
ethernet_device = nio_settings["ethernet_device"]
nio = NIOLinuxEthernet(node.hypervisor, ethernet_device)
elif nio_settings["type"] == "nio_tap":
@@ -564,7 +551,6 @@ class Dynamips(BaseManager):
await vm.set_idlepc("0x0")
was_auto_started = False
- old_priority = None
try:
status = await vm.get_status()
if status != "running":
@@ -576,8 +562,6 @@ class Dynamips(BaseManager):
if not idlepcs:
raise DynamipsError("No Idle-PC values found")
- if sys.platform.startswith("win"):
- old_priority = vm.set_process_priority_windows(vm.hypervisor.process.pid)
for idlepc in idlepcs:
match = re.search(r"^0x[0-9a-f]{8}$", idlepc.split()[0])
if not match:
@@ -606,8 +590,6 @@ class Dynamips(BaseManager):
except DynamipsError:
raise
finally:
- if old_priority is not None:
- vm.set_process_priority_windows(vm.hypervisor.process.pid, old_priority)
if was_auto_started:
await vm.stop()
return validated_idlepc
diff --git a/gns3server/compute/dynamips/hypervisor.py b/gns3server/compute/dynamips/hypervisor.py
index 9a1aa041..2d4999f6 100644
--- a/gns3server/compute/dynamips/hypervisor.py
+++ b/gns3server/compute/dynamips/hypervisor.py
@@ -118,11 +118,6 @@ class Hypervisor(DynamipsHypervisor):
self._command = self._build_command()
env = os.environ.copy()
- if sys.platform.startswith("win"):
- # add the Npcap directory to $PATH to force Dynamips to use npcap DLL instead of Winpcap (if installed)
- system_root = os.path.join(os.path.expandvars("%SystemRoot%"), "System32", "Npcap")
- if os.path.isdir(system_root):
- env["PATH"] = system_root + ";" + env["PATH"]
try:
log.info(f"Starting Dynamips: {self._command}")
self._stdout_file = os.path.join(self.working_dir, f"dynamips_i{self._id}_stdout.txt")
diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py
index a2bbe235..f75b5160 100644
--- a/gns3server/compute/dynamips/nodes/router.py
+++ b/gns3server/compute/dynamips/nodes/router.py
@@ -103,10 +103,7 @@ class Router(BaseNode):
self._idlesleep = 30
self._ghost_file = ""
self._ghost_status = 0
- if sys.platform.startswith("win"):
- self._exec_area = 16 # 16 MB by default on Windows (Cygwin)
- else:
- self._exec_area = 64 # 64 MB on other systems
+ self._exec_area = 64
self._disk0 = 0 # Megabytes
self._disk1 = 0 # Megabytes
self._auto_delete_disks = False
@@ -711,29 +708,6 @@ class Router(BaseNode):
log.info(f'Router "{self._name}" [{self._id}]: idle-PC set to {idlepc}')
self._idlepc = idlepc
- def set_process_priority_windows(self, pid, priority=None):
- """
- Sets process priority on Windows
-
- :param pid: process PID
- """
-
- import win32api
- import win32process
- import win32con
- import pywintypes
-
- old_priority = None
- try:
- handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
- old_priority = win32process.GetPriorityClass(handle)
- if priority is None:
- priority = win32process.BELOW_NORMAL_PRIORITY_CLASS
- win32process.SetPriorityClass(handle, priority)
- except pywintypes.error as e:
- log.error(f"Cannot set priority for Dynamips process (PID={pid}) ")
- return old_priority
-
async def get_idle_pc_prop(self):
"""
Gets the idle PC proposals.
@@ -751,13 +725,8 @@ class Router(BaseNode):
await asyncio.sleep(20) # leave time to the router to boot
log.info(f'Router "{self._name}" [{self._id}] has started calculating Idle-PC values')
- old_priority = None
- if sys.platform.startswith("win"):
- old_priority = self.set_process_priority_windows(self._hypervisor.process.pid)
begin = time.time()
idlepcs = await self._hypervisor.send(f'vm get_idle_pc_prop "{self._name}" 0')
- if old_priority is not None:
- self.set_process_priority_windows(self._hypervisor.process.pid, old_priority)
log.info(
'Router "{name}" [{id}] has finished calculating Idle-PC values after {time:.4f} seconds'.format(
name=self._name, id=self._id, time=time.time() - begin
diff --git a/gns3server/compute/qemu/__init__.py b/gns3server/compute/qemu/__init__.py
index 2a0c48c3..400f5222 100644
--- a/gns3server/compute/qemu/__init__.py
+++ b/gns3server/compute/qemu/__init__.py
@@ -114,29 +114,15 @@ class Qemu(BaseManager):
else:
log.warning("The PATH environment variable doesn't exist")
# look for Qemu binaries in the current working directory and $PATH
- if sys.platform.startswith("win"):
- # add specific Windows paths
- if hasattr(sys, "frozen"):
- # add any qemu dir in the same location as gns3server.exe to the list of paths
+ if sys.platform.startswith("darwin") and hasattr(sys, "frozen"):
+ # add specific locations on Mac OS X regardless of what's in $PATH
+ paths.update(["/usr/bin", "/usr/local/bin", "/opt/local/bin"])
+ try:
exec_dir = os.path.dirname(os.path.abspath(sys.executable))
- for f in os.listdir(exec_dir):
- if f.lower().startswith("qemu"):
- paths.add(os.path.join(exec_dir, f))
-
- if "PROGRAMFILES(X86)" in os.environ and os.path.exists(os.environ["PROGRAMFILES(X86)"]):
- paths.add(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu"))
- if "PROGRAMFILES" in os.environ and os.path.exists(os.environ["PROGRAMFILES"]):
- paths.add(os.path.join(os.environ["PROGRAMFILES"], "qemu"))
- elif sys.platform.startswith("darwin"):
- if hasattr(sys, "frozen"):
- # add specific locations on Mac OS X regardless of what's in $PATH
- paths.update(["/usr/bin", "/usr/local/bin", "/opt/local/bin"])
- try:
- exec_dir = os.path.dirname(os.path.abspath(sys.executable))
- paths.add(os.path.abspath(os.path.join(exec_dir, "qemu/bin")))
- # If the user run the server by hand from outside
- except FileNotFoundError:
- paths.add("/Applications/GNS3.app/Contents/MacOS/qemu/bin")
+ paths.add(os.path.abspath(os.path.join(exec_dir, "qemu/bin")))
+ # If the user run the server by hand from outside
+ except FileNotFoundError:
+ paths.add("/Applications/GNS3.app/Contents/MacOS/qemu/bin")
return paths
@staticmethod
@@ -205,31 +191,16 @@ class Qemu(BaseManager):
:param qemu_path: path to Qemu executable.
"""
- if sys.platform.startswith("win"):
- # Qemu on Windows doesn't return anything with parameter -version
- # look for a version number in version.txt file in the same directory instead
- version_file = os.path.join(os.path.dirname(qemu_path), "version.txt")
- if os.path.isfile(version_file):
- try:
- with open(version_file, "rb") as file:
- version = file.read().decode("utf-8").strip()
- match = re.search(r"[0-9\.]+", version)
- if match:
- return version
- except (UnicodeDecodeError, OSError) as e:
- log.warning(f"could not read {version_file}: {e}")
- return ""
- else:
- try:
- output = await subprocess_check_output(qemu_path, "-version", "-nographic")
- match = re.search(r"version\s+([0-9a-z\-\.]+)", output)
- if match:
- version = match.group(1)
- return version
- else:
- raise QemuError(f"Could not determine the Qemu version for {qemu_path}")
- except (OSError, subprocess.SubprocessError) as e:
- raise QemuError(f"Error while looking for the Qemu version: {e}")
+ try:
+ output = await subprocess_check_output(qemu_path, "-version", "-nographic")
+ match = re.search(r"version\s+([0-9a-z\-\.]+)", output)
+ if match:
+ version = match.group(1)
+ return version
+ else:
+ raise QemuError(f"Could not determine the Qemu version for {qemu_path}")
+ except (OSError, subprocess.SubprocessError) as e:
+ raise QemuError(f"Error while looking for the Qemu version: {e}")
@staticmethod
async def _get_qemu_img_version(qemu_img_path):
@@ -250,38 +221,6 @@ class Qemu(BaseManager):
except (OSError, subprocess.SubprocessError) as e:
raise QemuError(f"Error while looking for the Qemu-img version: {e}")
- @staticmethod
- def get_haxm_windows_version():
- """
- Gets the HAXM version number (Windows).
-
- :returns: HAXM version number. Returns None if HAXM is not installed.
- """
-
- assert sys.platform.startswith("win")
- import winreg
-
- hkey = winreg.OpenKey(
- winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products"
- )
- version = None
- for index in range(winreg.QueryInfoKey(hkey)[0]):
- product_id = winreg.EnumKey(hkey, index)
- try:
- product_key = winreg.OpenKey(hkey, fr"{product_id}\InstallProperties")
- try:
- if winreg.QueryValueEx(product_key, "DisplayName")[0].endswith(
- "Hardware Accelerated Execution Manager"
- ):
- version = winreg.QueryValueEx(product_key, "DisplayVersion")[0]
- break
- finally:
- winreg.CloseKey(product_key)
- except OSError:
- continue
- winreg.CloseKey(hkey)
- return version
-
@staticmethod
def get_legacy_vm_workdir(legacy_vm_id, name):
"""
diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py
index adec97db..e25f1467 100644
--- a/gns3server/compute/qemu/qemu_vm.py
+++ b/gns3server/compute/qemu/qemu_vm.py
@@ -32,8 +32,9 @@ import gns3server
import subprocess
import time
import json
+import shlex
-from gns3server.utils import parse_version, shlex_quote
+from gns3server.utils import parse_version
from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor
from .qemu_error import QemuError
from .utils.qcow2 import Qcow2, Qcow2Error
@@ -220,8 +221,6 @@ class QemuVM(BaseNode):
"""
if qemu_path and os.pathsep not in qemu_path:
- if sys.platform.startswith("win") and ".exe" not in qemu_path.lower():
- qemu_path += "w.exe"
new_qemu_path = shutil.which(qemu_path, path=os.pathsep.join(self._manager.paths_list()))
if new_qemu_path is None:
raise QemuError(f"QEMU binary path {qemu_path} is not found in the path")
@@ -271,10 +270,7 @@ class QemuVM(BaseNode):
def platform(self, platform):
self._platform = platform
- if sys.platform.startswith("win"):
- self.qemu_path = f"qemu-system-{platform}w.exe"
- else:
- self.qemu_path = f"qemu-system-{platform}"
+ self.qemu_path = f"qemu-system-{platform}"
def _disk_setter(self, variable, value):
"""
@@ -289,7 +285,7 @@ class QemuVM(BaseNode):
for node in self.manager.nodes:
if node != self and getattr(node, variable) == value:
raise QemuError(
- f"Sorry a node without the linked base setting enabled can only be used once on your server. {value} is already used by {node.name}"
+ f"Sorry a node without the linked base setting enabled can only be used once on your server. {value} is already used by {node.name} in project {node.project.name}"
)
setattr(self, "_" + variable, value)
log.info(
@@ -901,8 +897,8 @@ class QemuVM(BaseNode):
options = options.replace("-enable-kvm", "-machine accel=kvm")
if "-enable-hax" in options:
- if not sys.platform.startswith("win"):
- # HAXM is only available on Windows
+ if not sys.platform.startswith("darwin"):
+ # HAXM is only available on macOS
options = options.replace("-enable-hax", "")
else:
options = options.replace("-enable-hax", "-machine accel=hax")
@@ -1002,52 +998,25 @@ class QemuVM(BaseNode):
if self._process_priority == "normal":
return
- if sys.platform.startswith("win"):
- try:
- import win32api
- import win32con
- import win32process
- except ImportError:
- log.error(f"pywin32 must be installed to change the priority class for QEMU VM {self._name}")
- else:
- log.info(f"Setting QEMU VM {self._name} priority class to {self._process_priority}")
- handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, self._process.pid)
- if self._process_priority == "realtime":
- priority = win32process.REALTIME_PRIORITY_CLASS
- elif self._process_priority == "very high":
- priority = win32process.HIGH_PRIORITY_CLASS
- elif self._process_priority == "high":
- priority = win32process.ABOVE_NORMAL_PRIORITY_CLASS
- elif self._process_priority == "low":
- priority = win32process.BELOW_NORMAL_PRIORITY_CLASS
- elif self._process_priority == "very low":
- priority = win32process.IDLE_PRIORITY_CLASS
- else:
- priority = win32process.NORMAL_PRIORITY_CLASS
- try:
- win32process.SetPriorityClass(handle, priority)
- except win32process.error as e:
- log.error(f'Could not change process priority for QEMU VM "{self._name}": {e}')
+ if self._process_priority == "realtime":
+ priority = -20
+ elif self._process_priority == "very high":
+ priority = -15
+ elif self._process_priority == "high":
+ priority = -5
+ elif self._process_priority == "low":
+ priority = 5
+ elif self._process_priority == "very low":
+ priority = 19
else:
- if self._process_priority == "realtime":
- priority = -20
- elif self._process_priority == "very high":
- priority = -15
- elif self._process_priority == "high":
- priority = -5
- elif self._process_priority == "low":
- priority = 5
- elif self._process_priority == "very low":
- priority = 19
- else:
- priority = 0
- try:
- process = await asyncio.create_subprocess_exec(
- "renice", "-n", str(priority), "-p", str(self._process.pid)
- )
- await process.wait()
- except (OSError, subprocess.SubprocessError) as e:
- log.error(f'Could not change process priority for QEMU VM "{self._name}": {e}')
+ priority = 0
+ try:
+ process = await asyncio.create_subprocess_exec(
+ "renice", "-n", str(priority), "-p", str(self._process.pid)
+ )
+ await process.wait()
+ except (OSError, subprocess.SubprocessError) as e:
+ log.error(f'Could not change process priority for QEMU VM "{self._name}": {e}')
def _stop_cpulimit(self):
"""
@@ -1070,14 +1039,8 @@ class QemuVM(BaseNode):
return
try:
- if sys.platform.startswith("win") and hasattr(sys, "frozen"):
- cpulimit_exec = os.path.join(
- os.path.dirname(os.path.abspath(sys.executable)), "cpulimit", "cpulimit.exe"
- )
- else:
- cpulimit_exec = "cpulimit"
subprocess.Popen(
- [cpulimit_exec, "--lazy", f"--pid={self._process.pid}", f"--limit={self._cpu_throttling}"],
+ ["cpulimit", "--lazy", f"--pid={self._process.pid}", f"--limit={self._cpu_throttling}"],
cwd=self.working_dir,
)
log.info(f"CPU throttled to {self._cpu_throttling}%")
@@ -1133,7 +1096,7 @@ class QemuVM(BaseNode):
self.check_available_ram(self.ram)
command = await self._build_command()
- command_string = " ".join(shlex_quote(s) for s in command)
+ command_string = " ".join(shlex.quote(s) for s in command)
try:
log.info(f"Starting QEMU with: {command_string}")
self._stdout_file = os.path.join(self.working_dir, "qemu.log")
@@ -1193,8 +1156,7 @@ class QemuVM(BaseNode):
if self.started:
log.info("QEMU process has stopped, return code: %d", returncode)
await self.stop()
- # A return code of 1 seem fine on Windows
- if returncode != 0 and (not sys.platform.startswith("win") or returncode != 1):
+ if returncode != 0:
self.project.emit(
"log.error",
{"message": f"QEMU process has stopped, return code: {returncode}\n{self.read_stdout()}"},
@@ -1822,7 +1784,7 @@ class QemuVM(BaseNode):
self._qemu_img_stdout_file = os.path.join(self.working_dir, "qemu-img.log")
log.info(f"logging to {self._qemu_img_stdout_file}")
- command_string = " ".join(shlex_quote(s) for s in command)
+ command_string = " ".join(shlex.quote(s) for s in command)
log.info(f"Executing qemu-img with: {command_string}")
with open(self._qemu_img_stdout_file, "w", encoding="utf-8") as fd:
process = await asyncio.create_subprocess_exec(
@@ -2272,15 +2234,7 @@ class QemuVM(BaseNode):
require_hardware_accel = self.manager.config.settings.Qemu.require_hardware_acceleration
if enable_hardware_accel and "-machine accel=tcg" not in options:
# Turn OFF hardware acceleration for non x86 architectures
- if sys.platform.startswith("win"):
- supported_binaries = [
- "qemu-system-x86_64.exe",
- "qemu-system-x86_64w.exe",
- "qemu-system-i386.exe",
- "qemu-system-i386w.exe",
- ]
- else:
- supported_binaries = ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"]
+ supported_binaries = ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"]
if os.path.basename(qemu_path) not in supported_binaries:
if require_hardware_accel:
raise QemuError(
@@ -2298,29 +2252,6 @@ class QemuVM(BaseNode):
)
else:
return False
- elif sys.platform.startswith("win"):
- if require_hardware_accel:
- # HAXM is only available starting with Qemu version 2.9.0
- version = await self.manager.get_qemu_version(self.qemu_path)
- if version and parse_version(version) < parse_version("2.9.0"):
- raise QemuError(
- f"HAXM acceleration can only be enable for Qemu version 2.9.0 and above (current version: {version})"
- )
-
- # check if HAXM is installed
- version = self.manager.get_haxm_windows_version()
- if version is None:
- raise QemuError("HAXM acceleration support is not installed on this host")
- log.info(f"HAXM support version {version} detected")
-
- # check if the HAXM service is running
- from gns3server.utils.windows_service import check_windows_service_is_running
-
- if not check_windows_service_is_running("intelhaxm"):
- raise QemuError("Intel HAXM service is not running on this host")
-
- else:
- return False
elif sys.platform.startswith("darwin"):
process = await asyncio.create_subprocess_shell("kextstat | grep com.intel.kext.intelhaxm")
await process.wait()
@@ -2440,7 +2371,7 @@ class QemuVM(BaseNode):
# https://github.com/GNS3/gns3-server/issues/685
if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64":
command.extend(["-machine", "smm=off"])
- elif sys.platform.startswith("win") or sys.platform.startswith("darwin"):
+ elif sys.platform.startswith("darwin"):
command.extend(["-enable-hax"])
command.extend(["-boot", f"order={self._boot_priority}"])
command.extend(self._bios_option())
diff --git a/gns3server/compute/ubridge/hypervisor.py b/gns3server/compute/ubridge/hypervisor.py
index 832f0af6..a702adb3 100644
--- a/gns3server/compute/ubridge/hypervisor.py
+++ b/gns3server/compute/ubridge/hypervisor.py
@@ -27,7 +27,6 @@ import re
from gns3server.utils import parse_version
from gns3server.utils.asyncio import wait_for_process_termination
-from gns3server.utils.asyncio import monitor_process
from gns3server.utils.asyncio import subprocess_check_output
from .ubridge_hypervisor import UBridgeHypervisor
from .ubridge_error import UbridgeError
@@ -139,7 +138,7 @@ class Hypervisor(UBridgeHypervisor):
match = re.search(r"ubridge version ([0-9a-z\.]+)", output)
if match:
self._version = match.group(1)
- if sys.platform.startswith("win") or sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
minimum_required_version = "0.9.12"
else:
# uBridge version 0.9.14 is required for packet filters
@@ -158,11 +157,6 @@ class Hypervisor(UBridgeHypervisor):
"""
env = os.environ.copy()
- if sys.platform.startswith("win"):
- # add the Npcap directory to $PATH to force uBridge to use npcap DLL instead of Winpcap (if installed)
- system_root = os.path.join(os.path.expandvars("%SystemRoot%"), "System32", "Npcap")
- if os.path.isdir(system_root):
- env["PATH"] = system_root + ";" + env["PATH"]
await self._check_ubridge_version(env)
try:
command = self._build_command()
diff --git a/gns3server/compute/virtualbox/__init__.py b/gns3server/compute/virtualbox/__init__.py
index 561a9051..628a24c2 100644
--- a/gns3server/compute/virtualbox/__init__.py
+++ b/gns3server/compute/virtualbox/__init__.py
@@ -62,16 +62,7 @@ class VirtualBox(BaseManager):
vboxmanage_path = shutil.which(vboxmanage_path)
else:
log.info("A path to VBoxManage has not been configured, trying to find it...")
- if sys.platform.startswith("win"):
- if "VBOX_INSTALL_PATH" in os.environ:
- vboxmanage_path_windows = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
- if os.path.exists(vboxmanage_path_windows):
- vboxmanage_path = vboxmanage_path_windows
- elif "VBOX_MSI_INSTALL_PATH" in os.environ:
- vboxmanage_path_windows = os.path.join(os.environ["VBOX_MSI_INSTALL_PATH"], "VBoxManage.exe")
- if os.path.exists(vboxmanage_path_windows):
- vboxmanage_path = vboxmanage_path_windows
- elif sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
vboxmanage_path_osx = "/Applications/VirtualBox.app/Contents/MacOS/VBoxManage"
if os.path.exists(vboxmanage_path_osx):
vboxmanage_path = vboxmanage_path_osx
diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py
index 88719dd4..a9e156b6 100644
--- a/gns3server/compute/virtualbox/virtualbox_vm.py
+++ b/gns3server/compute/virtualbox/virtualbox_vm.py
@@ -38,10 +38,6 @@ from gns3server.compute.nios.nio_udp import NIOUDP
from gns3server.compute.adapters.ethernet_adapter import EthernetAdapter
from gns3server.compute.base_node import BaseNode
-if sys.platform.startswith("win"):
- import msvcrt
- import win32file
-
import logging
log = logging.getLogger(__name__)
@@ -839,14 +835,11 @@ class VirtualBoxVM(BaseNode):
:returns: pipe path (string)
"""
- if sys.platform.startswith("win"):
- pipe_name = fr"\\.\pipe\gns3_vbox\{self.id}"
- else:
- pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vbox", f"{self.id}")
- try:
- os.makedirs(os.path.dirname(pipe_name), exist_ok=True)
- except OSError as e:
- raise VirtualBoxError(f"Could not create the VirtualBox pipe directory: {e}")
+ pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vbox", f"{self.id}")
+ try:
+ os.makedirs(os.path.dirname(pipe_name), exist_ok=True)
+ except OSError as e:
+ raise VirtualBoxError(f"Could not create the VirtualBox pipe directory: {e}")
return pipe_name
async def _set_serial_console(self):
diff --git a/gns3server/compute/vmware/__init__.py b/gns3server/compute/vmware/__init__.py
index fb439798..f0f12d33 100644
--- a/gns3server/compute/vmware/__init__.py
+++ b/gns3server/compute/vmware/__init__.py
@@ -27,11 +27,12 @@ import subprocess
import logging
import codecs
import ipaddress
+import shlex
from collections import OrderedDict
from gns3server.utils.interfaces import interfaces
from gns3server.utils.asyncio import subprocess_check_output
-from gns3server.utils import parse_version, shlex_quote
+from gns3server.utils import parse_version
log = logging.getLogger(__name__)
@@ -53,10 +54,7 @@ class VMware(BaseManager):
self._vmnets = []
self._vmnets_info = {}
self._vmnet_start_range = 2
- if sys.platform.startswith("win"):
- self._vmnet_end_range = 19
- else:
- self._vmnet_end_range = 255
+ self._vmnet_end_range = 255
@property
def vmrun_path(self):
@@ -95,15 +93,7 @@ class VMware(BaseManager):
# look for vmrun
vmrun_path = self.config.settings.VMware.vmrun_path
if not vmrun_path:
- if sys.platform.startswith("win"):
- vmrun_path = shutil.which("vmrun")
- if vmrun_path is None:
- # look for vmrun.exe using the VMware Workstation directory listed in the registry
- vmrun_path = self._find_vmrun_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Workstation")
- if vmrun_path is None:
- # look for vmrun.exe using the VIX directory listed in the registry
- vmrun_path = self._find_vmrun_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware VIX")
- elif sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
vmrun_path = "/Applications/VMware Fusion.app/Contents/Library/vmrun"
else:
vmrun_path = "vmrun"
@@ -197,84 +187,44 @@ class VMware(BaseManager):
Check VMware version
"""
- if sys.platform.startswith("win"):
- # look for vmrun.exe using the directory listed in the registry
- ws_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Workstation")
- if ws_version is None:
- player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player")
- if player_version:
- log.debug(f"VMware Player version {player_version} detected")
- await self._check_vmware_player_requirements(player_version)
- else:
- log.warning("Could not find VMware version")
- self._host_type = "ws"
- else:
- log.debug(f"VMware Workstation version {ws_version} detected")
- await self._check_vmware_workstation_requirements(ws_version)
- else:
- if sys.platform.startswith("darwin"):
- if not os.path.isdir("/Applications/VMware Fusion.app"):
- raise VMwareError(
- "VMware Fusion is not installed in the standard location /Applications/VMware Fusion.app"
- )
- self._host_type = "fusion"
- return # FIXME: no version checking on Mac OS X but we support all versions of fusion
+ if sys.platform.startswith("darwin"):
+ if not os.path.isdir("/Applications/VMware Fusion.app"):
+ raise VMwareError(
+ "VMware Fusion is not installed in the standard location /Applications/VMware Fusion.app"
+ )
+ self._host_type = "fusion"
+ return # FIXME: no version checking on Mac OS X but we support all versions of fusion
- vmware_path = VMware._get_linux_vmware_binary()
- if vmware_path is None:
- raise VMwareError("VMware is not installed (vmware or vmplayer executable could not be found in $PATH)")
+ vmware_path = VMware._get_linux_vmware_binary()
+ if vmware_path is None:
+ raise VMwareError("VMware is not installed (vmware or vmplayer executable could not be found in $PATH)")
- try:
- output = await subprocess_check_output(vmware_path, "-v")
- match = re.search(r"VMware Workstation ([0-9]+)\.", output)
- version = None
- if match:
- # VMware Workstation has been detected
- version = match.group(1)
- log.debug(f"VMware Workstation version {version} detected")
- await self._check_vmware_workstation_requirements(version)
- match = re.search(r"VMware Player ([0-9]+)\.", output)
- if match:
- # VMware Player has been detected
- version = match.group(1)
- log.debug(f"VMware Player version {version} detected")
- await self._check_vmware_player_requirements(version)
- if version is None:
- log.warning(f"Could not find VMware version. Output of VMware: {output}")
- raise VMwareError(f"Could not find VMware version. Output of VMware: {output}")
- except (OSError, subprocess.SubprocessError) as e:
- log.error(f"Error while looking for the VMware version: {e}")
- raise VMwareError(f"Error while looking for the VMware version: {e}")
-
- @staticmethod
- def _get_vmnet_interfaces_registry():
-
- import winreg
-
- vmnet_interfaces = []
- regkey = r"SOFTWARE\Wow6432Node\VMware, Inc.\VMnetLib\VMnetConfig"
try:
- hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, regkey)
- for index in range(winreg.QueryInfoKey(hkey)[0]):
- vmnet = winreg.EnumKey(hkey, index)
- hkeyvmnet = winreg.OpenKey(hkey, vmnet)
- if winreg.QueryInfoKey(hkeyvmnet)[1]:
- # the vmnet has not been configure if the key has no values
- vmnet = vmnet.replace("vm", "VM")
- if vmnet not in ("VMnet0", "VMnet1", "VMnet8"):
- vmnet_interfaces.append(vmnet)
- winreg.CloseKey(hkeyvmnet)
- winreg.CloseKey(hkey)
- except OSError as e:
- raise VMwareError(f"Could not read registry key {regkey}: {e}")
- return vmnet_interfaces
+ output = await subprocess_check_output(vmware_path, "-v")
+ match = re.search(r"VMware Workstation ([0-9]+)\.", output)
+ version = None
+ if match:
+ # VMware Workstation has been detected
+ version = match.group(1)
+ log.debug(f"VMware Workstation version {version} detected")
+ await self._check_vmware_workstation_requirements(version)
+ match = re.search(r"VMware Player ([0-9]+)\.", output)
+ if match:
+ # VMware Player has been detected
+ version = match.group(1)
+ log.debug(f"VMware Player version {version} detected")
+ await self._check_vmware_player_requirements(version)
+ if version is None:
+ log.warning(f"Could not find VMware version. Output of VMware: {output}")
+ raise VMwareError(f"Could not find VMware version. Output of VMware: {output}")
+ except (OSError, subprocess.SubprocessError) as e:
+ log.error(f"Error while looking for the VMware version: {e}")
+ raise VMwareError(f"Error while looking for the VMware version: {e}")
@staticmethod
def _get_vmnet_interfaces():
- if sys.platform.startswith("win"):
- return VMware._get_vmnet_interfaces_registry()
- elif sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
vmware_networking_file = "/Library/Preferences/VMware Fusion/networking"
else:
# location on Linux
@@ -310,17 +260,7 @@ class VMware(BaseManager):
vmnet_interfaces = []
for interface in interfaces():
- if sys.platform.startswith("win"):
- if "netcard" in interface:
- windows_name = interface["netcard"]
- else:
- windows_name = interface["name"]
- match = re.search(r"(VMnet[0-9]+)", windows_name)
- if match:
- vmnet = match.group(1)
- if vmnet not in ("VMnet0", "VMnet1", "VMnet8"):
- vmnet_interfaces.append(vmnet)
- elif interface["name"].startswith("vmnet"):
+ if interface["name"].startswith("vmnet"):
vmnet = interface["name"]
if vmnet not in ("vmnet0", "vmnet1", "vmnet8"):
vmnet_interfaces.append(interface["name"])
@@ -428,7 +368,7 @@ class VMware(BaseManager):
command = [vmrun_path, "-T", self.host_type, subcommand]
command.extend(args)
- command_string = " ".join([shlex_quote(c) for c in command])
+ command_string = " ".join([shlex.quote(c) for c in command])
log.log(log_level, f"Executing vmrun with command: {command_string}")
try:
process = await asyncio.create_subprocess_exec(
@@ -677,9 +617,7 @@ class VMware(BaseManager):
:returns: path to the inventory file
"""
- if sys.platform.startswith("win"):
- return os.path.expandvars(r"%APPDATA%\Vmware\Inventory.vmls")
- elif sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
return os.path.expanduser("~/Library/Application Support/VMware Fusion/vmInventory")
else:
return os.path.expanduser("~/.vmware/inventory.vmls")
@@ -692,9 +630,7 @@ class VMware(BaseManager):
:returns: path to the preferences file
"""
- if sys.platform.startswith("win"):
- return os.path.expandvars(r"%APPDATA%\VMware\preferences.ini")
- elif sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
return os.path.expanduser("~/Library/Preferences/VMware Fusion/preferences")
else:
return os.path.expanduser("~/.vmware/preferences")
@@ -707,15 +643,7 @@ class VMware(BaseManager):
:returns: path to the default VM directory
"""
- if sys.platform.startswith("win"):
- import ctypes
- import ctypes.wintypes
-
- path = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
- ctypes.windll.shell32.SHGetFolderPathW(None, 5, None, 0, path)
- documents_folder = path.value
- return [fr"{documents_folder}\My Virtual Machines", fr"{documents_folder}\Virtual Machines"]
- elif sys.platform.startswith("darwin"):
+ if sys.platform.startswith("darwin"):
return [os.path.expanduser("~/Documents/Virtual Machines.localized")]
else:
return [os.path.expanduser("~/vmware")]
diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py
index ac8cc836..1c7c3861 100644
--- a/gns3server/compute/vmware/vmware_vm.py
+++ b/gns3server/compute/vmware/vmware_vm.py
@@ -882,14 +882,11 @@ class VMwareVM(BaseNode):
:returns: pipe path (string)
"""
- if sys.platform.startswith("win"):
- pipe_name = fr"\\.\pipe\gns3_vmware\{self.id}"
- else:
- pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vmware", f"{self.id}")
- try:
- os.makedirs(os.path.dirname(pipe_name), exist_ok=True)
- except OSError as e:
- raise VMwareError(f"Could not create the VMware pipe directory: {e}")
+ pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vmware", f"{self.id}")
+ try:
+ os.makedirs(os.path.dirname(pipe_name), exist_ok=True)
+ except OSError as e:
+ raise VMwareError(f"Could not create the VMware pipe directory: {e}")
return pipe_name
def _set_serial_console(self):
diff --git a/gns3server/config.py b/gns3server/config.py
index 3388ed97..b01e076d 100644
--- a/gns3server/config.py
+++ b/gns3server/config.py
@@ -63,63 +63,32 @@ class Config:
appname = "GNS3"
version = f"{__version_info__[0]}.{__version_info__[1]}"
- if sys.platform.startswith("win"):
+ # On UNIX-like platforms, the configuration file location can be one of the following:
+ # 1: $HOME/.config/GNS3/gns3_server.conf
+ # 2: $HOME/.config/GNS3.conf
+ # 3: /etc/xdg/GNS3/gns3_server.conf
+ # 4: /etc/xdg/GNS3.conf
+ # 5: gns3_server.conf in the current working directory
- # On windows, the configuration file location can be one of the following:
- # 1: %APPDATA%/GNS3/gns3_server.ini
- # 2: %APPDATA%/GNS3.ini
- # 3: %COMMON_APPDATA%/GNS3/gns3_server.ini
- # 4: %COMMON_APPDATA%/GNS3.ini
- # 5: server.ini in the current working directory
+ home = os.path.expanduser("~")
+ server_filename = "gns3_server.conf"
- appdata = os.path.expandvars("%APPDATA%")
- common_appdata = os.path.expandvars("%COMMON_APPDATA%")
-
- if self._profile:
- legacy_user_dir = os.path.join(appdata, appname, "profiles", self._profile)
- versioned_user_dir = os.path.join(appdata, appname, version, "profiles", self._profile)
- else:
- legacy_user_dir = os.path.join(appdata, appname)
- versioned_user_dir = os.path.join(appdata, appname, version)
-
- server_filename = "gns3_server.ini"
-
- if self._files is None and not hasattr(sys, "_called_from_test"):
- self._files = [
- os.path.join(os.getcwd(), server_filename),
- os.path.join(versioned_user_dir, server_filename),
- os.path.join(appdata, appname + ".ini"),
- os.path.join(common_appdata, appname, server_filename),
- os.path.join(common_appdata, appname + ".ini"),
- ]
+ if self._profile:
+ legacy_user_dir = os.path.join(home, ".config", appname, "profiles", self._profile)
+ versioned_user_dir = os.path.join(home, ".config", appname, version, "profiles", self._profile)
else:
+ legacy_user_dir = os.path.join(home, ".config", appname)
+ versioned_user_dir = os.path.join(home, ".config", appname, version)
- # On UNIX-like platforms, the configuration file location can be one of the following:
- # 1: $HOME/.config/GNS3/gns3_server.conf
- # 2: $HOME/.config/GNS3.conf
- # 3: /etc/xdg/GNS3/gns3_server.conf
- # 4: /etc/xdg/GNS3.conf
- # 5: gns3_server.conf in the current working directory
-
- home = os.path.expanduser("~")
- server_filename = "gns3_server.conf"
-
- if self._profile:
- legacy_user_dir = os.path.join(home, ".config", appname, "profiles", self._profile)
- versioned_user_dir = os.path.join(home, ".config", appname, version, "profiles", self._profile)
- else:
- legacy_user_dir = os.path.join(home, ".config", appname)
- versioned_user_dir = os.path.join(home, ".config", appname, version)
-
- if self._files is None and not hasattr(sys, "_called_from_test"):
- self._files = [
- os.path.join(os.getcwd(), server_filename),
- os.path.join(versioned_user_dir, server_filename),
- os.path.join(home, ".config", appname + ".conf"),
- os.path.join("/etc/gns3", server_filename),
- os.path.join("/etc/xdg", appname, server_filename),
- os.path.join("/etc/xdg", appname + ".conf"),
- ]
+ if self._files is None and not hasattr(sys, "_called_from_test"):
+ self._files = [
+ os.path.join(os.getcwd(), server_filename),
+ os.path.join(versioned_user_dir, server_filename),
+ os.path.join(home, ".config", appname + ".conf"),
+ os.path.join("/etc/gns3", server_filename),
+ os.path.join("/etc/xdg", appname, server_filename),
+ os.path.join("/etc/xdg", appname + ".conf"),
+ ]
if self._files is None:
self._files = []
@@ -182,10 +151,7 @@ class Config:
Return the server configuration file path.
"""
- if sys.platform.startswith("win"):
- server_config_filename = "gns3_server.ini"
- else:
- server_config_filename = "gns3_server.conf"
+ server_config_filename = "gns3_server.conf"
return os.path.join(self.config_dir, server_config_filename)
def clear(self):
diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py
index ff5b9f7a..cb5d874e 100644
--- a/gns3server/controller/__init__.py
+++ b/gns3server/controller/__init__.py
@@ -77,9 +77,6 @@ class Controller:
self._load_controller_settings()
if server_config.enable_ssl:
- if sys.platform.startswith("win"):
- log.critical("SSL mode is not supported on Windows")
- raise SystemExit
self._ssl_context = self._create_ssl_context(server_config)
protocol = server_config.protocol
diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py
index eb968f0d..b7874deb 100644
--- a/gns3server/controller/appliance_manager.py
+++ b/gns3server/controller/appliance_manager.py
@@ -123,7 +123,7 @@ class ApplianceManager:
async with HTTPClient.get(image_url) as response:
if response.status != 200:
raise ControllerError(f"Could not download '{image_name}' due to HTTP error code {response.status}")
- await write_image(image_name, image_type, image_path, response.content.iter_any(), images_repo)
+ await write_image(image_name, image_path, response.content.iter_any(), images_repo)
except (OSError, InvalidImageError) as e:
raise ControllerError(f"Could not save {image_type} image '{image_path}': {e}")
except ClientError as e:
@@ -156,7 +156,7 @@ class ApplianceManager:
image_path = os.path.join(image_dir, appliance_file)
if os.path.exists(image_path) and await wait_run_in_executor(md5sum, image_path) == image_checksum:
async with aiofiles.open(image_path, "rb") as f:
- await write_image(appliance_file, appliance.type, image_path, f, images_repo)
+ await write_image(appliance_file, image_path, f, images_repo)
else:
# download the image if there is a direct download URL
direct_download_url = image.get("direct_download_url")
@@ -217,7 +217,7 @@ class ApplianceManager:
try:
schemas.Appliance.parse_obj(appliance.asdict())
except ValidationError as e:
- log.warning(message=f"Could not validate appliance '{appliance.id}': {e}")
+ log.warning(f"Could not validate appliance '{appliance.id}': {e}")
if appliance.versions:
for version in appliance.versions:
if version.get("name") == image_version:
diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py
index 0301ebc8..3b308a3f 100644
--- a/gns3server/controller/export_project.py
+++ b/gns3server/controller/export_project.py
@@ -146,9 +146,6 @@ def _patch_mtime(path):
:param path: file path
"""
- if sys.platform.startswith("win"):
- # only UNIX type platforms
- return
st = os.stat(path)
file_date = datetime.fromtimestamp(st.st_mtime)
if file_date.year < 1980:
diff --git a/gns3server/controller/gns3vm/virtualbox_gns3_vm.py b/gns3server/controller/gns3vm/virtualbox_gns3_vm.py
index 95d21e8c..1329a31a 100644
--- a/gns3server/controller/gns3vm/virtualbox_gns3_vm.py
+++ b/gns3server/controller/gns3vm/virtualbox_gns3_vm.py
@@ -243,22 +243,11 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
)
if not (await self._check_vboxnet_exists(vboxnet)):
- if sys.platform.startswith("win") and vboxnet == "vboxnet0":
- # The GNS3 VM is configured with vboxnet0 by default which is not available
- # on Windows. Try to patch this with the first available vboxnet we find.
- first_available_vboxnet = await self._find_first_available_vboxnet()
- if first_available_vboxnet is None:
- raise GNS3VMError(
- f'Please add a VirtualBox host-only network with DHCP enabled and attached it to network adapter {hostonly_interface_number} for "{self._vmname}"'
- )
- await self.set_hostonly_network(hostonly_interface_number, first_available_vboxnet)
- vboxnet = first_available_vboxnet
- else:
- raise GNS3VMError(
- 'VirtualBox host-only network "{}" does not exist, please make the sure the network adapter {} configuration is valid for "{}"'.format(
- vboxnet, hostonly_interface_number, self._vmname
- )
+ raise GNS3VMError(
+ 'VirtualBox host-only network "{}" does not exist, please make the sure the network adapter {} configuration is valid for "{}"'.format(
+ vboxnet, hostonly_interface_number, self._vmname
)
+ )
if not (await self._check_dhcp_server(vboxnet)):
raise GNS3VMError(f'DHCP must be enabled on VirtualBox host-only network "{vboxnet}"')
diff --git a/gns3server/core/tasks.py b/gns3server/core/tasks.py
index f1a95556..85df5791 100644
--- a/gns3server/core/tasks.py
+++ b/gns3server/core/tasks.py
@@ -42,16 +42,6 @@ def create_startup_handler(app: FastAPI) -> Callable:
logger = logging.getLogger("asyncio")
logger.setLevel(logging.ERROR)
- if sys.platform.startswith("win"):
- # Add a periodic callback to give a chance to process signals on Windows
- # because asyncio.add_signal_handler() is not supported yet on that platform
- # otherwise the loop runs outside of signal module's ability to trap signals.
-
- def wakeup():
- loop.call_later(0.5, wakeup)
-
- loop.call_later(0.5, wakeup)
-
if log.getEffectiveLevel() == logging.DEBUG:
# On debug version we enable info that
# coroutine is not called in a way await/await
diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py
index 120b5ae3..73cc22d0 100644
--- a/gns3server/crash_report.py
+++ b/gns3server/crash_report.py
@@ -59,7 +59,7 @@ class CrashReport:
Report crash to a third party service
"""
- DSN = "https://8eb8a1f4730949f9886df2c6fdc27755:795f8ac399d04d24a273fcb35f48b725@o19455.ingest.sentry.io/38482"
+ DSN = "https://8f474628c1e44d0799140ccf05c486b8:f952ab1783d3427188fd81cc37da323c@o19455.ingest.sentry.io/38482"
_instance = None
def __init__(self):
diff --git a/gns3server/handlers/api/compute/server_handler.py b/gns3server/handlers/api/compute/server_handler.py
deleted file mode 100644
index f2e15c5f..00000000
--- a/gns3server/handlers/api/compute/server_handler.py
+++ /dev/null
@@ -1,135 +0,0 @@
-
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2015 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 .
-
-import psutil
-import platform
-
-from gns3server.web.route import Route
-from gns3server.config import Config
-from gns3server.schemas.version import VERSION_SCHEMA
-from gns3server.schemas.server_statistics import SERVER_STATISTICS_SCHEMA
-from gns3server.compute.port_manager import PortManager
-from gns3server.utils.cpu_percent import CpuPercent
-from gns3server.utils.path import get_default_project_directory
-from gns3server.version import __version__
-from aiohttp.web import HTTPConflict
-
-
-class ServerHandler:
-
- @Route.get(
- r"/version",
- description="Retrieve the server version number",
- output=VERSION_SCHEMA)
- def version(request, response):
-
- config = Config.instance()
- local_server = config.get_section_config("Server").getboolean("local", False)
- response.json({"version": __version__, "local": local_server})
-
- @Route.get(
- r"/statistics",
- description="Retrieve server statistics",
- output=SERVER_STATISTICS_SCHEMA,
- status_codes={
- 200: "Statistics information returned",
- 409: "Conflict"
- })
- def statistics(request, response):
-
- try:
- memory_total = psutil.virtual_memory().total
- memory_free = psutil.virtual_memory().available
- memory_used = memory_total - memory_free # actual memory usage in a cross platform fashion
- swap_total = psutil.swap_memory().total
- swap_free = psutil.swap_memory().free
- swap_used = psutil.swap_memory().used
- cpu_percent = int(CpuPercent.get())
- load_average_percent = [int(x / psutil.cpu_count() * 100) for x in psutil.getloadavg()]
- memory_percent = int(psutil.virtual_memory().percent)
- swap_percent = int(psutil.swap_memory().percent)
- disk_usage_percent = int(psutil.disk_usage(get_default_project_directory()).percent)
- except psutil.Error as e:
- raise HTTPConflict(text="Psutil error detected: {}".format(e))
- response.json({"memory_total": memory_total,
- "memory_free": memory_free,
- "memory_used": memory_used,
- "swap_total": swap_total,
- "swap_free": swap_free,
- "swap_used": swap_used,
- "cpu_usage_percent": cpu_percent,
- "memory_usage_percent": memory_percent,
- "swap_usage_percent": swap_percent,
- "disk_usage_percent": disk_usage_percent,
- "load_average_percent": load_average_percent})
-
- @Route.get(
- r"/debug",
- description="Return debug information about the compute",
- status_codes={
- 201: "Written"
- })
- def debug(request, response):
- response.content_type = "text/plain"
- response.text = ServerHandler._getDebugData()
-
- @staticmethod
- def _getDebugData():
- try:
- addrs = ["* {}: {}".format(key, val) for key, val in psutil.net_if_addrs().items()]
- except UnicodeDecodeError:
- addrs = ["INVALID ADDR WITH UNICODE CHARACTERS"]
-
- data = """Version: {version}
-OS: {os}
-Python: {python}
-CPU: {cpu}
-Memory: {memory}
-
-Networks:
-{addrs}
-""".format(
- version=__version__,
- os=platform.platform(),
- python=platform.python_version(),
- memory=psutil.virtual_memory(),
- cpu=psutil.cpu_times(),
- addrs="\n".join(addrs)
- )
-
- try:
- connections = psutil.net_connections()
- # You need to be root for OSX
- except psutil.AccessDenied:
- connections = None
-
- if connections:
- data += "\n\nConnections:\n"
- for port in PortManager.instance().tcp_ports:
- found = False
- for open_port in connections:
- if open_port.laddr[1] == port:
- found = True
- data += "TCP {}: {}\n".format(port, found)
- for port in PortManager.instance().udp_ports:
- found = False
- for open_port in connections:
- if open_port.laddr[1] == port:
- found = True
- data += "UDP {}: {}\n".format(port, found)
- return data
diff --git a/gns3server/logger.py b/gns3server/logger.py
index 74cd2093..2baba0fd 100644
--- a/gns3server/logger.py
+++ b/gns3server/logger.py
@@ -38,7 +38,7 @@ class ColouredFormatter(logging.Formatter):
message = super().format(record)
- if not colour or sys.platform.startswith("win"):
+ if not colour:
return message.replace("#RESET#", "")
level_no = record.levelno
@@ -150,11 +150,6 @@ def init_logger(level, logfile=None, max_bytes=10000000, backup_count=10, compre
stream_handler.formatter = ColouredFormatter(
"{asctime} {levelname} {filename}:{lineno} {message}", "%Y-%m-%d %H:%M:%S", "{"
)
- elif sys.platform.startswith("win"):
- stream_handler = WinStreamHandler(sys.stdout)
- stream_handler.formatter = ColouredFormatter(
- "{asctime} {levelname} {filename}:{lineno} {message}", "%Y-%m-%d %H:%M:%S", "{"
- )
else:
stream_handler = ColouredStreamHandler(sys.stdout)
stream_handler.formatter = ColouredFormatter(
diff --git a/gns3server/main.py b/gns3server/main.py
index 9f4e8a20..624612f3 100644
--- a/gns3server/main.py
+++ b/gns3server/main.py
@@ -29,19 +29,6 @@ import gns3server.utils.get_resource
import os
import sys
-import types
-
-
-# To avoid strange bug later we switch the event loop before any other operation
-if sys.platform.startswith("win"):
- import asyncio
-
- # use the Proactor event loop on Windows
- loop = asyncio.ProactorEventLoop()
- asyncio.set_event_loop(loop)
-
-if sys.platform.startswith("win"):
- sys.modules["termios"] = types.ModuleType("termios")
def daemonize():
@@ -77,9 +64,10 @@ def main():
Entry point for GNS3 server
"""
- if not sys.platform.startswith("win"):
- if "--daemon" in sys.argv:
- daemonize()
+ if sys.platform.startswith("win"):
+ raise SystemExit("Windows is not a supported platform to run the GNS3 server")
+ if "--daemon" in sys.argv:
+ daemonize()
from gns3server.server import Server
Server().run()
diff --git a/gns3server/schemas/controller/images.py b/gns3server/schemas/controller/images.py
index bee6621e..64efcdbf 100644
--- a/gns3server/schemas/controller/images.py
+++ b/gns3server/schemas/controller/images.py
@@ -32,7 +32,8 @@ class ImageBase(BaseModel):
Common image properties.
"""
- filename: str = Field(..., description="Image name")
+ filename: str = Field(..., description="Image filename")
+ path: str = Field(..., description="Image path")
image_type: ImageType = Field(..., description="Image type")
image_size: int = Field(..., description="Image size in bytes")
checksum: str = Field(..., description="Checksum value")
diff --git a/gns3server/server.py b/gns3server/server.py
index 2e6ffd73..39c4e095 100644
--- a/gns3server/server.py
+++ b/gns3server/server.py
@@ -64,8 +64,8 @@ class Server:
or there: http://robjwells.com/post/61198832297/get-your-us-ascii-out-of-my-face
"""
- # no need to check on Windows or when this application is frozen
- if sys.platform.startswith("win") or hasattr(sys, "frozen"):
+ # no need to check when this application is frozen
+ if hasattr(sys, "frozen"):
return
language = encoding = None
@@ -185,20 +185,11 @@ class Server:
except asyncio.CancelledError:
pass
- signals = [] # SIGINT and SIGTERM are already registered by uvicorn
- if sys.platform.startswith("win"):
- signals.extend(["SIGBREAK"])
- else:
- signals.extend(["SIGHUP", "SIGQUIT"])
-
+ signals = ["SIGHUP", "SIGQUIT"] # SIGINT and SIGTERM are already registered by uvicorn
for signal_name in signals:
callback = functools.partial(signal_handler, signal_name)
- if sys.platform.startswith("win"):
- # add_signal_handler() is not yet supported on Windows
- signal.signal(getattr(signal, signal_name), callback)
- else:
- loop = asyncio.get_event_loop()
- loop.add_signal_handler(getattr(signal, signal_name), callback)
+ loop = asyncio.get_event_loop()
+ loop.add_signal_handler(getattr(signal, signal_name), callback)
@staticmethod
def _kill_ghosts():
@@ -250,10 +241,6 @@ class Server:
args = self._parse_arguments(sys.argv[1:])
- if args.daemon and sys.platform.startswith("win"):
- log.critical("Daemon is not supported on Windows")
- sys.exit(1)
-
if args.pid:
self._pid_lock(args.pid)
self._kill_ghosts()
@@ -281,9 +268,9 @@ class Server:
else:
log.info(f"Compute authentication is enabled with username '{config.Server.compute_username}'")
- # we only support Python 3 version >= 3.6
- if sys.version_info < (3, 6, 0):
- raise SystemExit("Python 3.6 or higher is required")
+ # we only support Python 3 version >= 3.7
+ if sys.version_info < (3, 7, 0):
+ raise SystemExit("Python 3.7 or higher is required")
log.info(
"Running with Python {major}.{minor}.{micro} and has PID {pid}".format(
@@ -316,9 +303,6 @@ class Server:
access_log = True
if config.Server.enable_ssl:
- if sys.platform.startswith("win"):
- log.critical("SSL mode is not supported on Windows")
- raise SystemExit
log.info("SSL is enabled")
config = uvicorn.Config(
@@ -346,10 +330,6 @@ class Server:
loop = asyncio.get_event_loop()
loop.run_until_complete(server.serve())
- except OSError as e:
- # This is to ignore OSError: [WinError 0] The operation completed successfully exception on Windows.
- if not sys.platform.startswith("win") or not e.winerror == 0:
- raise
except Exception as e:
log.critical(f"Critical error while running the server: {e}", exc_info=1)
CrashReport.instance().capture_exception()
diff --git a/gns3server/static/web-ui/index.html b/gns3server/static/web-ui/index.html
index 1cdf3068..9fc43f11 100644
--- a/gns3server/static/web-ui/index.html
+++ b/gns3server/static/web-ui/index.html
@@ -46,6 +46,6 @@
gtag('config', 'G-5D6FZL9923');
-
+