Compare commits

...

100 Commits

Author SHA1 Message Date
grossmj
fcf62fc507 Release v2.2.54 2025-04-21 17:15:26 +07:00
grossmj
c811c270ec Merge branch 'master' into 2.2 2025-04-21 17:08:42 +07:00
grossmj
dbc519e0af Development on 2.2.54.dev1 2025-04-18 15:05:37 +07:00
grossmj
bcbf8be182 Replace "Docker hub" by "Docker repository" because it is possible to use different repositories 2025-04-16 17:42:44 +07:00
grossmj
43df10520f Upgrade dependencies 2025-04-16 17:08:13 +07:00
grossmj
55b37716a3 Fix bring console in front when clicking on "Open all consoles". Fixes #3706 2025-04-16 16:55:11 +07:00
grossmj
e08253e362 Add -F arg to wmctrl. Ref #3706 2025-02-16 00:10:10 +10:00
Jeremy Grossmann
b9a59183a1 Merge pull request #3699 from GNS3/release/v2.2.53
Release v2.2.53
2025-01-21 09:46:53 +07:00
grossmj
342ca95bd2 Release v2.2.53 2025-01-21 11:52:12 +10:00
grossmj
187ef561fd Update file browser filters for all files and IOU images 2025-01-07 11:32:17 +07:00
grossmj
97070718fa Upgrade dependencies 2024-12-30 10:49:40 +07:00
grossmj
9466b2a1fb Merge branch 'master' into 2.2 2024-12-28 18:03:26 +07:00
grossmj
3edde1274b Fix Linux Mint default terminal configuration 2024-12-25 22:14:25 +07:00
Jeremy Grossmann
e17e7fc033 Merge pull request #3672 from ob7/enable-css-grid-colors
Add CSS Grid Color Customization Support
2024-12-02 19:56:29 +10:00
ob7
bfe11d7976 apply grid color via css property 2024-12-02 00:13:01 -09:00
Jeremy Grossmann
2b98d51ff7 Merge pull request #3668 from GNS3/release/v2.2.52
release/v2.2.52
2024-12-02 11:35:42 +10:00
grossmj
f6fb0100e2 Development on 2.2.53.dev1 2024-12-02 11:34:00 +10:00
grossmj
804b871cd6 Release v2.2.52 2024-12-02 11:14:23 +10:00
grossmj
c604ff70c7 Change title of QMessageBox 2024-12-02 10:55:49 +10:00
grossmj
63a7f36cfe Add iol extension filter. Ref #3664 2024-11-25 10:59:00 +10:00
grossmj
731fee1217 Remove maximum 64GB RAM limitation for QEMU VMs. Fixes #3658 2024-11-18 14:56:00 +10:00
Jeremy Grossmann
714dae44f3 Merge pull request #3659 from GNS3/feature/bring-to-front-linux
Bring to front support for consoles on Linux.
2024-11-10 18:26:21 +10:00
grossmj
c67dc19ec8 Bring to front support for consoles on Linux. 2024-11-10 18:23:01 +10:00
grossmj
1c5f6b362b Relax setuptools requirement to allow for easier Debian packaging on Ubuntu Focal & Jammy 2024-11-08 12:46:56 +10:00
grossmj
683400c204 Development on 2.2.52.dev1 2024-11-07 23:13:31 +10:00
Jeremy Grossmann
5efb3019f4 Merge pull request #3656 from GNS3/release/v2.2.51
release/v2.2.51
2024-11-07 23:13:11 +10:00
grossmj
23e0520cd2 Release v2.2.51 2024-11-07 15:08:27 +10:00
grossmj
1199de27df Merge branch 'master' into 2.2 2024-10-28 17:32:19 +10:00
grossmj
b3140f9d8e Python 3.13 support 2024-10-26 18:38:07 +10:00
grossmj
1b50cdc341 Upgrade dependencies 2024-10-26 18:22:40 +10:00
Jeremy Grossmann
bd15734c30 Merge pull request #3649 from ob7/add-link-shortcut-key
Add Keyboard Shortcut (Ctrl+L) for Add Link Tool
2024-10-24 21:23:15 +10:00
ob7
24fff8972a Add keyboard shortcut for Add Link 2024-10-23 17:13:17 -08:00
grossmj
9614a1253c Development on 2.2.51.dev1 2024-10-21 13:24:14 +10:00
Jeremy Grossmann
a3a0be863e Merge pull request #3644 from GNS3/2.2
Release v2.2.50
2024-10-21 13:22:00 +10:00
grossmj
9b5713df03 Release v2.2.50 2024-10-21 12:14:18 +10:00
grossmj
6df7dc4730 Replace AppVeyor testing with GH Actions 2024-10-19 16:07:54 +10:00
grossmj
e84ef8bf13 Fix issue when pid file contains invalid data 2024-10-16 16:28:46 +10:00
grossmj
f7703e3fa2 Add comment to indicate sentry-sdk is optional. Ref https://github.com/GNS3/gns3-server/issues/2423 2024-10-14 17:46:53 +10:00
grossmj
de8b9bc8f1 Fix f-string syntax error. Fixes #3639 2024-09-28 11:20:39 +07:00
grossmj
f21a530729 Improve information provided when uploading invalid appliance image. Fixes #3637 2024-09-26 16:14:02 +07:00
grossmj
be2b5eecf6 Use "experimental features" option to force listening for HTTP notification streams. Ref #3579 2024-09-24 12:25:26 +07:00
Jeremy Grossmann
2d4f6e6ecf Merge pull request #3635 from GNS3/revert-3633-backport-aux-console-support
Revert "Backport auxiliary console support for Qemu, Docker and Dynamips nodes"
2024-09-23 13:15:32 +07:00
Jeremy Grossmann
c152de84de Revert "Backport auxiliary console support for Qemu, Docker and Dynamips nodes" 2024-09-23 13:11:23 +07:00
Jeremy Grossmann
d34e7b377c Merge pull request #3633 from GNS3/backport-aux-console-support
Backport auxiliary console support for Qemu, Docker and Dynamips nodes
2024-09-22 21:44:25 +07:00
grossmj
74c55241cf Backport auxiliary console support for Qemu, Docker and Dynamips nodes 2024-09-22 18:29:05 +07:00
grossmj
6edb9c9303 Fix to allow packet capture on more than 6 links. Fixes #3594 2024-09-21 16:40:22 +07:00
grossmj
5d14dd9ab8 Merge branch 'master' into 2.2 2024-09-18 17:14:33 +07:00
Jeremy Grossmann
0c7cae2222 Merge pull request #3632 from GNS3/docker-mac-address
Support for custom MAC addresses in Docker containers
2024-09-18 04:05:46 -06:00
grossmj
9b882924c0 Support for configuring MAC address in Docker containers 2024-09-18 16:30:22 +07:00
Jeremy Grossmann
fccfc01f2e Merge pull request #3630 from griffi-gh/patch-1
Add KRDC to pre-configured VNC console commands
2024-09-12 12:08:38 -06:00
griffi-gh
46872c664d Add KRDC to pre-configured VNC console commands 2024-09-12 17:37:16 +02:00
grossmj
89166b9d35 Development on 2.2.50.dev1 2024-08-06 20:33:55 +02:00
Jeremy Grossmann
d0421e8b1f Merge pull request #3616 from GNS3/2.2
Release v2.2.49
2024-08-06 20:32:46 +02:00
grossmj
b35ce7303d Release v2.2.49 2024-08-06 12:55:15 +02:00
grossmj
47432568e6 Upgrade development packages 2024-08-03 12:32:43 +02:00
grossmj
42ea34ca6c Upgrade jsonschema and sentry-sdk packages 2024-08-03 11:57:15 +02:00
grossmj
9f3598d36d Upgrade to PyQt5 v5.15.11 2024-08-03 11:55:40 +02:00
grossmj
8df248a1e6 Add shortcuts info dialog 2024-07-27 16:23:23 +02:00
grossmj
7196aeb3cf Merge branch 'master' into 2.2 2024-07-27 15:46:14 +02:00
Jeremy Grossmann
38657f4112 Merge pull request #3612 from braza2004/master
Control menu Shortcut Keys
2024-07-27 15:43:53 +02:00
Bilal
84017fa0f1 modified Control menu shortcuts further 2024-07-24 07:05:38 +00:00
Bilal
3aa5a5369f modified Control menu shortcuts 2024-07-24 07:02:14 +00:00
athaarnaqvi
0ed791b946 Added Key Shortcuts 2024-07-23 16:59:55 +05:00
Jeremy Grossmann
133732b7ae Merge pull request #3608 from braza2004/FitInViewKeyShortcut
Fit In View Shortcut Key
2024-07-22 23:27:11 +02:00
grossmj
2ed48def9f Add Fit In View Shortcut in Ui file instead 2024-07-22 23:25:05 +02:00
grossmj
279a91d402 Fix tests. Fixes #3605 2024-07-22 18:58:32 +02:00
Bilal_Raza
4906678d13 feature/FitInViewShortcut 2024-07-22 16:53:58 +05:00
grossmj
a5ff9318f0 Development on 2.2.49.dev1 2024-07-13 16:32:13 +02:00
Jeremy Grossmann
faa802d59c Merge pull request #3597 from GNS3/release/v2.2.48.1
Release v2.2.48.1
2024-07-13 14:43:39 +02:00
grossmj
01b5b8bfa8 Release v2.2.48.1 2024-07-12 18:26:17 +02:00
grossmj
b22c5c8442 Development on 2.2.49.dev1 2024-07-09 00:32:45 +02:00
Jeremy Grossmann
141e7d8307 Merge pull request #3592 from GNS3/2.2
Release v2.2.48
2024-07-09 00:31:15 +02:00
grossmj
1e80354e4e Release v2.2.48 2024-07-08 18:44:09 +02:00
grossmj
d340b1f50a Use "experimental features" to allow bypassing hostname validation. Ref #3524 2024-07-08 14:19:42 +02:00
grossmj
d7bb195610 Update appliance_v8.json. Ref https://github.com/GNS3/gns3-registry/pull/897 2024-07-07 17:47:24 +02:00
Jeremy Grossmann
d963dd4746 Merge pull request #3591 from GNS3/feature/keep-compute-ids
Option to keep the compute IDs unchanged when exporting a project
2024-07-06 17:12:42 +02:00
grossmj
60addccd95 Option to keep the compute IDs unchanged when exporting a project 2024-07-06 17:08:16 +02:00
grossmj
80b654ba53 Upgrade sentry-sdk and psutil packages 2024-07-03 18:52:49 +02:00
grossmj
b67a8c87a7 Switch to PyQt5 5.15.10 for macOS build 2024-06-10 17:20:52 +02:00
grossmj
53ece94a08 Development on 2.2.48.dev1 2024-05-23 12:19:15 +07:00
Jeremy Grossmann
a6c7e0be59 Merge pull request #3586 from GNS3/update-ga-workflows
Update GitHub Action workflows
2024-05-17 12:21:05 +07:00
grossmj
fcea25dcbb Update GitHub Action workflows 2024-05-17 12:14:47 +07:00
Jeremy Grossmann
f29c065164 Merge pull request #3585 from GNS3/release/v2.2.47
Release v2.2.47
2024-05-15 17:27:30 +07:00
grossmj
c6b5494ce6 Use system Python 2024-05-15 17:19:54 +07:00
grossmj
02b14f6aea Call pip as a Python module 2024-05-15 17:12:42 +07:00
grossmj
e2cc378aee Use ubuntu:latest for running tests inside Docker container 2024-05-15 17:10:04 +07:00
grossmj
812aedebe3 Release v2.2.47 2024-05-15 12:14:48 +07:00
grossmj
2f0d2063cf Remove maximum size for capture dialog. Ref #3576 2024-05-10 12:58:35 +07:00
Jeremy Grossmann
ce0515f0ae Merge pull request #3582 from GNS3/drop-python3.7
Drop Python 3.7
2024-05-09 19:09:14 +07:00
grossmj
bc10c69a2d Change sentry-sdk version 2024-05-09 19:03:08 +07:00
grossmj
3707758388 Upgrade aiohttp, sentry-sdk and truststore 2024-05-09 18:58:38 +07:00
grossmj
8aaefac91b Upgrade jsonschema and aiohttp 2024-05-09 18:37:41 +07:00
grossmj
19157ab49d Drop Python 3.7 2024-05-09 18:23:59 +07:00
grossmj
38b98cd883 Remove dev requirements for Python 3.6 2024-05-09 18:05:08 +07:00
Jeremy Grossmann
e9419924c5 Merge pull request #3580 from GNS3/feature/nat-symbols
NAT symbols
2024-04-22 19:00:15 +07:00
grossmj
cf3c5c09fa Add NAT symbols 2024-04-22 18:51:29 +07:00
grossmj
9e89cf5ad5 Only show log message if event has "message" 2024-03-07 17:18:30 +01:00
grossmj
131ef09b55 Development on 2.2.47.dev1 2024-03-05 01:07:17 +08:00
grossmj
2df18ee04e Upgrade sentry-sdk to version 1.40.6 2024-03-05 00:45:26 +08:00
Jeremy Grossmann
854e1fded6 Merge pull request #3568 from GNS3/release/v2.2.46
Release v2.2.46
2024-02-26 17:50:19 +08:00
62 changed files with 918 additions and 438 deletions

View File

@@ -10,7 +10,7 @@ jobs:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.4.0
- uses: actions/add-to-project@v1.0.1
with:
project-url: https://github.com/orgs/GNS3/projects/3
github-token: ${{ secrets.ADD_NEW_ISSUES_TO_PROJECT }}

View File

@@ -15,62 +15,79 @@ on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '27 6 * * 2'
- cron: '17 22 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
include:
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build and run Docker image
run: |
docker build -t gns3-gui-test .

View File

@@ -1,5 +1,71 @@
# Change Log
## 2.2.54 21/04/2025
* Replace "Docker hub" by "Docker repository" because it is possible to use different repositories
* Upgrade dependencies
* Fix bring console in front when clicking on "Open all consoles". Fixes #3706
* Add -F arg to wmctrl. Ref #3706
## 2.2.53 21/01/2025
* Update file browser filters for all files and IOU images
* Upgrade dependencies
* Fix Linux Mint default terminal configuration
## 2.2.52 02/12/2024
* Add iol extension filter. Ref #3664
* Remove maximum 64GB RAM limitation for QEMU VMs. Fixes #3658
* Bring to front support for consoles on Linux.
* Relax setuptools requirement to allow for easier Debian packaging on Ubuntu Focal & Jammy
## 2.2.51 07/11/2024
* Python 3.13 support
* Upgrade dependencies
* Add keyboard shortcut for Add Link
## 2.2.50 21/10/2024
* Fix issue when pid file contains invalid data
* Add comment to indicate sentry-sdk is optional. Ref https://github.com/GNS3/gns3-server/issues/2423
* Improve information provided when uploading invalid appliance image. Fixes #3637
* Use "experimental features" option to force listening for HTTP notification streams. Ref #3579
* Fix to allow packet capture on more than 6 links. Fixes #3594
* Support for configuring MAC address in Docker containers
* Add KRDC to pre-configured VNC console commands
## 2.2.49 06/08/2024
* Upgrade jsonschema and sentry-sdk packages
* Upgrade to PyQt5 v5.15.11
* Add shortcuts info dialog
* Added Key Shortcuts
## 2.2.48.1 12/07/2024
* No changes
## 2.2.48 08/07/2024
* Use "experimental features" to allow bypassing hostname validation. Ref #3524
* Update appliance_v8.json. Ref https://github.com/GNS3/gns3-registry/pull/897
* Option to keep the compute IDs unchanged when exporting a project
* Upgrade sentry-sdk and psutil packages
* Switch to PyQt5 5.15.10 for macOS build
## 2.2.47 15/05/2024
* Remove maximum size for capture dialog. Ref #3576
* Change sentry-sdk version
* Upgrade aiohttp, sentry-sdk and truststore
* Upgrade jsonschema and aiohttp
* Drop Python 3.7
* Remove dev requirements for Python 3.6
* Add NAT symbols
* Only show log message if event has "message"
## 2.2.46 26/02/2024
* Add GNS3 console command "env" to show what environment variables are used. Ref https://github.com/GNS3/gns3-server/issues/2306

View File

@@ -1,16 +1,16 @@
# Run tests inside a container
FROM ubuntu:18.04
FROM ubuntu:latest
MAINTAINER GNS3 Team
RUN apt-get update
RUN apt-get install -y --force-yes python3.6 python3-pyqt5 python3-pip python3-pyqt5.qtsvg python3-pyqt5.qtwebsockets python3.6-dev xvfb
RUN apt-get install -y --force-yes python3 python3-pyqt5 python3-pip python3-pyqt5.qtsvg python3-pyqt5.qtwebsockets python3-dev xvfb
RUN apt-get clean
ADD dev-requirements.txt /dev-requirements.txt
ADD requirements.txt /requirements.txt
RUN pip3 install --no-cache-dir -r /dev-requirements.txt
RUN python3 -m pip install --break-system-packages --no-cache-dir -r /dev-requirements.txt
ADD . /src
WORKDIR /src
CMD xvfb-run python3.6 -m pytest -vv
CMD xvfb-run python3 -m pytest -vv

View File

@@ -1,20 +0,0 @@
version: '{build}-{branch}'
image: Visual Studio 2022
platform: x64
environment:
PYTHON: "C:\\Python38-x64"
DISTUTILS_USE_SDK: "1"
install:
- cinst nmap
- "%PYTHON%\\python.exe -m pip install -U pip setuptools" # upgrade pip & setuptools first
- "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt"
- "%PYTHON%\\python.exe -m pip install -r win-requirements.txt"
build: off
test_script:
- "%PYTHON%\\python.exe -m pytest -v"

View File

@@ -1,6 +1,4 @@
-rrequirements.txt
pytest==7.2.0; python_version >= '3.7'
pytest==7.0.1; python_version < '3.7' # v7.0.1 is the last version to support Python 3.6
flake8==5.0.4
pytest-timeout==2.1.0
pytest==8.3.2
pytest-timeout==2.3.1

View File

@@ -25,6 +25,8 @@ from .qt import QtCore, QtNetwork, QtGui, QtWidgets, QtWebSockets, qpartial, qsl
from .symbol import Symbol
from .local_server_config import LocalServerConfig
from .settings import LOCAL_SERVER_SETTINGS
from gns3.local_config import LocalConfig
from gns3.utils import parse_version
import logging
@@ -416,19 +418,23 @@ class Controller(QtCore.QObject):
self._notification_stream = None
# Qt websocket before Qt 5.6 doesn't support auth
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"):
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0") or LocalConfig.instance().experimental():
self._notification_stream = Controller.instance().createHTTPQuery("GET", "/notifications", self._endListenNotificationCallback,
downloadProgressCallback=self._event_received,
networkManager=self._notification_network_manager,
timeout=None,
showProgress=False,
ignoreErrors=True)
url = self._http_client.url() + '/notifications'
log.info("Listening for controller notifications on '{}'".format(url))
else:
self._notification_stream = self._http_client.connectWebSocket(self._websocket, "/notifications/ws")
self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
self._notification_stream.error.connect(self._websocket_error)
self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
log.info("Listening for controller notifications on '{}'".format(self._notification_stream.requestUrl().toString()))
def stopListenNotifications(self):
if self._notification_stream:
@@ -489,11 +495,11 @@ class Controller(QtCore.QObject):
project = Topology.instance().project()
if project and project.id() == result["event"]["project_id"]:
project.projectUpdatedCallback(result["event"])
elif result["action"] == "log.error":
log.error(result["event"]["message"])
elif result["action"] == "log.warning":
log.warning(result["event"]["message"])
elif result["action"] == "log.info":
log.info(result["event"]["message"], extra={"show": True})
elif result["action"] == "log.error" and result["event"].get("message"):
log.error(result["event"].get("message"))
elif result["action"] == "log.warning" and result["event"].get("message"):
log.warning(result["event"].get("message"))
elif result["action"] == "log.info" and result["event"].get("message"):
log.info(result["event"].get("message"), extra={"show": True})
elif result["action"] == "ping":
pass

View File

@@ -50,7 +50,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://3dec04c8d64949b41e70d86b398871ee@o19455.ingest.sentry.io/38506"
DSN = "https://62d45083e8fee6a3f5c28d4710ef2cb6@o19455.ingest.us.sentry.io/38506"
_instance = None
def __init__(self):

View File

@@ -543,9 +543,19 @@ Usage: {}
image = Image(self._appliance.template_type(), path, filename=disk["filename"])
try:
if "md5sum" in disk and image.md5sum != disk["md5sum"]:
reply = QtWidgets.QMessageBox.question(self, "Add appliance",
"This is not the correct file. The MD5 sum is {} and should be {}.\nDo you want to accept it at your own risks?".format(image.md5sum, disk["md5sum"]),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
reply = QtWidgets.QMessageBox.question(
self,
"Add appliance",
"This is not the correct file.\n\n"
"MD5 checksum\n"
f"actual:\t{image.md5sum}\n"
f"expected:\t{disk['md5sum']}\n\n"
"File size\n"
f"actual:\t{image.filesize} bytes\n"
f"expected:\t{disk['filesize']} bytes\n\n"
"Do you want to accept it at your own risks?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.No:
return
except OSError as e:

View File

@@ -126,20 +126,18 @@ class ExportProjectWizard(QtWidgets.QWizard, Ui_ExportProjectWizard):
"""
if result:
include_images = include_snapshots = reset_mac_addresses = keep_compute_ids = "no"
if self.uiIncludeImagesCheckBox.isChecked():
include_images = "yes"
else:
include_images = "no"
if self.uiIncludeSnapshotsCheckBox.isChecked():
include_snapshots = "yes"
else:
include_snapshots = "no"
if self.uiResetMacAddressesCheckBox.isChecked():
reset_mac_addresses = "yes"
else:
reset_mac_addresses = "no"
if self.uiKeepComputeIdsCheckBox.isChecked():
keep_compute_ids = "yes"
compression = self.uiCompressionComboBox.currentData()
export_worker = ExportProjectWorker(self._project, self._path, include_images, include_snapshots, reset_mac_addresses, compression)
export_worker = ExportProjectWorker(self._project, self._path, include_images, include_snapshots, reset_mac_addresses, keep_compute_ids, compression)
progress_dialog = ProgressDialog(export_worker, "Exporting project", "Exporting portable project files...", "Cancel", parent=self, create_thread=False)
progress_dialog.show()
progress_dialog.exec_()

View File

@@ -184,7 +184,7 @@ class SymbolSelectionDialog(QtWidgets.QDialog, Ui_SymbolSelectionDialog):
def _symbolBrowserSlot(self):
# supported image file formats
file_formats = "Image files (*.svg *.bmp *.jpeg *.jpg *.pbm *.pgm *.png *.ppm *.xbm *.xpm *.gif);;All files (*.*)"
file_formats = "Image files (*.svg *.bmp *.jpeg *.jpg *.pbm *.pgm *.png *.ppm *.xbm *.xpm *.gif);;All files (*)"
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Image", SymbolSelectionDialog._symbols_dir, file_formats)
if not path:
return

View File

@@ -74,6 +74,10 @@ class GraphicsView(QtWidgets.QGraphicsView):
:param parent: parent widget
"""
# Class-level constants for default colors
DEFAULT_DRAWING_GRID_COLOR = QtGui.QColor(208, 208, 208) # #D0D0D0
DEFAULT_NODE_GRID_COLOR = QtGui.QColor(190, 190, 190) # #BEBEBE
def __init__(self, parent):
# Our parent is the central widget which parent is the main window.
@@ -92,6 +96,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
self._dragging = False
self._grid_size = 75
self._drawing_grid_size = 25
self._drawing_grid_color = self.DEFAULT_DRAWING_GRID_COLOR
self._node_grid_color = self.DEFAULT_NODE_GRID_COLOR
self._last_mouse_position = None
self._topology = Topology.instance()
self._background_warning_msgbox = QtWidgets.QErrorMessage(self)
@@ -666,7 +672,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
self.configureSlot()
return
else:
if sys.platform.startswith("win") and item.node().bringToFront():
if item.node().bringToFront():
return
self.consoleFromItems(self.scene().selectedItems())
return
@@ -860,8 +866,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
show_in_file_manager_action.triggered.connect(self.showInFileManagerSlot)
menu.addAction(show_in_file_manager_action)
if sys.platform.startswith("win") and True in list(map(lambda item: isinstance(item, NodeItem) and hasattr(item.node(), "bringToFront"), items)):
# Action: bring console or window to front (Windows only)
if not sys.platform.startswith("darwin") and True in list(map(lambda item: isinstance(item, NodeItem) and hasattr(item.node(), "bringToFront"), items)):
# Action: bring console or window to front (Windows and Linux only)
bring_to_front_action = QtWidgets.QAction("Bring to front", menu)
bring_to_front_action.setIcon(get_icon("front.svg"))
bring_to_front_action.triggered.connect(self.bringToFrontSlot)
@@ -1032,7 +1038,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
if not new_hostname.strip():
QtWidgets.QMessageBox.critical(self, "Change hostname", "Hostname cannot be blank")
continue
if hasattr(item.node(), "validateHostname"):
if hasattr(item.node(), "validateHostname") and not LocalConfig.instance().experimental():
if not item.node().validateHostname(new_hostname):
QtWidgets.QMessageBox.critical(self, "Change hostname", "Invalid name detected for this node: {}".format(new_hostname))
continue
@@ -1249,7 +1255,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
path, _ = QtWidgets.QFileDialog.getOpenFileName(self,
"Import {}".format(os.path.basename(config_file)),
self._import_config_directory,
"All files (*.*);;Config files (*.cfg)",
"All files (*);;Config files (*.cfg)",
"Config files (*.cfg)")
if not path:
continue
@@ -1296,7 +1302,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
for item in items:
for config_file in item.node().configFiles():
path, ok = QtWidgets.QFileDialog.getSaveFileName(self, "Export file", os.path.join(self._export_config_directory, item.node().name() + "_" + os.path.basename(config_file)), "All files (*.*);;Config files (*.cfg)")
path, ok = QtWidgets.QFileDialog.getSaveFileName(self, "Export file", os.path.join(self._export_config_directory, item.node().name() + "_" + os.path.basename(config_file)), "All files (*);;Config files (*.cfg)")
if not path:
continue
self._export_config_directory = os.path.dirname(path)
@@ -1659,11 +1665,39 @@ class GraphicsView(QtWidgets.QGraphicsView):
self._topology.addDrawing(item)
return item
@QtCore.Property(QtGui.QColor)
def drawingGridColor(self):
"""Returns the drawing grid color"""
return self._drawing_grid_color
@drawingGridColor.setter
def drawingGridColor(self, color):
"""Sets the drawing grid color"""
self._drawing_grid_color = color
self.viewport().update()
@QtCore.Property(QtGui.QColor)
def nodeGridColor(self):
"""Returns the node grid color"""
return self._node_grid_color
@nodeGridColor.setter
def nodeGridColor(self, color):
"""Sets the node grid color"""
self._node_grid_color = color
self.viewport().update()
def resetGridColors(self):
"""Reset grid colors to defaults"""
self._drawing_grid_color = self.DEFAULT_DRAWING_GRID_COLOR
self._node_grid_color = self.DEFAULT_NODE_GRID_COLOR
self.viewport().update()
def drawBackground(self, painter, rect):
super().drawBackground(painter, rect)
if self._main_window.uiShowGridAction.isChecked():
grids = [(self.drawingGridSize(), QtGui.QColor(208, 208, 208)),
(self.nodeGridSize(), QtGui.QColor(190, 190, 190))]
grids = [(self.drawingGridSize(), self._drawing_grid_color),
(self.nodeGridSize(), self._node_grid_color)]
painter.save()
for (grid, colour) in grids:
if not grid:

View File

@@ -23,7 +23,7 @@ import re
from .qt import sip
import uuid
from .qt import QtCore
from .qt import QtCore, QtNetwork
from .controller import Controller
@@ -78,6 +78,7 @@ class Link(QtCore.QObject):
self._deleting = False
self._capture_file_path = None
self._capture_file = None
self._network_manager = None
self._response_stream = None
self._capture_compute_id = None
self._initialized = False
@@ -117,12 +118,15 @@ class Link(QtCore.QObject):
else:
self._capture_file = QtCore.QFile(self._capture_file_path)
self._capture_file.open(QtCore.QFile.WriteOnly)
if self._network_manager is None:
self._network_manager = QtNetwork.QNetworkAccessManager(self)
self._response_stream = Controller.instance().get("/projects/{project_id}/links/{link_id}/pcap".format(project_id=self.project().id(), link_id=self._link_id),
None,
showProgress=False,
downloadProgressCallback=self._downloadPcapProgress,
ignoreErrors=True, # If something is wrong avoid disconnect us from server
timeout=None)
timeout=None,
networkManager=self._network_manager)
log.debug("Has successfully started capturing packets on link {} to '{}'".format(self._link_id, self._capture_file_path))
else:
self._response_stream = None

View File

@@ -483,7 +483,7 @@ class LocalConfig(QtCore.QObject):
if os.path.exists(pid_path):
try:
with open(pid_path) as f:
with open(pid_path, encoding="utf-8") as f:
pid = int(f.read())
if pid != my_pid:
try:
@@ -498,9 +498,17 @@ class LocalConfig(QtCore.QObject):
return False
else:
return True
except (OSError, ValueError) as e:
except OSError as e:
log.critical("Can't read pid file %s: %s", pid_path, str(e))
return False
except ValueError as e:
log.warning("Invalid data in pid file %s: %s", pid_path, str(e))
try:
# try removing the file since it contains invalid data
os.remove(pid_path)
except OSError:
log.critical("Can't remove pid file %s", pid_path)
return False
try:
with open(pid_path, 'w+') as f:

View File

@@ -184,9 +184,9 @@ def main():
# catch exceptions to write them in a file
sys.excepthook = exceptionHook
# we only support Python 3 version >= 3.7
if sys.version_info < (3, 7):
raise SystemExit("Python 3.7 or higher is required")
# we only support Python 3 version >= 3.8
if sys.version_info < (3, 8):
raise SystemExit("Python 3.8 or higher is required")
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.5.0"):
raise SystemExit("Requirement is PyQt5 version 5.5.0 or higher, got version {}".format(QtCore.QT_VERSION_STR))

View File

@@ -268,6 +268,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.uiExportDebugInformationAction.triggered.connect(self._exportDebugInformationSlot)
self.uiDoctorAction.triggered.connect(self._doctorSlot)
self.uiAcademyAction.triggered.connect(self._academyActionSlot)
self.uiShortcutsAction.triggered.connect(self._shortcutsActionSlot)
# browsers tool bar connections
self.uiBrowseRoutersAction.triggered.connect(self._browseRoutersActionSlot)
@@ -427,7 +428,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
if not os.path.exists(self._appliance_dir):
directory = Topology.instance().projectsDirPath()
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Import appliance", directory,
"All files (*.*);;GNS3 Appliance (*.gns3appliance *.gns3a)",
"All files (*);;GNS3 Appliance (*.gns3appliance *.gns3a)",
"GNS3 Appliance (*.gns3appliance *.gns3a)")
if path:
self.loadPath(path)
@@ -446,7 +447,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
if self._project_dir is None or not os.path.exists(self._project_dir):
directory = Topology.instance().projectsDirPath()
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open project", directory,
"All files (*.*);;GNS3 Project (*.gns3);;GNS3 Portable Project (*.gns3project *.gns3p);;NET files (*.net)",
"All files (*);;GNS3 Project (*.gns3);;GNS3 Portable Project (*.gns3project *.gns3p);;NET files (*.net)",
"GNS3 Project (*.gns3)")
if path:
self.loadPath(path)
@@ -919,7 +920,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
Slot called when inserting an image on the scene.
"""
# supported image file formats
file_formats = "Image files (*.svg *.bmp *.jpeg *.jpg *.gif *.pbm *.pgm *.png *.ppm *.xbm *.xpm);;All files (*.*)"
file_formats = "Image files (*.svg *.bmp *.jpeg *.jpg *.gif *.pbm *.pgm *.png *.ppm *.xbm *.xpm);;All files (*)"
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Image", self._pictures_dir, file_formats)
if not path:
@@ -979,6 +980,15 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# start and connect to the local server if needed
LocalServer.instance().localServerAutoStartIfRequired()
def _shortcutsActionSlot(self):
shortcuts_text = ""
for action in self.findChildren(QtWidgets.QAction):
shortcut = action.shortcut().toString()
if shortcut:
shortcuts_text += f"{action.toolTip()}: {shortcut}\n"
QtWidgets.QMessageBox.information(self, "Shortcuts", shortcuts_text)
def _aboutQtActionSlot(self):
"""
Slot to display the Qt About dialog.
@@ -1439,7 +1449,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
if not os.path.exists(directory):
directory = Topology.instance().projectsDirPath()
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open portable project", directory,
"All files (*.*);;GNS3 Portable Project (*.gns3project *.gns3p)",
"All files (*);;GNS3 Portable Project (*.gns3project *.gns3p)",
"GNS3 Portable Project (*.gns3project *.gns3p)")
if path:
Topology.instance().importProject(path)

View File

@@ -73,7 +73,7 @@ class Nat(Node):
:returns: symbol path (or resource).
"""
return ":/symbols/cloud.svg"
return ":/symbols/nat.svg"
@staticmethod
def categories():

View File

@@ -42,6 +42,7 @@ class DockerVM(Node):
docker_vm_settings = {"image": "",
"usage": "",
"adapters": DOCKER_CONTAINER_SETTINGS["adapters"],
"mac_address": DOCKER_CONTAINER_SETTINGS["mac_address"],
"custom_adapters": DOCKER_CONTAINER_SETTINGS["custom_adapters"],
"start_command": DOCKER_CONTAINER_SETTINGS["start_command"],
"environment": DOCKER_CONTAINER_SETTINGS["environment"],
@@ -88,6 +89,9 @@ class DockerVM(Node):
port_name=port.name(),
port_description=port.description())
if port.macAddress():
port_info += " MAC address is {mac_address}\n".format(mac_address=port.macAddress())
usage = "\n" + self._settings.get("usage")
return info + port_info + usage

View File

@@ -19,6 +19,8 @@
Configuration page for Docker images.
"""
import re
from gns3.qt import QtWidgets
from gns3.node import Node
from gns3.dialogs.custom_adapters_configuration_dialog import CustomAdaptersConfigurationDialog
@@ -69,15 +71,25 @@ class DockerVMConfigurationPage(QtWidgets.QWidget, Ui_dockerVMConfigPageWidget):
if self._node:
adapters = self._settings["adapters"]
base_mac_address = self._settings["mac_address"]
else:
adapters = self.uiAdapterSpinBox.value()
mac = self.uiMacAddrLineEdit.text()
if mac != ":::::":
if not re.search(r"""^([0-9a-fA-F]{2}[:]){5}[0-9a-fA-F]{2}$""", mac):
QtWidgets.QMessageBox.critical(self, "MAC address", "Invalid MAC address (format required: hh:hh:hh:hh:hh:hh)")
return
else:
base_mac_address = mac
else:
base_mac_address = ""
ports = []
for adapter_number in range(0, adapters):
port_name = "eth{}".format(adapter_number)
ports.append(port_name)
dialog = CustomAdaptersConfigurationDialog(ports, self._custom_adapters, parent=self)
dialog = CustomAdaptersConfigurationDialog(ports, self._custom_adapters, "TAP", {"TAP": "Default"}, base_mac_address, parent=self)
dialog.show()
dialog.exec_()
@@ -150,6 +162,13 @@ class DockerVMConfigurationPage(QtWidgets.QWidget, Ui_dockerVMConfigPageWidget):
self.uiSymbolLineEdit.hide()
self.uiSymbolToolButton.hide()
# load the MAC address setting
self.uiMacAddrLineEdit.setInputMask("HH:HH:HH:HH:HH:HH;_")
if settings["mac_address"]:
self.uiMacAddrLineEdit.setText(settings["mac_address"])
else:
self.uiMacAddrLineEdit.clear()
self.uiUsageTextEdit.setPlainText(settings["usage"])
def _networkConfigEditSlot(self):
@@ -199,6 +218,18 @@ class DockerVMConfigurationPage(QtWidgets.QWidget, Ui_dockerVMConfigPageWidget):
else:
settings["name"] = name
# check and save the MAC address
mac = self.uiMacAddrLineEdit.text()
if mac != ":::::":
if not re.search(r"""^([0-9a-fA-F]{2}[:]){5}[0-9a-fA-F]{2}$""", mac):
QtWidgets.QMessageBox.critical(self, "MAC address", "Invalid MAC address (format required: hh:hh:hh:hh:hh:hh)")
if node:
raise ConfigurationError()
else:
settings["mac_address"] = mac
else:
settings["mac_address"] = None
if not node:
# these are template settings
settings["category"] = self.uiCategoryComboBox.itemData(self.uiCategoryComboBox.currentIndex())

View File

@@ -35,6 +35,7 @@ DOCKER_CONTAINER_SETTINGS = {
"name": "",
"image": "",
"adapters": 1,
"mac_address": "",
"custom_adapters": [],
"environment": "",
"console_type": "telnet",

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>938</width>
<height>872</height>
<width>504</width>
<height>560</height>
</rect>
</property>
<property name="windowTitle">
@@ -103,27 +103,37 @@
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="uiMacAddrLabel">
<property name="text">
<string>Base MAC:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="uiMacAddrLineEdit"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="uiCustomAdaptersLabel">
<property name="text">
<string>Custom adapters:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QPushButton" name="uiCustomAdaptersConfigurationPushButton">
<property name="text">
<string>&amp;Configure custom adapters</string>
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="uiConsoleTypeLabel">
<property name="text">
<string>Console type:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
@@ -166,14 +176,14 @@
</item>
</layout>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QLabel" name="uiConsoleResolutionLabel">
<property name="text">
<string>VNC console resolution:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QComboBox" name="uiConsoleResolutionComboBox">
<item>
<property name="text">
@@ -227,14 +237,14 @@
</item>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>HTTP port in the container:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QSpinBox" name="uiConsoleHttpPortSpinBox">
<property name="minimum">
<number>1</number>
@@ -244,17 +254,17 @@
</property>
</widget>
</item>
<item row="10" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>HTTP path:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QLineEdit" name="uiHttpConsolePathLineEdit"/>
</item>
<item row="11" column="0">
<item row="12" column="0">
<widget class="QLabel" name="uiEnvironmentLabel">
<property name="text">
<string>Environment variables:
@@ -268,17 +278,17 @@
</property>
</widget>
</item>
<item row="11" column="1">
<item row="12" column="1">
<widget class="QTextEdit" name="uiEnvironmentTextEdit"/>
</item>
<item row="12" column="0">
<item row="13" column="0">
<widget class="QLabel" name="uiNetworkConfigLabel">
<property name="text">
<string>Network configuration</string>
</property>
</widget>
</item>
<item row="12" column="1">
<item row="13" column="1">
<widget class="QPushButton" name="uiNetworkConfigEditButton">
<property name="text">
<string>Edit</string>

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/docker/ui/docker_vm_configuration_page.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_dockerVMConfigPageWidget(object):
def setupUi(self, dockerVMConfigPageWidget):
dockerVMConfigPageWidget.setObjectName("dockerVMConfigPageWidget")
dockerVMConfigPageWidget.resize(938, 872)
dockerVMConfigPageWidget.resize(504, 560)
self.verticalLayout = QtWidgets.QVBoxLayout(dockerVMConfigPageWidget)
self.verticalLayout.setObjectName("verticalLayout")
self.uiTabWidget = QtWidgets.QTabWidget(dockerVMConfigPageWidget)
@@ -67,15 +67,21 @@ class Ui_dockerVMConfigPageWidget(object):
self.uiAdapterSpinBox.setMinimum(1)
self.uiAdapterSpinBox.setObjectName("uiAdapterSpinBox")
self.gridLayout.addWidget(self.uiAdapterSpinBox, 5, 1, 1, 1)
self.uiMacAddrLabel = QtWidgets.QLabel(self.tab)
self.uiMacAddrLabel.setObjectName("uiMacAddrLabel")
self.gridLayout.addWidget(self.uiMacAddrLabel, 6, 0, 1, 1)
self.uiMacAddrLineEdit = QtWidgets.QLineEdit(self.tab)
self.uiMacAddrLineEdit.setObjectName("uiMacAddrLineEdit")
self.gridLayout.addWidget(self.uiMacAddrLineEdit, 6, 1, 1, 1)
self.uiCustomAdaptersLabel = QtWidgets.QLabel(self.tab)
self.uiCustomAdaptersLabel.setObjectName("uiCustomAdaptersLabel")
self.gridLayout.addWidget(self.uiCustomAdaptersLabel, 6, 0, 1, 1)
self.gridLayout.addWidget(self.uiCustomAdaptersLabel, 7, 0, 1, 1)
self.uiCustomAdaptersConfigurationPushButton = QtWidgets.QPushButton(self.tab)
self.uiCustomAdaptersConfigurationPushButton.setObjectName("uiCustomAdaptersConfigurationPushButton")
self.gridLayout.addWidget(self.uiCustomAdaptersConfigurationPushButton, 6, 1, 1, 1)
self.gridLayout.addWidget(self.uiCustomAdaptersConfigurationPushButton, 7, 1, 1, 1)
self.uiConsoleTypeLabel = QtWidgets.QLabel(self.tab)
self.uiConsoleTypeLabel.setObjectName("uiConsoleTypeLabel")
self.gridLayout.addWidget(self.uiConsoleTypeLabel, 7, 0, 1, 1)
self.gridLayout.addWidget(self.uiConsoleTypeLabel, 8, 0, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint)
self.horizontalLayout.setObjectName("horizontalLayout")
@@ -90,10 +96,10 @@ class Ui_dockerVMConfigPageWidget(object):
self.uiConsoleAutoStartCheckBox = QtWidgets.QCheckBox(self.tab)
self.uiConsoleAutoStartCheckBox.setObjectName("uiConsoleAutoStartCheckBox")
self.horizontalLayout.addWidget(self.uiConsoleAutoStartCheckBox)
self.gridLayout.addLayout(self.horizontalLayout, 7, 1, 1, 1)
self.gridLayout.addLayout(self.horizontalLayout, 8, 1, 1, 1)
self.uiConsoleResolutionLabel = QtWidgets.QLabel(self.tab)
self.uiConsoleResolutionLabel.setObjectName("uiConsoleResolutionLabel")
self.gridLayout.addWidget(self.uiConsoleResolutionLabel, 8, 0, 1, 1)
self.gridLayout.addWidget(self.uiConsoleResolutionLabel, 9, 0, 1, 1)
self.uiConsoleResolutionComboBox = QtWidgets.QComboBox(self.tab)
self.uiConsoleResolutionComboBox.setObjectName("uiConsoleResolutionComboBox")
self.uiConsoleResolutionComboBox.addItem("")
@@ -106,35 +112,35 @@ class Ui_dockerVMConfigPageWidget(object):
self.uiConsoleResolutionComboBox.addItem("")
self.uiConsoleResolutionComboBox.addItem("")
self.uiConsoleResolutionComboBox.addItem("")
self.gridLayout.addWidget(self.uiConsoleResolutionComboBox, 8, 1, 1, 1)
self.gridLayout.addWidget(self.uiConsoleResolutionComboBox, 9, 1, 1, 1)
self.label = QtWidgets.QLabel(self.tab)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 9, 0, 1, 1)
self.gridLayout.addWidget(self.label, 10, 0, 1, 1)
self.uiConsoleHttpPortSpinBox = QtWidgets.QSpinBox(self.tab)
self.uiConsoleHttpPortSpinBox.setMinimum(1)
self.uiConsoleHttpPortSpinBox.setMaximum(65535)
self.uiConsoleHttpPortSpinBox.setObjectName("uiConsoleHttpPortSpinBox")
self.gridLayout.addWidget(self.uiConsoleHttpPortSpinBox, 9, 1, 1, 1)
self.gridLayout.addWidget(self.uiConsoleHttpPortSpinBox, 10, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(self.tab)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 10, 0, 1, 1)
self.gridLayout.addWidget(self.label_2, 11, 0, 1, 1)
self.uiHttpConsolePathLineEdit = QtWidgets.QLineEdit(self.tab)
self.uiHttpConsolePathLineEdit.setObjectName("uiHttpConsolePathLineEdit")
self.gridLayout.addWidget(self.uiHttpConsolePathLineEdit, 10, 1, 1, 1)
self.gridLayout.addWidget(self.uiHttpConsolePathLineEdit, 11, 1, 1, 1)
self.uiEnvironmentLabel = QtWidgets.QLabel(self.tab)
self.uiEnvironmentLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.uiEnvironmentLabel.setWordWrap(False)
self.uiEnvironmentLabel.setObjectName("uiEnvironmentLabel")
self.gridLayout.addWidget(self.uiEnvironmentLabel, 11, 0, 1, 1)
self.gridLayout.addWidget(self.uiEnvironmentLabel, 12, 0, 1, 1)
self.uiEnvironmentTextEdit = QtWidgets.QTextEdit(self.tab)
self.uiEnvironmentTextEdit.setObjectName("uiEnvironmentTextEdit")
self.gridLayout.addWidget(self.uiEnvironmentTextEdit, 11, 1, 1, 1)
self.gridLayout.addWidget(self.uiEnvironmentTextEdit, 12, 1, 1, 1)
self.uiNetworkConfigLabel = QtWidgets.QLabel(self.tab)
self.uiNetworkConfigLabel.setObjectName("uiNetworkConfigLabel")
self.gridLayout.addWidget(self.uiNetworkConfigLabel, 12, 0, 1, 1)
self.gridLayout.addWidget(self.uiNetworkConfigLabel, 13, 0, 1, 1)
self.uiNetworkConfigEditButton = QtWidgets.QPushButton(self.tab)
self.uiNetworkConfigEditButton.setObjectName("uiNetworkConfigEditButton")
self.gridLayout.addWidget(self.uiNetworkConfigEditButton, 12, 1, 1, 1)
self.gridLayout.addWidget(self.uiNetworkConfigEditButton, 13, 1, 1, 1)
self.uiTabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
@@ -186,6 +192,7 @@ class Ui_dockerVMConfigPageWidget(object):
self.uiSymbolToolButton.setText(_translate("dockerVMConfigPageWidget", "&Browse..."))
self.uiCMDLabel.setText(_translate("dockerVMConfigPageWidget", "Start command:"))
self.uiAdapterLabel.setText(_translate("dockerVMConfigPageWidget", "Adapters:"))
self.uiMacAddrLabel.setText(_translate("dockerVMConfigPageWidget", "Base MAC:"))
self.uiCustomAdaptersLabel.setText(_translate("dockerVMConfigPageWidget", "Custom adapters:"))
self.uiCustomAdaptersConfigurationPushButton.setText(_translate("dockerVMConfigPageWidget", "&Configure custom adapters"))
self.uiConsoleTypeLabel.setText(_translate("dockerVMConfigPageWidget", "Console type:"))

View File

@@ -93,7 +93,7 @@
<string>Docker Virtual Machine</string>
</property>
<property name="subTitle">
<string>Please choose a Docker virtual machine from the list or provide an image name on Docker hub.</string>
<string>Please choose a Docker virtual machine from the list or provide an image name on a Docker repository.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>

View File

@@ -205,7 +205,7 @@ class Ui_DockerVMWizard(object):
self.uiRemoteServersGroupBox.setTitle(_translate("DockerVMWizard", "Remote server"))
self.uiRemoteServersLabel.setText(_translate("DockerVMWizard", "Run on:"))
self.uiImageWizardPage.setTitle(_translate("DockerVMWizard", "Docker Virtual Machine"))
self.uiImageWizardPage.setSubTitle(_translate("DockerVMWizard", "Please choose a Docker virtual machine from the list or provide an image name on Docker hub."))
self.uiImageWizardPage.setSubTitle(_translate("DockerVMWizard", "Please choose a Docker virtual machine from the list or provide an image name on a Docker repository."))
self.uiExistingImageRadioButton.setText(_translate("DockerVMWizard", "Existing image"))
self.uiNewImageRadioButton.setText(_translate("DockerVMWizard", "New image"))
self.uiImageListLabel.setText(_translate("DockerVMWizard", "Image list:"))

View File

@@ -51,7 +51,7 @@ class DynamipsPreferencesPage(QtWidgets.QWidget, Ui_DynamipsPreferencesPageWidge
file_filter = ""
if sys.platform.startswith("win"):
file_filter = "Executable (*.exe);;All files (*.*)"
file_filter = "Executable (*.exe);;All files (*)"
dynamips_path = shutil.which("dynamips")
if sys.platform.startswith("darwin") and dynamips_path is None:

View File

@@ -24,6 +24,7 @@ import re
from gns3.qt import QtCore, QtGui, QtWidgets
from gns3.local_server import LocalServer
from gns3.local_config import LocalConfig
from gns3.dialogs.node_properties_dialog import ConfigurationError
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
from gns3.controller import Controller
@@ -488,7 +489,7 @@ class IOSRouterConfigurationPage(QtWidgets.QWidget, Ui_iosRouterConfigPageWidget
name = self.uiNameLineEdit.text()
if not name:
QtWidgets.QMessageBox.critical(self, "Name", "IOS router name cannot be empty!")
elif node and not node.validateHostname(name):
elif node and not node.validateHostname(name) and not LocalConfig.instance().experimental():
QtWidgets.QMessageBox.critical(self, "Name", "Invalid name detected for IOS router: {}".format(name))
else:
settings["name"] = name

View File

@@ -229,7 +229,7 @@ class IOSRouterPreferencesPage(QtWidgets.QWidget, Ui_IOSRouterPreferencesPageWid
path, _ = QtWidgets.QFileDialog.getOpenFileName(parent,
"Select an IOS image",
cls._default_images_dir,
"All files (*.*);;IOS image (*.bin *.image)",
"All files (*);;IOS image (*.bin *.image)",
"IOS image (*.bin *.image)")
if not path:

View File

@@ -23,6 +23,7 @@ import os
from gns3.qt import QtWidgets
from gns3.local_server import LocalServer
from gns3.local_config import LocalConfig
from gns3.dialogs.node_properties_dialog import ConfigurationError
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
from gns3.node import Node
@@ -245,7 +246,7 @@ class iouDeviceConfigurationPage(QtWidgets.QWidget, Ui_iouDeviceConfigPageWidget
name = self.uiNameLineEdit.text()
if not name:
QtWidgets.QMessageBox.critical(self, "Name", "IOU device name cannot be empty!")
elif node and not node.validateHostname(name):
elif node and not node.validateHostname(name) and not LocalConfig.instance().experimental():
QtWidgets.QMessageBox.critical(self, "Name", "Invalid name detected for IOU device: {}".format(name))
else:
settings["name"] = name

View File

@@ -291,8 +291,8 @@ class IOUDevicePreferencesPage(QtWidgets.QWidget, Ui_IOUDevicePreferencesPageWid
path, _ = QtWidgets.QFileDialog.getOpenFileName(parent,
"Select an IOU image",
cls._default_images_dir,
"All file (*);;IOU image (*.bin *.image)",
"IOU image (*.bin *.image)")
"All file (*);;IOU image (x86_64* i86bi* *.bin *.image *.iol)",
"IOU image (x86_64* i86bi* *.bin *.image *.iol)")
if not path:
return

View File

@@ -94,7 +94,10 @@
<number>32</number>
</property>
<property name="maximum">
<number>65535</number>
<number>2147483647</number>
</property>
<property name="singleStep">
<number>1024</number>
</property>
<property name="value">
<number>256</number>

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/qemu/ui/qemu_vm_configuration_page.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@@ -59,7 +59,8 @@ class Ui_QemuVMConfigPageWidget(object):
self.gridLayout_4.addWidget(self.uiRamLabel, 4, 0, 1, 1)
self.uiRamSpinBox = QtWidgets.QSpinBox(self.uiGeneralSettingsTab)
self.uiRamSpinBox.setMinimum(32)
self.uiRamSpinBox.setMaximum(65535)
self.uiRamSpinBox.setMaximum(2147483647)
self.uiRamSpinBox.setSingleStep(1024)
self.uiRamSpinBox.setProperty("value", 256)
self.uiRamSpinBox.setObjectName("uiRamSpinBox")
self.gridLayout_4.addWidget(self.uiRamSpinBox, 4, 1, 1, 1)

View File

@@ -51,7 +51,7 @@ class TraceNGPreferencesPage(QtWidgets.QWidget, Ui_TraceNGPreferencesPageWidget)
filter = ""
if sys.platform.startswith("win"):
filter = "Executable (*.exe);;All files (*.*)"
filter = "Executable (*.exe);;All files (*)"
traceng_path = shutil.which("traceng")
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select TraceNG", traceng_path, filter)
if not path:

View File

@@ -18,6 +18,7 @@
"""
VirtualBox VM implementation.
"""
import sys
from gns3.node import Node
from gns3.utils.bring_to_front import bring_window_to_front_from_process_name
@@ -100,7 +101,7 @@ class VirtualBoxVM(Node):
Bring the VM window to front.
"""
if self.status() == Node.started:
if self.status() == Node.started and sys.platform.startswith("win"):
# try 2 different window title formats
bring_window_to_front_from_process_name("VirtualBox.exe", title="{} [".format(self._settings["vmname"]))
bring_window_to_front_from_process_name("VirtualBox.exe", title="{} (".format(self._settings["vmname"]))

View File

@@ -19,6 +19,8 @@
VMware VM implementation.
"""
import sys
from gns3.qt import QtCore
from gns3.node import Node
from gns3.utils.bring_to_front import bring_window_to_front_from_process_name
@@ -124,7 +126,7 @@ class VMwareVM(Node):
Bring the VM window to front.
"""
if self.status() == Node.started:
if self.status() == Node.started and sys.platform.startswith("win"):
try:
vmx_pairs = self.module().parseVMwareFile(self.settings()["vmx_path"])
except OSError as e:

View File

@@ -51,7 +51,7 @@ class VPCSPreferencesPage(QtWidgets.QWidget, Ui_VPCSPreferencesPageWidget):
filter = ""
if sys.platform.startswith("win"):
filter = "Executable (*.exe);;All files (*.*)"
filter = "Executable (*.exe);;All files (*)"
vpcs_path = shutil.which("vpcs")
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select VPCS", vpcs_path, filter)
if not path:

View File

@@ -16,8 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import pathlib
import re
import shutil
import subprocess
from gns3.controller import Controller
from gns3.ports.ethernet_port import EthernetPort
@@ -722,6 +725,9 @@ class Node(BaseNode):
if "console_type" in self.settings():
console_type = self.consoleType()
if aux is False and self.bringToFront() is True:
return
if console_type == "telnet":
from .telnet_console import nodeTelnetConsole
nodeTelnetConsole(self, console_port, command)
@@ -740,18 +746,30 @@ class Node(BaseNode):
"""
if self.status() == Node.started:
console_command = self.consoleCommand()
if console_command:
process_name = console_command.split()[0]
if bring_window_to_front_from_process_name(process_name, self.name()):
if sys.platform.startswith("linux"):
wmctrl_path = shutil.which("wmctrl")
if wmctrl_path:
try:
# use wmctrl to raise the window based on the node name (this doesn't work well with window having multiple tabs)
subprocess.run([wmctrl_path, "-Fa", self.name()], check=True, env=os.environ)
return True
except subprocess.CalledProcessError:
log.debug("Could not find window title '{}' to bring it to front".format(self.name()))
except OSError as e:
log.warning("Count not focus on terminal window: '{}'".format(e))
elif sys.platform.startswith("win"):
console_command = self.consoleCommand()
if console_command:
process_name = console_command.split()[0]
if bring_window_to_front_from_process_name(process_name, self.name()):
return True
else:
log.debug("Could not find process name '' and window title '{}' to bring it to front".format(process_name, self.name()))
if bring_window_to_front_from_title(self.name()):
return True
else:
log.debug("Could not find process name '' and window title '{}' to bring it to front".format(process_name, self.name()))
if bring_window_to_front_from_title(self.name()):
return True
else:
log.debug("Could not find window title '{}' to bring it to front".format(self.name()))
log.debug("Could not find window title '{}' to bring it to front".format(self.name()))
return False
def importFile(self, path, source_path):

View File

@@ -195,7 +195,7 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
configuration_file_path = LocalConfig.instance().configFilePath()
directory = os.path.dirname(configuration_file_path)
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Import configuration file", directory, "Configuration file (*.ini *.conf);;All files (*.*)")
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Import configuration file", directory, "Configuration file (*.ini *.conf);;All files (*)")
if not path:
return
@@ -240,7 +240,7 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
configuration_file_path = LocalConfig.instance().configFilePath()
directory = os.path.dirname(configuration_file_path)
path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Export configuration file", directory, "Configuration file (*.ini *.conf);;All files (*.*)")
path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Export configuration file", directory, "Configuration file (*.ini *.conf);;All files (*)")
if not path:
return

View File

@@ -106,7 +106,7 @@ class ServerPreferencesPage(QtWidgets.QWidget, Ui_ServerPreferencesPageWidget):
filter = ""
if sys.platform.startswith("win"):
filter = "Executable (*.exe);;All files (*.*)"
filter = "Executable (*.exe);;All files (*)"
server_path = shutil.which("gns3server")
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select the local server", server_path, filter)
if not path:
@@ -121,7 +121,7 @@ class ServerPreferencesPage(QtWidgets.QWidget, Ui_ServerPreferencesPageWidget):
filter = ""
if sys.platform.startswith("win"):
filter = "Executable (*.exe);;All files (*.*)"
filter = "Executable (*.exe);;All files (*)"
ubridge_path = shutil.which("ubridge")
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select ubridge executable", ubridge_path, filter)

View File

@@ -627,21 +627,24 @@ class Project(QtCore.QObject):
return
# Qt websocket before Qt 5.6 doesn't support auth
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"):
if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0") or LocalConfig.instance().experimental():
path = "/projects/{project_id}/notifications".format(project_id=self._id)
self._notification_stream = Controller.instance().createHTTPQuery("GET", path, self._endListenNotificationCallback,
downloadProgressCallback=self._event_received,
networkManager=self._notification_network_manager,
timeout=None,
showProgress=False,
ignoreErrors=True)
downloadProgressCallback=self._event_received,
networkManager=self._notification_network_manager,
timeout=None,
showProgress=False,
ignoreErrors=True)
url = Controller.instance().getHttpClient().url() + path
log.info("Listening for project notifications on '{}'".format(url))
else:
path = "/projects/{project_id}/notifications/ws".format(project_id=self._id)
self._notification_stream = Controller.instance().httpClient().connectWebSocket(self._websocket, path)
self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
self._notification_stream.error.connect(self._websocket_error)
self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
path = "/projects/{project_id}/notifications/ws".format(project_id=self._id)
self._notification_stream = Controller.instance().httpClient().connectWebSocket(self._websocket, path)
self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
self._notification_stream.error.connect(self._websocket_error)
self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
log.info("Listening for project notifications on '{}'".format(self._notification_stream.requestUrl().toString()))
def _endListenNotificationCallback(self, result, error=False, **kwargs):
"""
@@ -721,11 +724,11 @@ class Project(QtCore.QObject):
self.projectUpdatedCallback(result["event"])
elif result["action"] == "snapshot.restored":
Topology.instance().restoreSnapshot(result["event"]["project_id"])
elif result["action"] == "log.error":
log.error(result["event"]["message"])
elif result["action"] == "log.warning":
log.warning(result["event"]["message"])
elif result["action"] == "log.info":
log.info(result["event"]["message"], extra={"show": True})
elif result["action"] == "log.error" and result["event"].get("message"):
log.error(result["event"].get("message"))
elif result["action"] == "log.warning" and result["event"].get("message"):
log.warning(result["event"].get("message"))
elif result["action"] == "log.info" and result["event"].get("message"):
log.info(result["event"].get("message"), extra={"show": True})
elif result["action"] == "ping":
pass

View File

@@ -146,7 +146,7 @@
},
"image": {
"type": "string",
"title": "Docker image in the Docker Hub"
"title": "Docker image on the Docker repository"
},
"start_command": {
"type": "string",

View File

@@ -7,6 +7,7 @@
"enum": [
"router",
"multilayer_switch",
"switch",
"firewall",
"guest"
]
@@ -720,174 +721,174 @@
"template_properties"
]
}
}
},
"images": {
"type": "array",
"title": "Images for this appliance",
"items": {
"type": "object",
"title": "An image file",
"properties": {
"filename": {
"type": "string",
"title": "Filename"
},
"images": {
"type": "array",
"title": "Images for this appliance",
"items": {
"type": "object",
"title": "An image file",
"properties": {
"filename": {
"type": "string",
"title": "Filename"
},
"version": {
"type": "string",
"title": "Version of the image file"
},
"md5sum": {
"type": "string",
"title": "MD5 cheksum of the image file",
"pattern": "^[a-f0-9]{32}$"
},
"checksum": {
"type": "string",
"title": "checksum of the image file"
},
"checksum_type": {
"title": "checksum type of the image file",
"enum": [
"md5"
]
},
"filesize": {
"type": "integer",
"title": "File size in bytes of the image file"
},
"download_url": {
"type": "string",
"format": "uri",
"title": "Download URL where you can download the image file from a browser"
},
"direct_download_url": {
"type": "string",
"format": "uri",
"title": "Optional. Non authenticated URL to the image file where you can download the image directly"
},
"compression": {
"enum": [
"bzip2",
"gzip",
"lzma",
"xz",
"rar",
"zip",
"7z"
],
"title": "Optional, compression type of direct download URL image."
},
"compression_target": {
"type": "string",
"title": "Optional, file name of the image file inside the compressed file."
}
},
"version": {
"type": "string",
"title": "Version of the image file"
},
"md5sum": {
"type": "string",
"title": "MD5 cheksum of the image file",
"pattern": "^[a-f0-9]{32}$"
},
"checksum": {
"type": "string",
"title": "checksum of the image file"
},
"checksum_type": {
"title": "checksum type of the image file",
"enum": [
"md5"
]
},
"filesize": {
"type": "integer",
"title": "File size in bytes of the image file"
},
"download_url": {
"type": "string",
"format": "uri",
"title": "Download URL where you can download the image file from a browser"
},
"direct_download_url": {
"type": "string",
"format": "uri",
"title": "Optional. Non authenticated URL to the image file where you can download the image directly"
},
"compression": {
"enum": [
"bzip2",
"gzip",
"lzma",
"xz",
"rar",
"zip",
"7z"
],
"title": "Optional, compression type of direct download URL image."
},
"compression_target": {
"type": "string",
"title": "Optional, file name of the image file inside the compressed file."
}
},
"anyOf": [
{
"required": [
"filename",
"version",
"md5sum",
"filesize"
]
},
{
"required": [
"filename",
"version",
"checksum",
"filesize"
]
}
]
}
},
"versions": {
"type": "array",
"title": "Versions of the appliance",
"items": {
"type": "object",
"title": "A version of the appliance",
"properties": {
"name": {
"type": "string",
"title": "Name of the version"
},
"settings": {
"type": "string",
"title": "Template settings to use to run the version"
},
"category": {
"$ref": "#/definitions/categories",
"title": "Category of the version"
},
"installation_instructions": {
"type": "string",
"title": "Optional installation instructions for the version"
},
"usage": {
"type": "string",
"title": "Optional instructions about using the version"
},
"default_username": {
"type": "string",
"title": "Default username for the version"
},
"default_password": {
"type": "string",
"title": "Default password for the version"
},
"symbol": {
"type": "string",
"title": "An optional symbol for the version"
},
"images": {
"type": "object",
"title": "Images used for this version",
"properties": {
"kernel_image": {
"type": "string",
"title": "Kernel image (Qemu only)"
},
"initrd": {
"type": "string",
"title": "Initrd disk image (Qemu only)"
},
"image": {
"type": "string",
"title": "OS image (IOU and Dynamips only)"
},
"bios_image": {
"type": "string",
"title": "Bios image (Qemu only)"
},
"hda_disk_image": {
"type": "string",
"title": "Hda disk image (Qemu only)"
},
"hdb_disk_image": {
"type": "string",
"title": "Hdc disk image (Qemu only)"
},
"hdc_disk_image": {
"type": "string",
"title": "Hdd disk image (Qemu only)"
},
"hdd_disk_image": {
"type": "string",
"title": "Hdd disk image (Qemu only)"
},
"cdrom_image": {
"type": "string",
"title": "cdrom image (Qemu only)"
"anyOf": [
{
"required": [
"filename",
"version",
"md5sum",
"filesize"
]
},
{
"required": [
"filename",
"version",
"checksum",
"filesize"
]
}
]
}
},
"versions": {
"type": "array",
"title": "Versions of the appliance",
"items": {
"type": "object",
"title": "A version of the appliance",
"properties": {
"name": {
"type": "string",
"title": "Name of the version"
},
"settings": {
"type": "string",
"title": "Template settings to use to run the version"
},
"category": {
"$ref": "#/definitions/categories",
"title": "Category of the version"
},
"installation_instructions": {
"type": "string",
"title": "Optional installation instructions for the version"
},
"usage": {
"type": "string",
"title": "Optional instructions about using the version"
},
"default_username": {
"type": "string",
"title": "Default username for the version"
},
"default_password": {
"type": "string",
"title": "Default password for the version"
},
"symbol": {
"type": "string",
"title": "An optional symbol for the version"
},
"images": {
"type": "object",
"title": "Images used for this version",
"properties": {
"kernel_image": {
"type": "string",
"title": "Kernel image (Qemu only)"
},
"initrd": {
"type": "string",
"title": "Initrd disk image (Qemu only)"
},
"image": {
"type": "string",
"title": "OS image (IOU and Dynamips only)"
},
"bios_image": {
"type": "string",
"title": "Bios image (Qemu only)"
},
"hda_disk_image": {
"type": "string",
"title": "Hda disk image (Qemu only)"
},
"hdb_disk_image": {
"type": "string",
"title": "Hdc disk image (Qemu only)"
},
"hdc_disk_image": {
"type": "string",
"title": "Hdd disk image (Qemu only)"
},
"hdd_disk_image": {
"type": "string",
"title": "Hdd disk image (Qemu only)"
},
"cdrom_image": {
"type": "string",
"title": "cdrom image (Qemu only)"
}
}
}
}
},
"required": [
"name"
]
},
"required": [
"name"
]
}
}
},
"required": [

View File

@@ -168,7 +168,7 @@ else:
if sys.platform.startswith("linux"):
distro_name = distro.name()
if distro_name == "Debian" or distro_name == "Ubuntu" or distro_name == "LinuxMint":
if distro_name == "Debian" or distro_name == "Ubuntu" or distro_name == "Linux Mint":
if shutil.which("mate-terminal"):
DEFAULT_TELNET_CONSOLE_COMMAND = PRECONFIGURED_TELNET_CONSOLE_COMMANDS["Mate Terminal"]
else:
@@ -206,7 +206,8 @@ else:
'TightVNC': 'vncviewer {host}:{port}',
'Vinagre': 'vinagre {host}::{port}',
'gvncviewer': 'gvncviewer {host}:{display}',
'Remote Viewer': 'remote-viewer vnc://{host}:{port}'
'Remote Viewer': 'remote-viewer vnc://{host}:{port}',
'KRDC': 'krdc vnc://{host}:{port}'
}
# default VNC console command on other systems
@@ -287,7 +288,6 @@ GENERAL_SETTINGS = {
"check_for_update": True,
"overlay_notifications": True,
"experimental_features": False,
"stats_visitor_id": str(uuid.uuid4()), # An anonymous id for stats
"last_check_for_update": 0,
"telnet_console_command": DEFAULT_TELNET_CONSOLE_COMMAND,
"vnc_console_command": DEFAULT_VNC_CONSOLE_COMMAND,

View File

@@ -47,6 +47,10 @@ class Style:
Sets the legacy GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
self._mw.setStyleSheet("")
self._mw.uiNewProjectAction.setIcon(QtGui.QIcon(":/icons/new-project.svg"))
self._mw.uiOpenProjectAction.setIcon(QtGui.QIcon(":/icons/open.svg"))
@@ -99,6 +103,10 @@ class Style:
Sets the classic GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
self._mw.setStyleSheet("")
self._mw.uiNewProjectAction.setIcon(self._getStyleIcon(":/classic_icons/new-project.svg", ":/classic_icons/new-project-hover.svg"))
self._mw.uiOpenProjectAction.setIcon(self._getStyleIcon(":/classic_icons/open.svg", ":/classic_icons/open-hover.svg"))
@@ -155,6 +163,10 @@ class Style:
Sets the charcoal GUI style.
"""
graphics_view = self._mw.uiGraphicsView
if hasattr(graphics_view, 'resetGridColors'):
graphics_view.resetGridColors()
style_file = QtCore.QFile(":/styles/charcoal.css")
style_file.open(QtCore.QFile.ReadOnly)
style = QtCore.QTextStream(style_file).readAll()

View File

@@ -26,6 +26,7 @@ import sys
import shlex
import subprocess
import psutil
import shutil
from .main_window import MainWindow
from .controller import Controller
@@ -102,7 +103,16 @@ class ConsoleThread(QtCore.QThread):
# inject gnome-terminal environment variables
if "GNOME_TERMINAL_SERVICE" not in env or "GNOME_TERMINAL_SCREEN" not in env:
env.update(gnome_terminal_env())
subprocess.Popen(args, env=env)
proc = subprocess.Popen(args, env=env)
if sys.platform.startswith("linux"):
wmctrl_path = shutil.which("wmctrl")
if wmctrl_path:
proc.wait() # wait for the terminal to open
try:
# use wmctrl to raise the window based on the node name
subprocess.run([wmctrl_path, "-Fa", self._name], env=os.environ)
except OSError as e:
self.consoleError.emit("Count not focus on terminal window: '{}'".format(e))
def run(self):

View File

@@ -255,7 +255,7 @@ class Topology(QtCore.QObject):
if self._project:
self._project.project_creation_error_signal.disconnect(self._projectCreationErrorSlot)
self.setProject(None)
QtWidgets.QMessageBox.critical(self._main_window, "New project", message)
QtWidgets.QMessageBox.critical(self._main_window, "Project", message)
def exportProject(self):
if self._project is None:

View File

@@ -9,16 +9,10 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>147</height>
<width>460</width>
<height>142</height>
</rect>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>147</height>
</size>
</property>
<property name="windowTitle">
<string>Packet capture</string>
</property>

View File

@@ -2,19 +2,20 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/capture_dialog.ui'
#
# Created: Mon May 30 21:49:29 2016
# by: PyQt5 UI code generator 5.2.1
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING! All changes made in this file will be lost!
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_CaptureDialog(object):
def setupUi(self, CaptureDialog):
CaptureDialog.setObjectName("CaptureDialog")
CaptureDialog.setWindowModality(QtCore.Qt.WindowModal)
CaptureDialog.resize(500, 147)
CaptureDialog.setMaximumSize(QtCore.QSize(500, 147))
CaptureDialog.resize(460, 142)
CaptureDialog.setModal(False)
self.gridLayout = QtWidgets.QGridLayout(CaptureDialog)
self.gridLayout.setObjectName("gridLayout")
@@ -73,5 +74,4 @@ class Ui_CaptureDialog(object):
self.uiLinkTypeLabel.setText(_translate("CaptureDialog", "Link type:"))
self.uiFileNameLabel.setText(_translate("CaptureDialog", "File name:"))
self.uiStartCommandCheckBox.setText(_translate("CaptureDialog", "Start the capture visualization program"))
from . import resources_rc

View File

@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>900</width>
<height>600</height>
<width>602</width>
<height>367</height>
</rect>
</property>
<property name="windowTitle">
@@ -27,19 +27,6 @@
<string>Please select the location, whether to include base images or not and the compression type.</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="uiPathLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -54,6 +41,26 @@
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="uiResetMacAddressesCheckBox">
<property name="text">
<string>&amp;Reset MAC addresses</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="uiPathLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="uiCompressionLabel">
<property name="text">
@@ -61,6 +68,19 @@
</property>
</widget>
</item>
<item row="7" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>247</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="uiCompressionComboBox"/>
</item>
@@ -78,23 +98,10 @@
</property>
</widget>
</item>
<item row="6" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>247</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="uiResetMacAddressesCheckBox">
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="uiKeepComputeIdsCheckBox">
<property name="text">
<string>&amp;Reset MAC addresses</string>
<string>&amp;Keep the original compute IDs</string>
</property>
</widget>
</item>

View File

@@ -2,9 +2,10 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/export_project_wizard.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING! All changes made in this file will be lost!
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
@@ -14,20 +15,12 @@ class Ui_ExportProjectWizard(object):
def setupUi(self, ExportProjectWizard):
ExportProjectWizard.setObjectName("ExportProjectWizard")
ExportProjectWizard.setWindowModality(QtCore.Qt.ApplicationModal)
ExportProjectWizard.resize(900, 600)
ExportProjectWizard.resize(602, 367)
ExportProjectWizard.setOptions(QtWidgets.QWizard.HaveHelpButton)
self.uiExportOptionsWizardPage = QtWidgets.QWizardPage()
self.uiExportOptionsWizardPage.setObjectName("uiExportOptionsWizardPage")
self.gridLayout = QtWidgets.QGridLayout(self.uiExportOptionsWizardPage)
self.gridLayout.setObjectName("gridLayout")
self.uiPathLabel = QtWidgets.QLabel(self.uiExportOptionsWizardPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiPathLabel.sizePolicy().hasHeightForWidth())
self.uiPathLabel.setSizePolicy(sizePolicy)
self.uiPathLabel.setObjectName("uiPathLabel")
self.gridLayout.addWidget(self.uiPathLabel, 0, 0, 1, 1)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.uiPathLineEdit = QtWidgets.QLineEdit(self.uiExportOptionsWizardPage)
@@ -37,9 +30,22 @@ class Ui_ExportProjectWizard(object):
self.uiPathBrowserToolButton.setObjectName("uiPathBrowserToolButton")
self.horizontalLayout_3.addWidget(self.uiPathBrowserToolButton)
self.gridLayout.addLayout(self.horizontalLayout_3, 0, 1, 1, 2)
self.uiResetMacAddressesCheckBox = QtWidgets.QCheckBox(self.uiExportOptionsWizardPage)
self.uiResetMacAddressesCheckBox.setObjectName("uiResetMacAddressesCheckBox")
self.gridLayout.addWidget(self.uiResetMacAddressesCheckBox, 5, 0, 1, 2)
self.uiPathLabel = QtWidgets.QLabel(self.uiExportOptionsWizardPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiPathLabel.sizePolicy().hasHeightForWidth())
self.uiPathLabel.setSizePolicy(sizePolicy)
self.uiPathLabel.setObjectName("uiPathLabel")
self.gridLayout.addWidget(self.uiPathLabel, 0, 0, 1, 1)
self.uiCompressionLabel = QtWidgets.QLabel(self.uiExportOptionsWizardPage)
self.uiCompressionLabel.setObjectName("uiCompressionLabel")
self.gridLayout.addWidget(self.uiCompressionLabel, 1, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(20, 247, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem, 7, 2, 1, 1)
self.uiCompressionComboBox = QtWidgets.QComboBox(self.uiExportOptionsWizardPage)
self.uiCompressionComboBox.setObjectName("uiCompressionComboBox")
self.gridLayout.addWidget(self.uiCompressionComboBox, 1, 1, 1, 2)
@@ -49,11 +55,9 @@ class Ui_ExportProjectWizard(object):
self.uiIncludeSnapshotsCheckBox = QtWidgets.QCheckBox(self.uiExportOptionsWizardPage)
self.uiIncludeSnapshotsCheckBox.setObjectName("uiIncludeSnapshotsCheckBox")
self.gridLayout.addWidget(self.uiIncludeSnapshotsCheckBox, 4, 0, 1, 2)
spacerItem = QtWidgets.QSpacerItem(20, 247, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem, 6, 2, 1, 1)
self.uiResetMacAddressesCheckBox = QtWidgets.QCheckBox(self.uiExportOptionsWizardPage)
self.uiResetMacAddressesCheckBox.setObjectName("uiResetMacAddressesCheckBox")
self.gridLayout.addWidget(self.uiResetMacAddressesCheckBox, 5, 0, 1, 2)
self.uiKeepComputeIdsCheckBox = QtWidgets.QCheckBox(self.uiExportOptionsWizardPage)
self.uiKeepComputeIdsCheckBox.setObjectName("uiKeepComputeIdsCheckBox")
self.gridLayout.addWidget(self.uiKeepComputeIdsCheckBox, 6, 0, 1, 3)
ExportProjectWizard.addPage(self.uiExportOptionsWizardPage)
self.uiProjectReadmeWizardPage = QtWidgets.QWizardPage()
self.uiProjectReadmeWizardPage.setObjectName("uiProjectReadmeWizardPage")
@@ -72,12 +76,13 @@ class Ui_ExportProjectWizard(object):
ExportProjectWizard.setWindowTitle(_translate("ExportProjectWizard", "Export project"))
self.uiExportOptionsWizardPage.setTitle(_translate("ExportProjectWizard", "Export project"))
self.uiExportOptionsWizardPage.setSubTitle(_translate("ExportProjectWizard", "Please select the location, whether to include base images or not and the compression type."))
self.uiPathLabel.setText(_translate("ExportProjectWizard", "Path:"))
self.uiPathBrowserToolButton.setText(_translate("ExportProjectWizard", "Browse..."))
self.uiResetMacAddressesCheckBox.setText(_translate("ExportProjectWizard", "&Reset MAC addresses"))
self.uiPathLabel.setText(_translate("ExportProjectWizard", "Path:"))
self.uiCompressionLabel.setText(_translate("ExportProjectWizard", "Compression:"))
self.uiIncludeImagesCheckBox.setText(_translate("ExportProjectWizard", "&Include base images"))
self.uiIncludeSnapshotsCheckBox.setText(_translate("ExportProjectWizard", "&Include snapshots"))
self.uiResetMacAddressesCheckBox.setText(_translate("ExportProjectWizard", "&Reset MAC addresses"))
self.uiKeepComputeIdsCheckBox.setText(_translate("ExportProjectWizard", "&Keep the original compute IDs"))
self.uiProjectReadmeWizardPage.setTitle(_translate("ExportProjectWizard", "Readme file"))
self.uiProjectReadmeWizardPage.setSubTitle(_translate("ExportProjectWizard", "Write a summary of the project."))
self.uiReadmeTextEdit.setHtml(_translate("ExportProjectWizard", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"

View File

@@ -1012,7 +1012,7 @@
<item>
<widget class="QCheckBox" name="uiExperimentalFeaturesCheckBox">
<property name="text">
<string>Enable experimental features (dangerous, restart required)</string>
<string>Enable experimental features</string>
</property>
</widget>
</item>

View File

@@ -598,7 +598,7 @@ class Ui_GeneralPreferencesPageWidget(object):
self.uiCheckForUpdateCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Automatically check for update"))
self.uiCrashReportCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Send anonymous crash reports"))
self.uiOverlayNotificationsCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Display error, warning and info in an overlay popup"))
self.uiExperimentalFeaturesCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Enable experimental features (dangerous, restart required)"))
self.uiExperimentalFeaturesCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Enable experimental features"))
self.uiHdpiCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Enable HDPI mode (this may crash on Linux, restart required)"))
self.uiMultiProfilesCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Request for profile settings at application startup"))
self.uiDirectFileUpload.setToolTip(_translate("GeneralPreferencesPageWidget", "Experimental, requires computes visibility from GUI network"))

View File

@@ -112,6 +112,7 @@ background-none;
<addaction name="uiAcademyAction"/>
<addaction name="uiDoctorAction"/>
<addaction name="uiExportDebugInformationAction"/>
<addaction name="uiShortcutsAction"/>
<addaction name="uiAboutQtAction"/>
<addaction name="uiAboutAction"/>
</widget>
@@ -644,6 +645,9 @@ background-none;
<property name="statusTip">
<string>Start/Resume all devices</string>
</property>
<property name="shortcut">
<string>Ctrl+B</string>
</property>
</action>
<action name="uiStopAllAction">
<property name="enabled">
@@ -663,6 +667,9 @@ background-none;
<property name="statusTip">
<string>Stop all devices</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
</action>
<action name="uiConsoleAllAction">
<property name="enabled">
@@ -797,6 +804,9 @@ background-none;
<property name="statusTip">
<string>Suspend all devices</string>
</property>
<property name="shortcut">
<string>Ctrl+J</string>
</property>
</action>
<action name="uiAddNoteAction">
<property name="checkable">
@@ -1142,11 +1152,17 @@ background-none;
<property name="statusTip">
<string>Add a link</string>
</property>
<property name="shortcut">
<string>Ctrl+L</string>
</property>
</action>
<action name="uiFitInViewAction">
<property name="text">
<string>Fit in view</string>
</property>
<property name="shortcut">
<string>Ctrl+1</string>
</property>
</action>
<action name="uiActionFullscreen">
<property name="text">
@@ -1306,6 +1322,11 @@ background-none;
<string>Reset GUI state</string>
</property>
</action>
<action name="uiShortcutsAction">
<property name="text">
<string>&amp;Shortcuts</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/main_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@@ -455,6 +455,8 @@ class Ui_MainWindow(object):
self.uiResetConsoleAllAction.setObjectName("uiResetConsoleAllAction")
self.uiResetGUIStateAction = QtWidgets.QAction(MainWindow)
self.uiResetGUIStateAction.setObjectName("uiResetGUIStateAction")
self.uiShortcutsAction = QtWidgets.QAction(MainWindow)
self.uiShortcutsAction.setObjectName("uiShortcutsAction")
self.uiEditMenu.addAction(self.uiSelectAllAction)
self.uiEditMenu.addAction(self.uiSelectNoneAction)
self.uiEditMenu.addSeparator()
@@ -479,6 +481,7 @@ class Ui_MainWindow(object):
self.uiHelpMenu.addAction(self.uiAcademyAction)
self.uiHelpMenu.addAction(self.uiDoctorAction)
self.uiHelpMenu.addAction(self.uiExportDebugInformationAction)
self.uiHelpMenu.addAction(self.uiShortcutsAction)
self.uiHelpMenu.addAction(self.uiAboutQtAction)
self.uiHelpMenu.addAction(self.uiAboutAction)
self.uiViewMenu.addAction(self.uiActionFullscreen)
@@ -604,9 +607,11 @@ class Ui_MainWindow(object):
self.uiStartAllAction.setText(_translate("MainWindow", "Start/Resume all nodes"))
self.uiStartAllAction.setToolTip(_translate("MainWindow", "Start/Resume all nodes"))
self.uiStartAllAction.setStatusTip(_translate("MainWindow", "Start/Resume all devices"))
self.uiStartAllAction.setShortcut(_translate("MainWindow", "Ctrl+B"))
self.uiStopAllAction.setText(_translate("MainWindow", "Stop all nodes"))
self.uiStopAllAction.setToolTip(_translate("MainWindow", "Stop all nodes"))
self.uiStopAllAction.setStatusTip(_translate("MainWindow", "Stop all devices"))
self.uiStopAllAction.setShortcut(_translate("MainWindow", "Ctrl+E"))
self.uiConsoleAllAction.setText(_translate("MainWindow", "Console connect to all nodes"))
self.uiConsoleAllAction.setToolTip(_translate("MainWindow", "Console connect to all nodes"))
self.uiConsoleAllAction.setStatusTip(_translate("MainWindow", "Console to all devices"))
@@ -635,6 +640,7 @@ class Ui_MainWindow(object):
self.uiSuspendAllAction.setText(_translate("MainWindow", "Suspend all nodes"))
self.uiSuspendAllAction.setToolTip(_translate("MainWindow", "Suspend all nodes"))
self.uiSuspendAllAction.setStatusTip(_translate("MainWindow", "Suspend all devices"))
self.uiSuspendAllAction.setShortcut(_translate("MainWindow", "Ctrl+J"))
self.uiAddNoteAction.setText(_translate("MainWindow", "Add note"))
self.uiAddNoteAction.setToolTip(_translate("MainWindow", "Add a note"))
self.uiAddNoteAction.setStatusTip(_translate("MainWindow", "Add a note"))
@@ -701,7 +707,9 @@ class Ui_MainWindow(object):
self.uiAddLinkAction.setText(_translate("MainWindow", "Add a link"))
self.uiAddLinkAction.setToolTip(_translate("MainWindow", "Add a link"))
self.uiAddLinkAction.setStatusTip(_translate("MainWindow", "Add a link"))
self.uiAddLinkAction.setShortcut(_translate("MainWindow", "Ctrl+L"))
self.uiFitInViewAction.setText(_translate("MainWindow", "Fit in view"))
self.uiFitInViewAction.setShortcut(_translate("MainWindow", "Ctrl+1"))
self.uiActionFullscreen.setText(_translate("MainWindow", "Fullscreen"))
self.uiActionFullscreen.setShortcut(_translate("MainWindow", "Ctrl+F"))
self.uiSetupWizard.setText(_translate("MainWindow", "&Setup Wizard"))
@@ -724,6 +732,7 @@ class Ui_MainWindow(object):
self.uiResetDocksAction.setText(_translate("MainWindow", "Reset docks"))
self.uiResetConsoleAllAction.setText(_translate("MainWindow", "Reset all console connections"))
self.uiResetGUIStateAction.setText(_translate("MainWindow", "Reset GUI state"))
self.uiShortcutsAction.setText(_translate("MainWindow", "&Shortcuts"))
from ..compute_summary_view import ComputeSummaryView
from ..console_view import ConsoleView
from ..graphics_view import GraphicsView

View File

@@ -28,21 +28,24 @@ class ExportProjectWorker(QtCore.QObject):
finished = QtCore.pyqtSignal()
updated = QtCore.pyqtSignal(int)
def __init__(self, project, path, include_images, include_snapshots, reset_mac_addresses, compression):
def __init__(self, project, path, include_images, include_snapshots, reset_mac_addresses, keep_compute_ids, compression):
super().__init__()
self._project = project
self._path = path
self._include_images = include_images
self._include_snapshots = include_snapshots
self._reset_mac_addresses = reset_mac_addresses
self._path = path
self._keep_compute_ids = keep_compute_ids
self._compression = compression
def run(self):
if self._project:
self._project.get("/export?include_images={}&include_snapshots={}&reset_mac_addresses={}&compression={}".format(self._include_images, self._include_snapshots, self._reset_mac_addresses, self._compression),
self._exportReceived,
downloadProgressCallback=self._downloadFileProgress,
timeout=None)
self._project.get(
"/export?include_images={}&include_snapshots={}&reset_mac_addresses={}&keep_compute_ids={}&compression={}".format(self._include_images, self._include_snapshots, self._reset_mac_addresses, self._keep_compute_ids, self._compression),
self._exportReceived,
downloadProgressCallback=self._downloadFileProgress,
timeout=None
)
def _exportReceived(self, content, error=False, server=None, context={}, **kwargs):
if error:

View File

@@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "2.2.46"
__version_info__ = (2, 2, 46, 0)
__version__ = "2.2.54"
__version_info__ = (2, 2, 54, 0)
if "dev" in __version__:
try:

View File

@@ -1,5 +1,3 @@
-rrequirements.txt
PyQt5-Qt5==5.15.2
PyQt5-sip==12.12.2
PyQt5==5.15.9
PyQt5==5.15.11

View File

@@ -1,7 +1,6 @@
jsonschema>=4.17.3,<4.18; python_version >= '3.7' # v4.17.3 is the last version to support Python 3.7
sentry-sdk==1.39.2,<1.40
psutil==5.9.8
jsonschema>=4.23,<4.24
sentry-sdk>=2.26.1,<2.27 # optional dependency
psutil>=7.0.0
distro>=1.9.0
truststore>=0.8.0; python_version >= '3.10'
truststore>=0.10.0; python_version >= '3.10'
importlib-resources>=1.3; python_version < '3.9'
setuptools>=60.8.1

207
resources/symbols/nat.svg Normal file
View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
width="4.5009999cm"
height="2.0009999cm"
viewBox="0.2 0.149 4.7 2.151"
id="svg2"
sodipodi:docname="nat.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="namedview30"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="cm"
showgrid="false"
inkscape:lockguides="true"
inkscape:zoom="5.7444245"
inkscape:cx="75.725602"
inkscape:cy="42.040765"
inkscape:window-width="1658"
inkscape:window-height="1016"
inkscape:window-x="70"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<metadata
id="metadata57">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Jeremy Grossmann</dc:title>
</cc:Agent>
</dc:creator>
<dc:publisher>
<cc:Agent>
<dc:title>GNS-3</dc:title>
</cc:Agent>
</dc:publisher>
<dc:description>Created for the GNS-3 project (www.gns3.net)</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
<cc:requires
rdf:resource="http://web.resource.org/cc/SourceCode" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs55" />
<path
d="m 3.7599791,0.61531938 -10e-4,-0.021 -0.003,-0.021 -0.007,-0.02 -0.009,-0.02 -0.011,-0.021 -0.013,-0.02 -0.017,-0.02 -0.018,-0.019 -0.021,-0.019 -0.023,-0.019 -0.024,-0.017 -0.028,-0.017 -0.03,-0.017 -0.031,-0.017 -0.033,-0.015 -0.035,-0.015 -0.037,-0.014 -0.038,-0.013 -0.04,-0.012 -0.041,-0.011 -0.043,-0.011 -0.045,-0.01 -0.044,-0.008 -0.046,-0.008 -0.048,-0.007 -0.047,-0.006 -0.048,-0.004 -0.049,-0.004 -0.05,-0.003 -0.049,-0.001 -0.049,-0.001 0,0 -0.05,0.001 -0.049,0.001 -0.05,0.003 -0.048,0.004 -0.049,0.004 -0.047,0.006 -0.047,0.007 -0.046,0.008 -0.045,0.008 -0.043,0.01 -0.043,0.011 -0.042,0.011 -0.04,0.012 -0.039,0.013 -0.036,0.014 -0.035,0.015 -0.033,0.015 -0.032,0.017 -0.029,0.017 -0.027,0.017 -0.026,0.017 -0.023,0.019 -0.02,0.019 -0.019,0.019 -0.016,0.02 -0.014,0.02 -0.01,0.021 -0.009,0.02 -0.006,0.02 -0.005,0.021 -0.001,0.021 0,0 0.001,0.02 0.005,0.022 0.006,0.02 0.009,0.021 0.01,0.02 0.014,0.019 0.016,0.02 0.019,0.02 0.02,0.019 0.023,0.018 0.026,0.018 0.027,0.017 0.029,0.017 0.032,0.016 0.033,0.016 0.035,0.014 0.036,0.014 0.039,0.013 0.04,0.013 0.042,0.011 0.043,0.01 0.043,0.011 0.045,0.008 0.046,0.007 0.047,0.007 0.047,0.006 0.049,0.005 0.048,0.003 0.05,0.003 0.049,10e-4 0.05,0.001 0,0 0.049,-0.001 0.049,-10e-4 0.05,-0.003 0.049,-0.003 0.048,-0.005 0.047,-0.006 0.048,-0.007 0.046,-0.007 0.044,-0.008 0.045,-0.011 0.043,-0.01 0.041,-0.011 0.04,-0.013 0.038,-0.013 0.037,-0.014 0.035,-0.014 0.033,-0.016 0.031,-0.016 0.03,-0.017 0.028,-0.017 0.024,-0.018 0.023,-0.018 0.021,-0.019 0.018,-0.02 0.017,-0.02 0.013,-0.019 0.011,-0.02 0.009,-0.021 0.007,-0.02 0.003,-0.022 10e-4,-0.02"
id="path4"
style="fill:#ffffff;stroke:none" />
<path
d="m 2.2259791,0.83031938 -10e-4,-0.021 -0.003,-0.02 -0.004,-0.021 -0.007,-0.02 -0.009,-0.021 -0.01,-0.02 -0.012,-0.02 -0.015,-0.018 -0.015,-0.019 -0.018,-0.019 -0.019,-0.018 -0.021,-0.017 -0.022,-0.017 -0.024,-0.016 -0.026,-0.015 -0.026,-0.015 -0.029,-0.014 -0.029,-0.013 -0.031,-0.013 -0.032,-0.011 -0.032,-0.011 -0.034,-0.009 -0.035,-0.009 -0.035,-0.008 -0.036,-0.006 -0.036,-0.006 -0.037,-0.005 -0.038,-0.004 -0.037,-0.002 -0.039,-0.002 -0.038,0 0,0 -0.037,0 -0.038,0.002 -0.038,0.002 -0.038,0.004 -0.037,0.005 -0.036,0.006 -0.036,0.006 -0.035,0.008 -0.035,0.009 -0.033,0.009 -0.033,0.011 -0.032,0.011 -0.03,0.013 -0.03,0.013 -0.028,0.014 -0.027,0.015 -0.025,0.015 -0.024,0.016 -0.023,0.017 -0.021,0.017 -0.018,0.018 -0.019,0.019 -0.015,0.019 -0.015,0.018 -0.012,0.02 -0.011,0.02 -0.008,0.021 -0.007,0.02 -0.005,0.021 -0.002,0.02 -0.001,0.021 0,0 0.001,0.021 0.002,0.021 0.005,0.02 0.007,0.021 0.008,0.02 0.011,0.02 0.012,0.02 0.015,0.019 0.015,0.019 0.019,0.018 0.018,0.018 0.021,0.018 0.023,0.016 0.024,0.016 0.025,0.016 0.027,0.014 0.028,0.015 0.03,0.012 0.03,0.013 0.032,0.012 0.033,0.01 0.033,0.01 0.035,0.008 0.035,0.008 0.036,0.007 0.036,0.005 0.037,0.005 0.038,0.004 0.038,0.002 0.038,0.002 0.037,0 0,0 0.038,0 0.039,-0.002 0.037,-0.002 0.038,-0.004 0.037,-0.005 0.036,-0.005 0.036,-0.007 0.035,-0.008 0.035,-0.008 0.034,-0.01 0.032,-0.01 0.032,-0.012 0.031,-0.013 0.029,-0.012 0.029,-0.015 0.026,-0.014 0.026,-0.016 0.024,-0.016 0.022,-0.016 0.021,-0.018 0.019,-0.018 0.018,-0.018 0.015,-0.019 0.015,-0.019 0.012,-0.02 0.01,-0.02 0.009,-0.02 0.007,-0.021 0.004,-0.02 0.003,-0.021 10e-4,-0.021"
id="path6"
style="fill:#ffffff;stroke:none" />
<path
d="m 1.2809791,1.2493194 -10e-4,-0.017 -0.001,-0.017 -0.004,-0.017 -0.005,-0.017 -0.005,-0.016 -0.007,-0.016 -0.009,-0.016 -0.009,-0.017 -0.011,-0.015 -0.012,-0.015 -0.012,-0.014 -0.014,-0.015 -0.015,-0.014 -0.017,-0.012 -0.017,-0.013 -0.018,-0.012 -0.019,-0.011 -0.02,-0.011 -0.02,-0.01 -0.022,-0.01 -0.022,-0.008 -0.023,-0.008 -0.023,-0.007 -0.024,-0.006 -0.024,-0.006 -0.025,-0.004 -0.024,-0.005 -0.026,-0.002 -0.025,-0.002 -0.025,-0.002 -0.026,0 0,0 -0.026,0 -0.026,0.002 -0.025,0.002 -0.025,0.002 -0.024,0.005 -0.026,0.004 -0.024,0.006 -0.023,0.006 -0.023,0.007 -0.023,0.008 -0.022,0.008 -0.022,0.01 -0.021,0.01 -0.019,0.011 -0.019,0.011 -0.018,0.012 -0.017,0.013 -0.017,0.012 -0.015,0.014 -0.014,0.015 -0.013,0.014 -0.011,0.015 -0.011,0.015 -0.01,0.017 -0.008,0.016 -0.007,0.016 -0.006,0.016 -0.004,0.017 -0.004,0.017 -0.002,0.017 0,0.017 0,0 0,0.016 0.002,0.018 0.004,0.016 0.004,0.017 0.006,0.017 0.007,0.016 0.008,0.016 0.01,0.015 0.011,0.016 0.011,0.015 0.013,0.015 0.014,0.014 0.015,0.014 0.017,0.013 0.017,0.012 0.018,0.012 0.019,0.012 0.019,0.01 0.021,0.01 0.022,0.01 0.022,0.008 0.023,0.008 0.023,0.007 0.023,0.007 0.024,0.005 0.026,0.005 0.024,0.004 0.025,0.003 0.025,10e-4 0.026,0.002 0.026,0.001 0,0 0.026,-0.001 0.025,-0.002 0.025,-10e-4 0.026,-0.003 0.024,-0.004 0.025,-0.005 0.024,-0.005 0.024,-0.007 0.023,-0.007 0.023,-0.008 0.022,-0.008 0.022,-0.01 0.02,-0.01 0.02,-0.01 0.019,-0.012 0.018,-0.012 0.017,-0.012 0.017,-0.013 0.015,-0.014 0.014,-0.014 0.012,-0.015 0.012,-0.015 0.011,-0.016 0.009,-0.015 0.009,-0.016 0.007,-0.016 0.005,-0.017 0.005,-0.017 0.004,-0.016 0.001,-0.018 10e-4,-0.016"
id="path8"
style="fill:#ffffff;stroke:none" />
<path
d="m 2.0969791,1.5713194 -0.002,-0.018 -0.003,-0.018 -0.004,-0.019 -0.007,-0.018 -0.009,-0.017 -0.01,-0.018 -0.013,-0.017 -0.015,-0.018 -0.016,-0.016 -0.017,-0.016 -0.019,-0.016 -0.021,-0.016 -0.024,-0.015 -0.024,-0.014 -0.026,-0.013 -0.027,-0.013 -0.028,-0.012 -0.031,-0.012 -0.03,-0.011 -0.032,-0.01 -0.034,-0.009 -0.035,-0.009 -0.034,-0.008 -0.036,-0.007 -0.036,-0.005 -0.038,-0.006 -0.037,-0.004 -0.038,-0.003 -0.038,-0.002 -0.039,-0.002 -0.038,0 0,0 -0.038,0 -0.039,0.002 -0.039,0.002 -0.038,0.003 -0.037,0.004 -0.037,0.006 -0.037,0.005 -0.035,0.007 -0.035,0.008 -0.034,0.009 -0.034,0.009 -0.032,0.01 -0.031,0.011 -0.03,0.012 -0.029,0.012 -0.027,0.013 -0.026,0.013 -0.024,0.014 -0.023,0.015 -0.021,0.016 -0.019,0.016 -0.018,0.016 -0.017,0.016 -0.014,0.018 -0.012,0.017 -0.01,0.018 -0.01,0.017 -0.006,0.018 -0.005,0.019 -0.003,0.018 -0.001,0.018 0,0 0.001,0.019 0.003,0.018 0.005,0.018 0.006,0.018 0.01,0.019 0.01,0.017 0.012,0.017 0.014,0.018 0.017,0.016 0.018,0.016 0.019,0.016 0.021,0.015 0.023,0.015 0.024,0.015 0.026,0.014 0.027,0.012 0.029,0.013 0.03,0.011 0.031,0.011 0.032,0.01 0.034,0.01 0.034,0.008 0.035,0.008 0.035,0.006 0.037,0.007 0.037,0.004 0.037,0.005 0.038,0.003 0.039,0.003 0.039,10e-4 0.038,0 0,0 0.038,0 0.039,-10e-4 0.038,-0.003 0.038,-0.003 0.037,-0.005 0.038,-0.004 0.036,-0.007 0.036,-0.006 0.034,-0.008 0.035,-0.008 0.034,-0.01 0.032,-0.01 0.03,-0.011 0.031,-0.011 0.028,-0.013 0.027,-0.012 0.026,-0.014 0.024,-0.015 0.024,-0.015 0.021,-0.015 0.019,-0.016 0.017,-0.016 0.016,-0.016 0.015,-0.018 0.013,-0.017 0.01,-0.017 0.009,-0.019 0.007,-0.018 0.004,-0.018 0.003,-0.018 0.002,-0.019"
id="path10"
style="fill:#ffffff;stroke:none" />
<path
d="m 3.9229791,1.7583194 -0.002,-0.021 -0.004,-0.022 -0.007,-0.022 -0.011,-0.021 -0.012,-0.021 -0.016,-0.021 -0.019,-0.021 -0.021,-0.02 -0.024,-0.02 -0.027,-0.02 -0.029,-0.018 -0.032,-0.019 -0.033,-0.017 -0.037,-0.017 -0.039,-0.016 -0.04,-0.016 -0.043,-0.014 -0.045,-0.014 -0.046,-0.013 -0.048,-0.012 -0.05,-0.01 -0.051,-0.011 -0.052,-0.009 -0.054,-0.008 -0.054,-0.007 -0.056,-0.006 -0.055,-0.005 -0.057,-0.004 -0.057,-0.002 -0.058,-0.002 -0.057,0 0,0 -0.057,0 -0.058,0.002 -0.057,0.002 -0.057,0.004 -0.056,0.005 -0.055,0.006 -0.054,0.007 -0.054,0.008 -0.052,0.009 -0.051,0.011 -0.05,0.01 -0.048,0.012 -0.047,0.013 -0.044,0.014 -0.043,0.014 -0.04,0.016 -0.039,0.016 -0.037,0.017 -0.034,0.017 -0.031,0.019 -0.029,0.018 -0.027,0.02 -0.024,0.02 -0.021,0.02 -0.018,0.021 -0.017,0.021 -0.012,0.021 -0.011,0.021 -0.007,0.022 -0.005,0.022 -10e-4,0.021 0,0 10e-4,0.023 0.005,0.021 0.007,0.022 0.011,0.021 0.012,0.022 0.017,0.02 0.018,0.021 0.021,0.02 0.024,0.02 0.027,0.02 0.029,0.018 0.031,0.018 0.034,0.018 0.037,0.017 0.039,0.016 0.04,0.016 0.043,0.015 0.044,0.013 0.047,0.013 0.048,0.012 0.05,0.011 0.051,0.01 0.052,0.009 0.054,0.009 0.054,0.007 0.055,0.005 0.056,0.005 0.057,0.004 0.057,0.003 0.058,0.002 0.057,0 0,0 0.057,0 0.058,-0.002 0.057,-0.003 0.057,-0.004 0.055,-0.005 0.056,-0.005 0.054,-0.007 0.054,-0.009 0.052,-0.009 0.051,-0.01 0.05,-0.011 0.048,-0.012 0.046,-0.013 0.045,-0.013 0.043,-0.015 0.04,-0.016 0.039,-0.016 0.037,-0.017 0.033,-0.018 0.032,-0.018 0.029,-0.018 0.027,-0.02 0.024,-0.02 0.021,-0.02 0.019,-0.021 0.016,-0.02 0.012,-0.022 0.011,-0.021 0.007,-0.022 0.004,-0.021 0.002,-0.023"
id="path12"
style="fill:#ffffff;stroke:none" />
<path
d="m 4.5529791,0.76631938 0,-0.016 -0.003,-0.016 -0.004,-0.017 -0.008,-0.016 -0.007,-0.016 -0.011,-0.016 -0.012,-0.015 -0.013,-0.015 -0.016,-0.015 -0.017,-0.015 -0.019,-0.013 -0.02,-0.014 -0.022,-0.013 -0.023,-0.013 -0.025,-0.012 -0.026,-0.011 -0.027,-0.012 -0.029,-0.01 -0.03,-0.01 -0.031,-0.009 -0.031,-0.008 -0.033,-0.008 -0.034,-0.007 -0.034,-0.006 -0.035,-0.005 -0.035,-0.005 -0.036,-0.003 -0.037,-0.003 -0.036,-0.002 -0.037,-0.001 -0.037,0 0,0 -0.037,0 -0.036,0.001 -0.038,0.002 -0.035,0.003 -0.036,0.003 -0.036,0.005 -0.034,0.005 -0.035,0.006 -0.034,0.007 -0.032,0.008 -0.032,0.008 -0.031,0.009 -0.03,0.01 -0.028,0.01 -0.028,0.012 -0.026,0.011 -0.025,0.012 -0.023,0.013 -0.022,0.013 -0.02,0.014 -0.019,0.013 -0.016,0.015 -0.016,0.015 -0.014,0.015 -0.012,0.015 -0.01,0.016 -0.009,0.016 -0.006,0.016 -0.005,0.017 -0.002,0.016 -10e-4,0.016 0,0 10e-4,0.016 0.002,0.016 0.005,0.017 0.006,0.016 0.009,0.016 0.01,0.016 0.012,0.016 0.014,0.014 0.016,0.016 0.016,0.014 0.019,0.014 0.02,0.013 0.022,0.014 0.023,0.013 0.025,0.012 0.026,0.011 0.028,0.011 0.028,0.011 0.03,0.009 0.031,0.009 0.032,0.009 0.032,0.007 0.034,0.007 0.035,0.006 0.034,0.006 0.036,0.004 0.036,0.003 0.035,0.003 0.038,0.003 0.036,0 0.037,10e-4 0,0 0.037,-10e-4 0.037,0 0.036,-0.003 0.037,-0.003 0.036,-0.003 0.035,-0.004 0.035,-0.006 0.034,-0.006 0.034,-0.007 0.033,-0.007 0.031,-0.009 0.031,-0.009 0.03,-0.009 0.029,-0.011 0.027,-0.011 0.026,-0.011 0.025,-0.012 0.023,-0.013 0.022,-0.014 0.02,-0.013 0.019,-0.014 0.017,-0.014 0.016,-0.016 0.013,-0.014 0.012,-0.016 0.011,-0.016 0.007,-0.016 0.008,-0.016 0.004,-0.017 0.003,-0.016 0,-0.016"
id="path14"
style="fill:#ffffff;stroke:none" />
<path
d="m 4.7599791,1.1813194 -10e-4,-0.016 -0.002,-0.016 -0.005,-0.017 -0.007,-0.016 -0.008,-0.016 -0.01,-0.016 -0.012,-0.016 -0.013,-0.014 -0.015,-0.015 -0.018,-0.015 -0.019,-0.014 -0.019,-0.013 -0.022,-0.013 -0.023,-0.014 -0.024,-0.011 -0.027,-0.012 -0.027,-0.011 -0.028,-0.01 -0.03,-0.01 -0.03,-0.009 -0.032,-0.008 -0.033,-0.008 -0.033,-0.007 -0.033,-0.005 -0.035,-0.006 -0.035,-0.004 -0.036,-0.004 -0.036,-0.003 -0.036,-0.002 -0.037,-0.001 -0.036,-0.001 0,0 -0.038,0.001 -0.036,0.001 -0.036,0.002 -0.036,0.003 -0.036,0.004 -0.035,0.004 -0.035,0.006 -0.033,0.005 -0.034,0.007 -0.032,0.008 -0.032,0.008 -0.03,0.009 -0.03,0.01 -0.028,0.01 -0.027,0.011 -0.027,0.012 -0.024,0.011 -0.022,0.014 -0.022,0.013 -0.021,0.013 -0.018,0.014 -0.018,0.015 -0.015,0.015 -0.013,0.014 -0.012,0.016 -0.01,0.016 -0.008,0.016 -0.007,0.016 -0.005,0.017 -0.002,0.016 -10e-4,0.016 0,0 10e-4,0.016 0.002,0.016 0.005,0.016 0.007,0.016 0.008,0.017 0.01,0.016 0.012,0.015 0.013,0.015 0.015,0.015 0.018,0.015 0.018,0.013 0.021,0.014 0.022,0.013 0.022,0.013 0.024,0.012 0.027,0.011 0.027,0.012 0.028,0.01 0.03,0.01 0.03,0.009 0.032,0.008 0.032,0.007 0.034,0.007 0.033,0.006 0.035,0.005 0.035,0.005 0.036,0.004 0.036,0.003 0.036,0.002 0.036,0.001 0.038,0 0,0 0.036,0 0.037,-0.001 0.036,-0.002 0.036,-0.003 0.036,-0.004 0.035,-0.005 0.035,-0.005 0.033,-0.006 0.033,-0.007 0.033,-0.007 0.032,-0.008 0.03,-0.009 0.03,-0.01 0.028,-0.01 0.027,-0.012 0.027,-0.011 0.024,-0.012 0.023,-0.013 0.022,-0.013 0.019,-0.014 0.019,-0.013 0.018,-0.015 0.015,-0.015 0.013,-0.015 0.012,-0.015 0.01,-0.016 0.008,-0.017 0.007,-0.016 0.005,-0.016 0.002,-0.016 10e-4,-0.016"
id="path16"
style="fill:#ffffff;stroke:none" />
<path
d="m 4.6189791,1.5243194 -10e-4,-0.027 -0.002,-0.027 -0.005,-0.026 -0.006,-0.027 -0.009,-0.026 -0.01,-0.026 -0.012,-0.025 -0.014,-0.025 -0.014,-0.025 -0.017,-0.023 -0.018,-0.024 -0.021,-0.022 -0.021,-0.022 -0.023,-0.02 -0.024,-0.021 -0.027,-0.018 -0.027,-0.018 -0.027,-0.018 -0.029,-0.015 -0.031,-0.015 -0.032,-0.013 -0.031,-0.013 -0.033,-0.011 -0.034,-0.01 -0.035,-0.009 -0.035,-0.007 -0.036,-0.006 -0.035,-0.005 -0.036,-0.003 -0.036,-0.002 -0.036,-0.001 0,0 -0.037,0.001 -0.036,0.002 -0.036,0.003 -0.036,0.005 -0.035,0.006 -0.035,0.007 -0.035,0.009 -0.034,0.01 -0.033,0.011 -0.032,0.013 -0.031,0.013 -0.031,0.015 -0.029,0.015 -0.028,0.018 -0.027,0.018 -0.026,0.018 -0.024,0.021 -0.023,0.02 -0.022,0.022 -0.019,0.022 -0.019,0.024 -0.017,0.023 -0.015,0.025 -0.014,0.025 -0.011,0.025 -0.01,0.026 -0.008,0.026 -0.007,0.027 -0.005,0.026 -0.002,0.027 -10e-4,0.027 0,0 10e-4,0.027 0.002,0.027 0.005,0.026 0.007,0.026 0.008,0.026 0.01,0.026 0.011,0.026 0.014,0.025 0.015,0.024 0.017,0.024 0.019,0.023 0.019,0.022 0.022,0.022 0.023,0.021 0.024,0.02 0.026,0.019 0.027,0.018 0.028,0.017 0.029,0.016 0.031,0.015 0.031,0.013 0.032,0.013 0.033,0.011 0.034,0.01 0.035,0.008 0.035,0.008 0.035,0.006 0.036,0.005 0.036,0.003 0.036,0.002 0.037,10e-4 0,0 0.036,-10e-4 0.036,-0.002 0.036,-0.003 0.035,-0.005 0.036,-0.006 0.035,-0.008 0.035,-0.008 0.034,-0.01 0.033,-0.011 0.031,-0.013 0.032,-0.013 0.031,-0.015 0.029,-0.016 0.027,-0.017 0.027,-0.018 0.027,-0.019 0.024,-0.02 0.023,-0.021 0.021,-0.022 0.021,-0.022 0.018,-0.023 0.017,-0.024 0.014,-0.024 0.014,-0.025 0.012,-0.026 0.01,-0.026 0.009,-0.026 0.006,-0.026 0.005,-0.026 0.002,-0.027 10e-4,-0.027"
id="path18"
style="fill:#ffffff;stroke:none" />
<path
d="m 2.7919791,0.61131938 0.965,-0.091 -0.013,-0.021 -0.014,-0.019 -0.017,-0.02 -0.019,-0.02 -0.022,-0.019 -0.024,-0.018 -0.026,-0.018 -0.028,-0.017 -0.031,-0.017 -0.032,-0.016 -0.034,-0.015 -0.036,-0.014 -0.038,-0.014 -0.039,-0.013 -0.042,-0.012 -0.042,-0.011 -0.043,-0.011 -0.045,-0.009 -0.046,-0.009 -0.047,-0.007 -0.048,-0.007 -0.048,-0.005 -0.049,-0.004 -0.049,-0.004 -0.049,-0.002 -0.051,-0.001 -0.05,0 -0.05,0 -0.049,0.003 -0.05,0.002 -0.049,0.004 -0.049,0.006 -0.047,0.006 -0.048,0.007 -0.046,0.008 -0.045,0.009 -0.044,0.01 -0.043,0.011 -0.041,0.012 -0.04,0.013 -0.038,0.013 -0.036,0.014 -0.036,0.016 -0.032,0.015 -0.032,0.017 -0.029,0.017 -0.026,0.018 -0.024,0.018 -0.024,0.019 -0.02,0.018 -0.018,0.02 -0.015,0.02 -0.012,0.021 -0.011,0.02 0.971,0.077"
id="path22"
style="fill:#ffffff;stroke:none" />
<path
d="m 3.7569791,0.51931938 -0.013,-0.021 -0.014,-0.019 -0.017,-0.02 -0.02,-0.019 -0.022,-0.019 -0.024,-0.018 -0.026,-0.018 -0.029,-0.018 -0.03,-0.016 -0.032,-0.016 -0.035,-0.016 -0.035,-0.014 -0.038,-0.014 -0.04,-0.012 -0.04,-0.013 -0.042,-0.011 -0.044,-0.01 -0.045,-0.01 -0.046,-0.008 -0.046,-0.008 -0.048,-0.006 -0.048,-0.005 -0.049,-0.004 -0.05,-0.004 -0.05,-0.002 -0.049,-0.001 -0.051,0 -0.049,0 -0.049,0.003 -0.051,0.002 -0.048,0.004 -0.049,0.006 -0.047,0.006 -0.048,0.007 -0.046,0.008 -0.045,0.009 -0.043,0.01 -0.043,0.011 -0.042,0.012 -0.039,0.013 -0.038,0.013 -0.036,0.014 -0.036,0.015 -0.033,0.015 -0.031,0.017 -0.028,0.018 -0.028,0.017 -0.024,0.018 -0.022,0.019 -0.021,0.019 -0.018,0.019 -0.016,0.02 -0.012,0.021 -0.011,0.02"
id="path24"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 1.4819791,0.82631938 0.486,-0.317 -0.029,-0.013 -0.032,-0.012 -0.032,-0.012 -0.033,-0.01 -0.034,-0.01 -0.035,-0.008 -0.036,-0.008 -0.036,-0.006 -0.037,-0.006 -0.037,-0.005 -0.038,-0.003 -0.038,-0.003 -0.039,-0.001 -0.038,0 -0.038,0 -0.038,0.002 -0.038,0.003 -0.038,0.004 -0.037,0.005 -0.036,0.006 -0.037,0.007 -0.035,0.008 -0.034,0.009 -0.034,0.009 -0.033,0.011 -0.031,0.013 -0.031,0.012 -0.029,0.013 -0.028,0.015 -0.027,0.015 -0.026,0.015 -0.024,0.016 -0.022,0.018 -0.021,0.017 -0.018,0.018 -0.018,0.02 -0.016,0.018 -0.014,0.02 -0.012,0.02 -0.01,0.02 -0.008,0.02 -0.006,0.022 -0.005,0.02 -0.003,0.021 0,0.021 0.001,0.021 0.003,0.021 0.005,0.021 0.74600003,-0.067"
id="path26"
style="fill:#ffffff;stroke:none" />
<path
d="m 1.9659791,0.50831938 -0.03,-0.014 -0.031,-0.011 -0.033,-0.012 -0.033,-0.01 -0.035,-0.01 -0.034,-0.008 -0.036,-0.008 -0.036,-0.006 -0.037,-0.005 -0.037,-0.005 -0.038,-0.004 -0.038,-0.002 -0.039,-0.001 -0.038,0 -0.038,0 -0.038,0.003 -0.038,0.002 -0.038,0.004 -0.037,0.005 -0.036,0.007 -0.036,0.006 -0.035,0.009 -0.034,0.008 -0.034,0.011 -0.033,0.011 -0.032,0.011 -0.03,0.013 -0.029,0.013 -0.028,0.015 -0.027,0.015 -0.025,0.015 -0.024,0.016 -0.022,0.018 -0.02,0.017 -0.02,0.019 -0.017,0.018 -0.016,0.019 -0.013,0.02 -0.012,0.02 -0.01,0.02 -0.008,0.021 -0.006,0.02 -0.005,0.021 -0.003,0.02 0,0.022 0.002,0.021 0.002,0.02 0.006,0.021"
id="path28"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 1.3459791,1.5683194 -0.78100003,-0.012 0,0.019 0.001,0.019 0.003,0.019 0.006,0.018 0.008,0.019 0.009,0.018 0.012,0.018 0.014,0.018 0.015,0.017 0.017,0.017 0.019,0.017 0.02,0.016 0.023,0.015 0.023,0.015 0.026,0.015 0.027,0.014 0.027,0.012 0.03,0.013 0.031,0.012 0.032,0.01 0.034,0.011 0.033,0.009 0.035,0.008 0.036,0.008 0.037,0.007 0.038,0.006 0.037,0.005 0.039,0.004 0.039,0.003 0.039,0.003 0.04,10e-4 0.039,0 0.039,-10e-4 0.039,-0.002 0.039,-0.002 0.039,-0.003 0.039,-0.005 0.037,-0.006 0.037,-0.005 0.037,-0.008 0.035,-0.008 0.035,-0.009 0.033,-0.009 0.033,-0.01 0.033,-0.011 0.03,-0.013 -0.513,-0.282"
id="path30"
style="fill:#ffffff;stroke:none" />
<path
d="m 0.56497907,1.5563194 -0.001,0.019 0.002,0.019 0.003,0.018 0.006,0.019 0.008,0.018 0.01,0.019 0.011,0.018 0.014,0.018 0.015,0.017 0.017,0.017 0.018,0.016 0.02,0.016 0.023,0.016 0.024,0.015 0.024,0.014 0.026,0.014 0.029,0.013 0.029,0.013 0.032,0.011 0.032,0.011 0.032,0.01 0.034,0.009 0.035,0.009 0.037,0.008 0.036,0.006 0.037,0.007 0.038,0.005 0.038,0.004 0.039,0.003 0.039,0.002 0.039,0.002 0.04,0 0.039,0 0.039,-0.003 0.039,-0.002 0.038,-0.003 0.038,-0.005 0.038,-0.004 0.038,-0.007 0.036,-0.007 0.036,-0.008 0.034,-0.009 0.034,-0.009 0.034,-0.01 0.031,-0.011 0.03,-0.012"
id="path32"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 3.8359791,0.75931938 0.689,0.104 0.011,-0.016 0.01,-0.015 0.007,-0.016 0.006,-0.016 0.003,-0.016 0.002,-0.017 0,-0.016 -0.002,-0.017 -0.003,-0.016 -0.006,-0.016 -0.007,-0.016 -0.01,-0.016 -0.01,-0.016 -0.013,-0.015 -0.015,-0.015 -0.016,-0.015 -0.018,-0.014 -0.02,-0.014 -0.022,-0.014 -0.021,-0.012 -0.025,-0.013 -0.025,-0.012 -0.027,-0.011 -0.029,-0.011 -0.028,-0.01 -0.03,-0.009 -0.032,-0.009 -0.032,-0.008 -0.034,-0.007 -0.033,-0.006 -0.036,-0.006 -0.034,-0.005 -0.037,-0.004 -0.036,-0.003 -0.037,-0.003 -0.037,-0.001 -0.036,-0.001 -0.037,0 -0.037,0.001 -0.037,0.002 -0.036,0.002 -0.036,0.003 -0.036,0.004 -0.036,0.005 0.238,0.306"
id="path34"
style="fill:#ffffff;stroke:none" />
<path
d="m 4.4569791,0.92331938 0.067,-0.059 0.011,-0.016 0.009,-0.015 0.009,-0.016 0.005,-0.017 0.004,-0.016 0.002,-0.016 0.001,-0.016 -0.003,-0.017 -0.003,-0.016 -0.006,-0.017 -0.007,-0.015 -0.009,-0.016 -0.011,-0.016 -0.012,-0.015 -0.016,-0.015 -0.016,-0.015 -0.017,-0.014 -0.02,-0.014 -0.021,-0.014 -0.023,-0.013 -0.023,-0.012 -0.026,-0.012 -0.027,-0.011 -0.028,-0.011 -0.029,-0.011 -0.03,-0.009 -0.032,-0.008 -0.031,-0.008 -0.033,-0.008 -0.034,-0.006 -0.035,-0.006 -0.035,-0.005 -0.036,-0.004 -0.036,-0.003 -0.037,-0.002 -0.037,-0.002 -0.037,-0.001 -0.037,0 -0.037,0.001 -0.037,0.002 -0.036,0.002 -0.036,0.003 -0.036,0.004 -0.035,0.005"
id="path36"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 3.9329791,1.1853194 0.775,0.132 0.016,-0.017 0.013,-0.017 0.012,-0.018 0.008,-0.018 0.008,-0.018 0.004,-0.019 10e-4,-0.018 0,-0.018 -0.003,-0.019 -0.004,-0.019 -0.008,-0.018 -0.009,-0.018 -0.012,-0.017 -0.014,-0.018 -0.016,-0.017 -0.018,-0.017 -0.02,-0.016 -0.023,-0.016 -0.024,-0.015 -0.686,0.20100002"
id="path38"
style="fill:#ffffff;stroke:none" />
<path
d="m 4.6121701,1.38473 0.098479,-0.059128 0.014482,-0.018039 0.013517,-0.018039 0.01062,-0.018039 0.00869,-0.018039 0.00772,-0.018039 0.00386,-0.018039 0.00193,-0.019041 0,-0.018039 -0.00386,-0.020044 -0.00386,-0.018039 -0.00676,-0.018039 -0.00965,-0.018039 -0.011586,-0.018039 -0.013517,-0.018039 -0.015448,-0.017037 -0.018344,-0.017037 -0.021241,-0.016035 -0.020275,-0.016035 -0.024137,-0.0160349 -0.1544773,-0.0591286"
id="path40"
style="fill:none;stroke:#6c8f93;stroke-width:0.0596842;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 3.9009791,1.5363194 -0.245,0.503 0.036,0.008 0.036,0.007 0.037,0.006 0.038,0.004 0.038,0.003 0.037,0.002 0.037,0 0.038,-0.002 0.037,-0.002 0.038,-0.004 0.036,-0.005 0.038,-0.007 0.035,-0.008 0.036,-0.009 0.034,-0.011 0.034,-0.012 0.033,-0.013 0.032,-0.014 0.031,-0.016 0.03,-0.016 0.028,-0.018 0.027,-0.019 0.027,-0.02 0.025,-0.02 0.022,-0.022 0.022,-0.021 0.02,-0.024 0.018,-0.024 0.016,-0.024 0.015,-0.025 0.014,-0.026 0.01,-0.025 0.01,-0.027 0.008,-0.027 0.006,-0.026 0.003,-0.028 0.002,-0.027 0,-0.027 -0.002,-0.027 -0.004,-0.027 -0.006,-0.026 -0.008,-0.028 -0.009,-0.025 -0.71,0.149"
id="path42"
style="fill:#ffffff;stroke:none" />
<path
d="m 3.6589791,2.0403194 0.036,0.008 0.036,0.007 0.037,0.006 0.037,0.004 0.037,0.002 0.038,0.002 0.038,10e-4 0.037,-0.003 0.038,-0.002 0.037,-0.004 0.037,-0.005 0.036,-0.007 0.036,-0.009 0.036,-0.009 0.034,-0.011 0.033,-0.011 0.034,-0.014 0.032,-0.014 0.031,-0.015 0.029,-0.018 0.029,-0.017 0.027,-0.019 0.026,-0.019 0.025,-0.021 0.023,-0.022 0.021,-0.022 0.019,-0.024 0.019,-0.023 0.016,-0.024 0.015,-0.025 0.014,-0.026 0.011,-0.026 0.008,-0.026 0.008,-0.027 0.006,-0.027 0.003,-0.027 0.002,-0.027 0,-0.027 -0.002,-0.027 -0.005,-0.027 -0.005,-0.028 -0.008,-0.026 -0.01,-0.026"
id="path44"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 0.80297907,1.2493194 -0.037,-0.34200002 -0.028,0.002 -0.026,0.002 -0.027,0.004 -0.026,0.004 -0.026,0.005 -0.026,0.006 -0.024,0.007 -0.025,0.008 -0.024,0.008 -0.023,0.009 -0.022,0.01 -0.021,0.01 -0.021,0.012 -0.02,0.012 -0.019,0.013 -0.017,0.013 -0.017,0.014 -0.015,0.014 -0.015,0.015 -0.013,0.015 -0.011,0.016 -0.011,0.016 -0.01,0.016 -0.008,0.017 -0.006,0.017 -0.006,0.016 -0.004,0.018 -0.003,0.018 -0.001,0.017 0,0.017 0.001,0.018 0.003,0.017 0.004,0.017 0.006,0.017 0.007,0.017 0.008,0.017 0.009,0.016 0.011,0.016 0.012,0.016 0.013,0.015 0.016,0.015 0.015,0.014 0.017,0.014 0.018,0.013 0.018,0.013 0.02,0.011 0.021,0.011 0.021,0.012 0.022,0.009 0.024,0.009 0.023,0.009 0.243,-0.305"
id="path46"
style="fill:#ffffff;stroke:none" />
<path
d="m 0.76697907,0.90731938 -0.027,0.002 -0.027,0.002 -0.026,0.003 -0.026,0.005 -0.025,0.004 -0.026,0.006 -0.025,0.007 -0.023,0.007 -0.023,0.009 -0.023,0.009 -0.023,0.009 -0.021,0.01 -0.02,0.011 -0.02,0.012 -0.018,0.012 -0.018,0.013 -0.017,0.013 -0.016,0.015 -0.015,0.014 -0.012,0.014 -0.012,0.016 -0.012,0.015 -0.009,0.016 -0.009,0.016 -0.007,0.017 -0.006,0.017 -0.005,0.017 -0.003,0.017 -0.002,0.017 0,0.017 0,0.017 0.003,0.017 0.003,0.018 0.004,0.016 0.007,0.017 0.006,0.016 0.009,0.017 0.009,0.016 0.012,0.015 0.012,0.016 0.014,0.014 0.014,0.014 0.016,0.015 0.016,0.013 0.018,0.013 0.019,0.012 0.019,0.011 0.021,0.012 0.021,0.01 0.022,0.009 0.023,0.009 0.024,0.009"
id="path48"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 2.7759791,1.7874896 -1.109,0.040159 0.008,0.020594 0.009,0.019565 0.013,0.020594 0.015,0.020595 0.018,0.019565 0.021,0.018535 0.023,0.018535 0.025,0.018535 0.029,0.018535 0.031,0.017505 0.032,0.015446 0.036,0.016475 0.037,0.015446 0.039,0.015446 0.042,0.013386 0.043,0.013386 0.045,0.011327 0.047,0.012357 0.048,0.010297 0.049,0.00927 0.051,0.00927 0.052,0.00824 0.053,0.00618 0.053,0.00618 0.055,0.00515 0.055,0.00412 0.056,0.00206 0.055,0.00206 0.057,0.00103 0.055,0 0.057,-0.00103 0.055,-0.00309 0.055,-0.00309 0.055,-0.00515 0.054,-0.00515 0.053,-0.00618 0.052,-0.00721 0.052,-0.00824 0.05,-0.010297 0.049,-0.00927 0.047,-0.011327 0.046,-0.011327 0.044,-0.013386 0.042,-0.014416 0.04,-0.013386 0.039,-0.015446 0.036,-0.015446 0.034,-0.016476 0.031,-0.016476 0.03,-0.017505 0.027,-0.019565 0.025,-0.017505 0.022,-0.019565 0.018,-0.018535 -1.056,-0.1307748"
id="path50"
style="fill:#ffffff;stroke:none" />
<path
d="m 1.6669791,1.8393194 0.008,0.019 0.01,0.02 0.012,0.02 0.016,0.019 0.017,0.019 0.021,0.018 0.023,0.018 0.025,0.019 0.03,0.017 0.03,0.017 0.032,0.015 0.036,0.016 0.037,0.015 0.039,0.015 0.042,0.013 0.044,0.013 0.043,0.011 0.048,0.012 0.047,0.01 0.049,0.009 0.051,0.009 0.052,0.008 0.053,0.006 0.053,0.005 0.055,0.006 0.054,0.003 0.056,0.003 0.056,0.002 0.056,10e-4 0.056,0 0.056,-10e-4 0.055,-0.003 0.055,-0.003 0.055,-0.005 0.054,-0.005 0.054,-0.006 0.051,-0.007 0.052,-0.008 0.05,-0.009 0.048,-0.01 0.048,-0.011 0.046,-0.011 0.043,-0.013 0.043,-0.012 0.04,-0.015 0.039,-0.014 0.036,-0.016 0.034,-0.015 0.032,-0.017 0.029,-0.016 0.027,-0.018 0.024,-0.018 0.022,-0.019 0.02,-0.018"
id="path52"
style="fill:none;stroke:#6c8f93;stroke-width:0.06067566;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<rect
width="2.7800133"
height="0.61515152"
rx="0"
ry="0.28983101"
x="1.2692652"
y="0.80605406"
transform="matrix(0.999604,0.02812984,-0.02812984,0.999604,0,0)"
id="rect1892"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" />
<path
d="m 2.5029776,1.9073766 0.023,-0.00895 0.020913,-0.00976 0.022119,-0.011947 0.021191,-0.0142 0.021284,-0.015682 0.02031,-0.017195 0.020402,-0.018676 0.020541,-0.020897 0.018455,-0.021702 0.018546,-0.023184 0.018685,-0.025405 0.016552,-0.02547 0.016691,-0.027691 0.014651,-0.029236 0.015717,-0.029204 0.013676,-0.03075 0.012702,-0.032263 0.012748,-0.033004 0.010665,-0.033809 0.010665,-0.033809 0.00863,-0.035354 0.00756,-0.035386 0.00653,-0.036158 0.00658,-0.0369 0.00445,-0.036963 0.00333,-0.036256 0.00236,-0.037768 0.00226,-0.036288 -9.319e-4,-0.036384 2.273e-4,-0.037833 -0.00205,-0.035676 -0.00413,-0.03648 L 2.883076,0.9720976 2.877836,0.9363258 2.871426,0.902003 2.863906,0.8683889 2.855226,0.8362236 2.845476,0.8040261 2.834621,0.7725371 2.82256,0.7432375 2.810453,0.7146786 2.797187,0.6875686 2.782854,0.6604265 2.768383,0.635506 2.751686,0.6120024 2.733784,0.590685 2.716948,0.569403 2.69886,0.5510511 2.679613,0.5341483 2.660319,0.517986 2.640887,0.5040454 2.620296,0.4915538 2.598453,0.4819918 2.577631,0.4732028"
id="path24-7"
style="fill:none;stroke:#6c8f93;stroke-width:0.107496;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
id="path2057-3"
d="M 1.5298421,1.2403194 H 2.3548856 V 1.303797 L 2.577141,1.1602384 2.3548856,1.0313279 v 0.064457 H 1.5298421 v 0.1445348"
style="fill:#6c8f93;fill-opacity:1;stroke:none;stroke-width:5.35746e-05;stroke-opacity:0.994961" />
<path
id="path2057-3-9"
d="M 3.1225773,1.2403194 H 3.9476208 V 1.303797 L 4.1698762,1.1602384 3.9476208,1.0313279 v 0.064457 H 3.1225773 v 0.1445348"
style="fill:#6c8f93;fill-opacity:1;stroke:none;stroke-width:5.35746e-05;stroke-opacity:0.994961" />
<path
id="path2057-3-5"
d="m 1.5278616,0.86827348 h 0.8319275 v 0.063478 L 2.5838989,0.78819244 2.3597891,0.65928186 v 0.064457 H 1.5278616 v 0.14453482"
style="fill:#6c8f93;fill-opacity:1;stroke:none;stroke-width:5.37977e-05;stroke-opacity:0.994961" />
<path
id="path2057-3-6"
d="m 1.527029,1.6225499 h 0.8217599 v 0.063478 L 2.5701597,1.5424689 2.3487889,1.4135585 v 0.064457 H 1.5270291 v 0.1445347"
style="fill:#6c8f93;fill-opacity:1;stroke:none;stroke-width:5.34679e-05;stroke-opacity:0.994961" />
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -19,9 +19,9 @@ import sys
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
# we only support Python 3 version >= 3.7
if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 7):
raise SystemExit("Python 3.7 or higher is required")
# we only support Python 3 version >= 3.8
if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 8):
raise SystemExit("Python 3.8 or higher is required")
class PyTest(TestCommand):
@@ -79,8 +79,8 @@ setup(
include_package_data=True,
package_data={"gns3": ["configs/*.txt", "schemas/*.json"]},
platforms="any",
python_requires='>=3.7',
setup_requires=["setuptools>=17.1"],
python_requires=">=3.8",
setup_requires=["setuptools>=45.2"],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: X11 Applications :: Qt",
@@ -92,13 +92,13 @@ setup(
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
],
)

View File

@@ -65,8 +65,8 @@
{
"filename": "empty100G.qcow2",
"version": "100G",
"checksum": "d08fdec95fffbda3f04e9a00db49295df73ae4a507396e442ba9e4ad5c14ce5a",
"checksum_type": "sha256",
"checksum": "1e6409a4523ada212dea2ebc50e50a65",
"checksum_type": "md5",
"filesize": 198656,
"download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty100G.qcow2/download"

View File

@@ -1,4 +1,4 @@
-rrequirements.txt
PyQt5==5.15.10 # pyup: ignore
PyQt5==5.15.11 # pyup: ignore
pywin32==306 # pyup: ignore