Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e50c2a4b1 | ||
|
|
94c636ae61 | ||
|
|
f53b9a266e | ||
|
|
2476448032 | ||
|
|
a34cd742e3 | ||
|
|
39698196ac | ||
|
|
61432ced4f | ||
|
|
2ddd13c445 | ||
|
|
af6c4c5b3e | ||
|
|
d4012294bf | ||
|
|
4b04b0e855 | ||
|
|
1bec5019bf | ||
|
|
ec6b876baa | ||
|
|
7cee0d01ab | ||
|
|
dd3314d06b | ||
|
|
9c58b26265 | ||
|
|
83c26f47da | ||
|
|
8ed2f55600 | ||
|
|
b435317904 | ||
|
|
acb8aa8ca2 | ||
|
|
c55d6b8a6f | ||
|
|
a4039a254e | ||
|
|
85ed4b3026 | ||
|
|
5207a99692 | ||
|
|
d69527995d | ||
|
|
4b000ba2f7 | ||
|
|
1b302b77a0 | ||
|
|
a5b5c404ec | ||
|
|
6b97b0c6cd | ||
|
|
115dd43eee | ||
|
|
2530bf97a8 | ||
|
|
9892fd0654 | ||
|
|
c71ee73da8 | ||
|
|
0643fd516d | ||
|
|
a25680f2ce | ||
|
|
58bd5be920 | ||
|
|
d95633ba2c | ||
|
|
dfea6d1723 | ||
|
|
ddeb95cb0a | ||
|
|
5f7ff0d70d | ||
|
|
a00e039cec | ||
|
|
a24e9adef1 | ||
|
|
ee5f8e8edd | ||
|
|
f5470130f5 | ||
|
|
1ff405885e | ||
|
|
9fb42ead9f | ||
|
|
2ea1946c0f | ||
|
|
963e054918 | ||
|
|
0f5f6ab645 | ||
|
|
8a905b5c39 | ||
|
|
e917193f06 | ||
|
|
16846ce49c | ||
|
|
624a670ae7 | ||
|
|
406326ccd8 | ||
|
|
24bc15fb73 | ||
|
|
348d8b9438 | ||
|
|
6787982408 | ||
|
|
c2384917fa | ||
|
|
b80178d0cf | ||
|
|
e6084ed834 | ||
|
|
ba924cd0d9 | ||
|
|
0c3d43346f | ||
|
|
fcf6ef3027 | ||
|
|
e0f87e573d | ||
|
|
3ec068f0cb | ||
|
|
37f1fcf6f7 | ||
|
|
c51dd1605d | ||
|
|
4ebf3b4e1c | ||
|
|
b1ec9d535c | ||
|
|
7fc9087cf0 | ||
|
|
4972d460d2 | ||
|
|
c388836be7 | ||
|
|
18ae4a6ce9 | ||
|
|
3020e1fc9f | ||
|
|
fe2f8424db | ||
|
|
a744f65199 | ||
|
|
d27578f0fc | ||
|
|
b01c11f19b | ||
|
|
fb269da4d3 | ||
|
|
ab15f96bb5 | ||
|
|
5bb8b8e8bd | ||
|
|
3f9632fae0 | ||
|
|
b5f8195abb | ||
|
|
73a293bd17 | ||
|
|
0a1dfb99e9 | ||
|
|
d352919264 | ||
|
|
65f2a1e461 | ||
|
|
71f289721b | ||
|
|
c28089d400 | ||
|
|
64f009bf71 | ||
|
|
edb2fd7fd9 | ||
|
|
62e7ad8c8a | ||
|
|
caeb5d71c3 | ||
|
|
cfe96b2311 | ||
|
|
8955b9ee29 | ||
|
|
e727abf27a | ||
|
|
f209bf7644 | ||
|
|
5860dedc32 | ||
|
|
9e2df17a4e | ||
|
|
a95761437a | ||
|
|
626510865f | ||
|
|
2e248aa340 | ||
|
|
e306f73f01 | ||
|
|
0c4367d77e | ||
|
|
6dda0ff787 | ||
|
|
d7d4b84309 | ||
|
|
7b57983699 | ||
|
|
bb89fe2275 | ||
|
|
7eb2a923b2 | ||
|
|
58052e3cce | ||
|
|
e431104f6b | ||
|
|
a4e9d6b8ce | ||
|
|
f58f5c7b95 | ||
|
|
37faa39309 | ||
|
|
4d64598ed2 | ||
|
|
5132c4e172 | ||
|
|
3c3fdd9ffd | ||
|
|
efa50571c6 | ||
|
|
ca9b10fcca | ||
|
|
8660161b10 | ||
|
|
50ebfb9c06 | ||
|
|
26df59d6b6 | ||
|
|
b903e2ad73 | ||
|
|
b2fe7eb643 | ||
|
|
8095fef228 | ||
|
|
b8da5440f5 | ||
|
|
6cea094e4e | ||
|
|
9ac46c9d50 | ||
|
|
c8a8663ff0 | ||
|
|
d27e5c1795 | ||
|
|
0d2f91709c | ||
|
|
6dc44d5108 | ||
|
|
9c6be0341b | ||
|
|
011a49e998 | ||
|
|
e18c2df5f5 | ||
|
|
1794b8389f | ||
|
|
0379c370eb | ||
|
|
e03550a89b | ||
|
|
c6ea775e81 | ||
|
|
38233ba5e9 | ||
|
|
89c1272bc1 | ||
|
|
8bbb46c599 | ||
|
|
74fca3d736 | ||
|
|
7aeed7aa59 | ||
|
|
aa15ace887 | ||
|
|
2d0a7b5f58 | ||
|
|
20a09b56c1 | ||
|
|
1938cdabae | ||
|
|
8d1bff782c | ||
|
|
4e3eee2383 | ||
|
|
da8aa0d2fd | ||
|
|
5b4481c43a | ||
|
|
593cb8c1fd | ||
|
|
210cf63fe2 | ||
|
|
3b178013c0 | ||
|
|
6e44d6b919 | ||
|
|
6b520b8036 | ||
|
|
803782b9d8 | ||
|
|
d3d6ca3f2e | ||
|
|
f545c793f8 | ||
|
|
47d6a4fef6 | ||
|
|
8862b608cf | ||
|
|
76832ab83f | ||
|
|
fed245fd34 | ||
|
|
3e0f1affd0 | ||
|
|
2110c2805e | ||
|
|
46cfdd8314 | ||
|
|
f8f648c2b6 | ||
|
|
7cd0187f33 | ||
|
|
4d8f362f11 | ||
|
|
469eaa4737 | ||
|
|
c921224b30 | ||
|
|
61487b2e2f | ||
|
|
9affca495e | ||
|
|
9d8886a640 | ||
|
|
98cfec1b77 | ||
|
|
aed174953e | ||
|
|
f0feea8262 | ||
|
|
e2aeaf0a78 | ||
|
|
b92bb94875 | ||
|
|
c56db59353 | ||
|
|
a87c4e21d7 | ||
|
|
ed99a989d7 | ||
|
|
f9a4c9399a | ||
|
|
efb5c8ca9a | ||
|
|
7f08675121 | ||
|
|
a9a2a541c0 | ||
|
|
8998c07e0e | ||
|
|
ba01a89af1 | ||
|
|
eae07d62ad | ||
|
|
23903cf0c9 | ||
|
|
4d908fd855 | ||
|
|
bb0e67be4f | ||
|
|
0410c446fc | ||
|
|
18486e4772 |
34
.github/ISSUE_TEMPLATE/gns3-bug-report.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: GNS3 bug report
|
||||
about: Create a report to help us fix a bug
|
||||
title: 'Short description of the bug'
|
||||
labels: Bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Before you start**
|
||||
Please open an issue only if you suspect there is a bug or any problem with GNS3. Go to https://gns3.com/community for any other questions or for requesting help with GNS3.
|
||||
|
||||
You may also post this issue directly on the GNS3 server repository if you know the bug comes from the server: https://github.com/GNS3/gns3-server/issues/new
|
||||
|
||||
**Describe the bug**
|
||||
Please provide a clear and detailed description of what the bug is.
|
||||
|
||||
**GNS3 version and operating system (please complete the following information):**
|
||||
- OS: [e.g. Windows, Linux or macOS]
|
||||
- GNS3 version [e.g. 2.1.14]
|
||||
- Any use of the GNS3 VM or remote server (ESXi, bare metal etc.)
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Screenshots or videos**
|
||||
If applicable, add screenshots (e.g. of the topology and/or error message) or links to videos to help explain the problem. This will help us a lot to quickly find the bug and fix it.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/gns3-development.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: GNS3 development
|
||||
about: Any question or discussion regarding GNS3 development
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
25
.github/ISSUE_TEMPLATE/gns3-feature-request.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: GNS3 feature request
|
||||
about: Suggest an idea for GNS3
|
||||
title: 'Short description of the feature request'
|
||||
labels: Enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Before you start**
|
||||
Please check if a similar feature request has already been submitted.
|
||||
|
||||
You may also post this issue directly on the GNS3 server repository if you know the feature request only applies to the server: https://github.com/GNS3/gns3-server/issues/new
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen. If applicable, please provide screenshots
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
19
.github/workflows/testing.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: testing
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build and run Docker image
|
||||
run: |
|
||||
docker build -t gns3-gui-test .
|
||||
docker run gns3-gui-test
|
||||
21
.travis.yml
@@ -1,21 +0,0 @@
|
||||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
notifications:
|
||||
email: false
|
||||
script:
|
||||
- docker build -t gns3-gui-test .
|
||||
- docker run gns3-gui-test
|
||||
before_deploy:
|
||||
- sudo pip install twine
|
||||
- sudo pip install urllib3[secure]
|
||||
deploy:
|
||||
provider: pypi
|
||||
edge:
|
||||
branch: v1.8.45
|
||||
user: noplay
|
||||
password:
|
||||
secure: FofcqlJjgqf2jaDaXpLHeigVoexbrOz3WwnDuiJpwJxeFUlPY8s2cQs/Bm+dzxzZaOaGiVE0A83v/Xa10yD5tflThHt4sqYJK3iQCinA7wgeAlDimB4xrWUNplfNJZ/Eod5Ssa++E02W+3i29PxpXY//mjCY7qDxaoxul1gnFJY=
|
||||
on:
|
||||
tags: true
|
||||
repo: GNS3/gns3-gui
|
||||
13
.whitesource
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"scanSettings": {
|
||||
"configMode": "AUTO",
|
||||
"configExternalURL": "",
|
||||
"projectToken" : ""
|
||||
},
|
||||
"checkRunSettings": {
|
||||
"vulnerableCheckRunConclusionLevel": "failure"
|
||||
},
|
||||
"issueSettings": {
|
||||
"minSeverityLevel": "LOW"
|
||||
}
|
||||
}
|
||||
151
CHANGELOG
@@ -1,5 +1,156 @@
|
||||
# Change Log
|
||||
|
||||
## 2.2.11 09/07/2020
|
||||
|
||||
* Try to fix "Recent project" selection not working. Ref #3007
|
||||
* Fix debug entries shown twice in console window and double error messages with remote GNS3VM. Fixes #3010
|
||||
* Fix deprecation warning. Ref #3009
|
||||
* Fix tests on macOS. Ref #3009
|
||||
* Fix sentry SDK is configured twice.
|
||||
|
||||
## 2.2.10 18/06/2020
|
||||
|
||||
* New fix for multi-device selection/deselection not working as expected with right click. Fixes #2986
|
||||
* Optimize snap-to-grid code for drawing items. Fixes #2997
|
||||
* Move jsonschema 2.6.0 requirement in build repository.
|
||||
* Only use jsonschema 2.6.0 on Windows and macOS.
|
||||
* Disable default integrations for sentry sdk.
|
||||
|
||||
## 2.2.9 04/06/2020
|
||||
|
||||
* Fix GUI doesn't detect another GUI on macOS. Fixes #2994
|
||||
* Support to activate/deactive network connection state replication in Qemu.
|
||||
* Option to reset or not all MAC addresses when exporting or duplicating a project.
|
||||
* Fix Multi-device selection/deselection not working as expected with right click. Fixes #2986
|
||||
* Replace Raven by Sentry SDK. Fixes https://github.com/GNS3/gns3-server/issues/1758
|
||||
* Fix online help menu URL. Fixes #2984
|
||||
* Require setuptools>=17.1 in setup.py. Ref https://github.com/GNS3/gns3-server/issues/1751 This is to support environmental markers. https://github.com/pypa/setuptools/blob/master/CHANGES.rst#171
|
||||
* Update README. Ref https://github.com/GNS3/gns3-server/issues/1719
|
||||
* Restore editReadme attribute which was removed in Change 'New export project wizard'
|
||||
* Updated GUI pyqt files from Tab Order 'fixes' in "Tab Order in Preferences and Project Dialog #2872"
|
||||
|
||||
## 2.2.8 07/05/2020
|
||||
|
||||
* Default port set to 80 for server running in the GNS3 VM. Fixes #1737
|
||||
* Make the Web UI the default page. Ref https://github.com/GNS3/gns3-server/issues/1737
|
||||
* Fix "export portable project forgets contents of README". Fixes #1724
|
||||
* Activate unified title and toolbar on MacOS. Fixes #2968
|
||||
* Confirmation dialog for "console connect to all nodes". Fixes #2971
|
||||
* Add "Resume all suspended links". Fixes #2858
|
||||
* Revert "Change default path for SecureCRT. Fixes #2896"
|
||||
* Remove @property from ConfigurationDialog(). Fixes #2819 #2965
|
||||
* Use Environmental Markers to force jsonschema version. Fixes https://github.com/GNS3/gns3-gui/issues/2849 Version 3.2.0 with Python >= 3.8 Version 2.6.0 with Python < 3.8
|
||||
* Use Environmental Markers to force jsonschema version 2.6.0 on Windows/macOS. Ref https://github.com/GNS3/gns3-gui/issues/2849
|
||||
* Remove preferences dialog geometry restoration. Fixes #2807
|
||||
* Fix unable to configure custom adapters for Qemu VMs. Fixes #2961
|
||||
|
||||
## 2.2.7 07/04/2020
|
||||
|
||||
* Fix VNC console template doesn't extract %i (Project UUID). Fixes #2960
|
||||
* Fix contextual menu issues. Ref #2955
|
||||
|
||||
## 2.2.6 26/03/2020
|
||||
|
||||
* Prevent locked drawings to be deleted. Fixes https://github.com/GNS3/gns3-gui/issues/2948
|
||||
* Fix issues with empty project variables. Fixes https://github.com/GNS3/gns3-gui/issues/2941
|
||||
* Upgrade psutil to version 5.6.6 due to CVE-2019-18874 https://github.com/advisories/GHSA-qfc5-mcwq-26q8
|
||||
* Use existing README.txt if existing when exporting portable project. Fixes https://github.com/GNS3/gns3-server/issues/1724
|
||||
* Allow creation of a diskless Qemu VMs. Fixes #2939
|
||||
* Re-enable "create new version" in appliance wizard. Fixes #2837
|
||||
* Fix unable to load project from project library. Fixes #2932
|
||||
* Fix some permission denied errors when loading remote project. Ref #2871 Fixes #2901
|
||||
* Add 'Royal TS V5' to predefined console list
|
||||
* Disallow invalid grid sized. Fixes #2908
|
||||
* Check if hostname is blank. Fixes #2924
|
||||
* Add nvme disk interface and fix scsi disk interface for Qemu VMs.
|
||||
* Add latest Qemu nic models.
|
||||
* Upgrade Qt version to 5.14.1. Ref #2778 #2903
|
||||
|
||||
## 2.2.5 09/01/2020
|
||||
|
||||
* Add gns3-gui.xml and update Linux icons paths & permissions. Ref #2919
|
||||
|
||||
## 2.2.4 08/01/2020
|
||||
|
||||
* Fix "Console to all nodes" doesn't open cloud objects with console configured. Fixes #2902
|
||||
* Change default path for SecureCRT. Fixes #2896
|
||||
* Add icons in setup.py Ref #2898
|
||||
* Add remote viewer as a VNC console for Linux. Fixes #2913
|
||||
|
||||
## 2.2.3 12/11/2019
|
||||
|
||||
* Fix issue when binding on 0.0.0.0. Fixes #2892
|
||||
* Allow double click on cloud with configured console to open session. Fixes #2894
|
||||
* Officially support Python 3.8. Ref https://github.com/GNS3/gns3-gui/issues/2895
|
||||
* Set psutil to version 5.6.3 in requirements.txt
|
||||
|
||||
## 2.2.2 04/11/2019
|
||||
|
||||
* Fix KeyError: 'spice+agent'. Fixes #2890
|
||||
* Fix wrong log.error() call when exporting file.
|
||||
* Revert "Explicitly cleanup the cache directory."
|
||||
* Fix "UnboundLocalError: local variable 'pywintypes' referenced before assignment"
|
||||
* Fix GUI uses only telnet console. Fixes #2885
|
||||
* Fix missing sys module in sudo.py Fixes #2886
|
||||
|
||||
## 2.2.1 01/11/2019
|
||||
|
||||
* Check if console_type is None.
|
||||
* Explicitly cleanup the cache directory.
|
||||
* Get Windows interface from registry if cannot load win32com module.
|
||||
* Ignore OSError returned by psutil when bringing console to front.
|
||||
* Catch error if NPF or NPCAP service cannot be detected. Ref https://github.com/GNS3/gns3-server/issues/1670
|
||||
* Better handling for reading synchronous JSON response from server. Ref #2874
|
||||
* Fix JSONDecodeError when getting server version. Fixes #2874
|
||||
* Fix FileNotFoundError exceptions when launching SPICE or VNC clients.
|
||||
* Fix UnboundLocalError local variable 'win32serviceutil' referenced before assignment
|
||||
* 'Fix' tab order in preferences dialog so it follows the layout
|
||||
* 'Fix' tab order in edit project dialog so it follows the layout
|
||||
* Use compatible shlex_quote to handle case where Windows needs double quotes around file names, not single quotes. Ref https://github.com/GNS3/gns3-gui/issues/2866
|
||||
* Use 0.0.0.0 by default for server host. Fixes https://github.com/GNS3/gns3-server/issues/1663
|
||||
* Catch IndexError when configuring port names. Fixes #2865
|
||||
|
||||
## 2.2.0 30/09/2019
|
||||
|
||||
* No changes
|
||||
|
||||
## 2.2.0rc5 09/09/2019
|
||||
|
||||
* Adjust size for setup dialog and remove question about running the wizard again. Ref #2846
|
||||
|
||||
## 2.2.0rc4 30/08/2019
|
||||
|
||||
* Fix issue when asking to run the setup wizard again. Ref #2846
|
||||
* Remove warning about VirtualBox not supporting nested virtualization. Ref https://github.com/GNS3/gns3-server/issues/1610
|
||||
* Ask user if they want to see the wizard again. Ref #2846
|
||||
|
||||
## 2.2.0rc3 12/08/2019
|
||||
|
||||
* Revert to jsonschema 2.6.0 due to packaging problem.
|
||||
|
||||
## 2.2.0rc2 10/08/2019
|
||||
|
||||
* Bump jsonschema to version 3.0.2
|
||||
* Fix "Unable to change Remote Main Server IP". Fixes #2823
|
||||
* Fix "AttributeError: 'QGraphicsTextItem' object has no attribute 'locked'". Fixes #2814
|
||||
* Fix a minor typo in the setup wizard
|
||||
|
||||
## 2.2.0b4 11/07/2019
|
||||
|
||||
* Fix issue preventing to open the QFileDialog in the correct directory.
|
||||
* Remove unused edit readme action. Fixes #2816
|
||||
* Remove deprecated Qemu parameter to run legacy ASA VMs. Fixes #2827
|
||||
* Upload images on remote controller. Fixes #2828
|
||||
* Preferences dialog: send API request only if connected to controller
|
||||
* Fix AttributeError: 'QGraphicsTextItem' object has no attribute 'locked'. Fixes #2814
|
||||
* Fix KeyError: 'chassis' when converting old IOS templates. Fixes #2813
|
||||
|
||||
## 2.2.0b3 15/06/2019
|
||||
|
||||
* Fix template migration issues from GUI to controller. Fixes https://github.com/GNS3/gns3-gui/issues/2803
|
||||
* %guest-cid% variable implementation for Qemu VMs. Fixes https://github.com/GNS3/gns3-gui/issues/2804
|
||||
* Increase timeout from 2 to 5 seconds for synchronous check. Ref #2805
|
||||
|
||||
## 2.2.0b2 29/05/2019
|
||||
|
||||
* Fix KeyError: 'endpoint' issue. Fixes #2802
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
# Run tests inside a container
|
||||
FROM ubuntu:18.04
|
||||
|
||||
MAINTAINER GNS3 Team
|
||||
|
||||
#ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
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 clean
|
||||
|
||||
|
||||
ADD dev-requirements.txt /dev-requirements.txt
|
||||
ADD requirements.txt /requirements.txt
|
||||
RUN pip3 install -r /dev-requirements.txt
|
||||
|
||||
|
||||
ADD . /src
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
CMD xvfb-run python3.6 -m pytest -vv
|
||||
|
||||
@@ -3,7 +3,6 @@ include AUTHORS
|
||||
include LICENSE
|
||||
include MANIFEST.in
|
||||
include requirements.txt
|
||||
include tox.ini
|
||||
recursive-include tests *
|
||||
recursive-include gns3 *
|
||||
recursive-include resources *
|
||||
|
||||
16
README.rst
@@ -1,12 +1,15 @@
|
||||
GNS3-gui
|
||||
========
|
||||
|
||||
.. image:: https://travis-ci.org/GNS3/gns3-gui.svg?branch=master
|
||||
:target: https://travis-ci.org/GNS3/gns3-gui
|
||||
.. image:: https://github.com/GNS3/gns3-gui/workflows/testing/badge.svg
|
||||
:target: https://github.com/GNS3/gns3-gui/actions?query=workflow%3Atesting
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/gns3-gui.svg
|
||||
:target: https://pypi.python.org/pypi/gns3-gui
|
||||
|
||||
.. image:: https://snyk.io/test/github/GNS3/gns3-gui/badge.svg
|
||||
:target: https://snyk.io/test/github/GNS3/gns3-gui
|
||||
|
||||
|
||||
GNS3 GUI repository.
|
||||
|
||||
@@ -15,6 +18,15 @@ Installation
|
||||
|
||||
Please see https://docs.gns3.com/
|
||||
|
||||
Software dependencies
|
||||
---------------------
|
||||
|
||||
PyQt5 which is either part of the Linux distribution or installable from PyPi. The other Python dependencies are automatically installed during the GNS3 GUI installation and are listed `here <https://github.com/GNS3/gns3-gui/blob/master/requirements.txt>`_
|
||||
|
||||
For connecting to nodes using Telnet, a Telnet client is required. On Linux that's a terminal emulator like xterm, gnome-terminal, konsole plus the telnet program. For connecting to nodes with a GUI, a VNC client is required, optionally a SPICE client can be used for Qemu nodes.
|
||||
|
||||
For using packet captures within GNS3, Wireshark should be installed. It's recommended, but if you don't need that functionality you can go without it.
|
||||
|
||||
Development
|
||||
-------------
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
-rrequirements.txt
|
||||
|
||||
pep8==1.7.0
|
||||
pytest==4.4.1
|
||||
pytest-pythonpath==0.7.3 # useful for running tests outside tox
|
||||
pytest-timeout==1.3.3
|
||||
pytest==5.4.3
|
||||
flake8==3.8.3
|
||||
pytest-timeout==1.4.1
|
||||
|
||||
@@ -223,17 +223,10 @@ class ConsoleCmd(cmd.Cmd):
|
||||
level = int(args[0])
|
||||
if level == 0:
|
||||
print("Deactivating debugging")
|
||||
for handler in root.handlers:
|
||||
if isinstance(handler, logging.StreamHandler):
|
||||
root.removeHandler(handler)
|
||||
root.setLevel(logging.INFO)
|
||||
else:
|
||||
root.addHandler(logging.StreamHandler(sys.stdout))
|
||||
if level == 1:
|
||||
print("Activating debugging")
|
||||
else:
|
||||
print("Activating full debugging")
|
||||
root.setLevel(logging.DEBUG)
|
||||
print("Activating debugging")
|
||||
root.setLevel(logging.DEBUG)
|
||||
from .main_window import MainWindow
|
||||
MainWindow.instance().setSettings({"debug_level": level})
|
||||
else:
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import psutil
|
||||
import os
|
||||
import platform
|
||||
import struct
|
||||
import distro
|
||||
|
||||
try:
|
||||
import raven
|
||||
from raven.transport.http import HTTPTransport
|
||||
RAVEN_AVAILABLE = True
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||
SENTRY_SDK_AVAILABLE = True
|
||||
except ImportError:
|
||||
# raven is not installed with deb package in order to simplify packaging
|
||||
RAVEN_AVAILABLE = False
|
||||
# Sentry SDK is not installed with deb package in order to simplify packaging
|
||||
SENTRY_SDK_AVAILABLE = False
|
||||
|
||||
from .utils.get_resource import get_resource
|
||||
from .version import __version__, __version_info__
|
||||
@@ -52,66 +51,67 @@ class CrashReport:
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "https://490d29e0583d4af5a05bea9d722c477d:e35f247c273d46fc8244681ad89bd5de@sentry.io/38506"
|
||||
if hasattr(sys, "frozen"):
|
||||
cacert = get_resource("cacert.pem")
|
||||
if cacert is not None and os.path.isfile(cacert):
|
||||
DSN += "?ca_certs={}".format(cacert)
|
||||
else:
|
||||
log.warning("The SSL certificate bundle file '{}' could not be found".format(cacert))
|
||||
DSN = "https://2bd594d79ae248d38422b43a914abc4f:cd35061ce69c4fc99c4dc9e195087512@o19455.ingest.sentry.io/38506"
|
||||
_instance = None
|
||||
|
||||
def __init__(self):
|
||||
# We don't want sentry making noise if an error is catched when you don't have internet
|
||||
# We don't want sentry making noise if an error is caught when we don't have internet
|
||||
sentry_errors = logging.getLogger('sentry.errors')
|
||||
sentry_errors.disabled = True
|
||||
|
||||
sentry_uncaught = logging.getLogger('sentry.errors.uncaught')
|
||||
sentry_uncaught.disabled = True
|
||||
self._sentry_initialized = False
|
||||
|
||||
def captureException(self, exception, value, tb):
|
||||
from .local_server import LocalServer
|
||||
from .local_config import LocalConfig
|
||||
from .controller import Controller
|
||||
from .compute_manager import ComputeManager
|
||||
if SENTRY_SDK_AVAILABLE:
|
||||
cacert = None
|
||||
if hasattr(sys, "frozen"):
|
||||
cacert_resource = get_resource("cacert.pem")
|
||||
if cacert_resource is not None and os.path.isfile(cacert_resource):
|
||||
cacert = cacert_resource
|
||||
else:
|
||||
log.error("The SSL certificate bundle file '{}' could not be found".format(cacert_resource))
|
||||
|
||||
local_server = LocalServer.instance().localServerSettings()
|
||||
if local_server["report_errors"]:
|
||||
if not RAVEN_AVAILABLE:
|
||||
return
|
||||
# Don't send log records as events.
|
||||
sentry_logging = LoggingIntegration(level=logging.INFO, event_level=None)
|
||||
|
||||
if os.path.exists(LocalConfig.instance().runAsRootPath()):
|
||||
log.warning("User has run application as root. Crash reports are disabled.")
|
||||
sys.exit(1)
|
||||
return
|
||||
sentry_sdk.init(dsn=CrashReport.DSN,
|
||||
release=__version__,
|
||||
ca_certs=cacert,
|
||||
default_integrations=False,
|
||||
integrations=[sentry_logging])
|
||||
|
||||
if os.path.exists(".git"):
|
||||
log.warning("A .git directory exist crash report is turn off for developers. Instant exit")
|
||||
sys.exit(1)
|
||||
return
|
||||
|
||||
if hasattr(exception, "fingerprint"):
|
||||
client = raven.Client(CrashReport.DSN, release=__version__, fingerprint=['{{ default }}', exception.fingerprint], transport=HTTPTransport)
|
||||
else:
|
||||
client = raven.Client(CrashReport.DSN, release=__version__, transport=HTTPTransport)
|
||||
context = {
|
||||
tags = {
|
||||
"os:name": platform.system(),
|
||||
"os:release": platform.release(),
|
||||
"os:win_32": " ".join(platform.win32_ver()),
|
||||
"os:mac": "{} {}".format(platform.mac_ver()[0], platform.mac_ver()[2]),
|
||||
"os:linux": " ".join(distro.linux_distribution()),
|
||||
|
||||
}
|
||||
|
||||
self._add_qt_information(tags)
|
||||
|
||||
with sentry_sdk.configure_scope() as scope:
|
||||
for key, value in tags.items():
|
||||
scope.set_tag(key, value)
|
||||
|
||||
extra_context = {
|
||||
"python:version": "{}.{}.{}".format(sys.version_info[0],
|
||||
sys.version_info[1],
|
||||
sys.version_info[2]),
|
||||
"python:bit": struct.calcsize("P") * 8,
|
||||
"python:encoding": sys.getdefaultencoding(),
|
||||
"python:frozen": "{}".format(hasattr(sys, "frozen")),
|
||||
"python:frozen": "{}".format(hasattr(sys, "frozen"))
|
||||
}
|
||||
|
||||
# extra controller and compute information
|
||||
extra_context = {"controller:version": Controller.instance().version(),
|
||||
"controller:host": Controller.instance().host(),
|
||||
"controller:connected": Controller.instance().connected()}
|
||||
from .controller import Controller
|
||||
from .compute_manager import ComputeManager
|
||||
extra_context["controller:version"] = Controller.instance().version()
|
||||
extra_context["controller:host"] = Controller.instance().host()
|
||||
extra_context["controller:connected"] = Controller.instance().connected()
|
||||
|
||||
for index, compute in enumerate(ComputeManager.instance().computes()):
|
||||
extra_context["compute{}:id".format(index)] = compute.id()
|
||||
extra_context["compute{}:name".format(index)] = compute.name(),
|
||||
@@ -120,27 +120,48 @@ class CrashReport:
|
||||
extra_context["compute{}:platform".format(index)] = compute.capabilities().get("platform")
|
||||
extra_context["compute{}:version".format(index)] = compute.capabilities().get("version")
|
||||
|
||||
context = self._add_qt_information(context)
|
||||
client.tags_context(context)
|
||||
client.extra_context(extra_context)
|
||||
try:
|
||||
report = client.captureException((exception, value, tb))
|
||||
except Exception as e:
|
||||
log.error("Can't send crash report to Sentry: {}".format(e))
|
||||
return
|
||||
log.debug("Crash report sent with event ID: {}".format(client.get_ident(report)))
|
||||
with sentry_sdk.configure_scope() as scope:
|
||||
for key, value in extra_context.items():
|
||||
scope.set_extra(key, value)
|
||||
|
||||
def captureException(self, exception, value, tb):
|
||||
|
||||
from .local_server import LocalServer
|
||||
from .local_config import LocalConfig
|
||||
|
||||
local_server = LocalServer.instance().localServerSettings()
|
||||
if local_server["report_errors"]:
|
||||
|
||||
if not SENTRY_SDK_AVAILABLE:
|
||||
log.warning("Cannot capture exception: Sentry SDK is not available")
|
||||
return
|
||||
|
||||
if os.path.exists(LocalConfig.instance().runAsRootPath()):
|
||||
log.warning("User is running application as root. Crash reports disabled.")
|
||||
return
|
||||
|
||||
if not hasattr(sys, "frozen") and os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".git")):
|
||||
log.warning(".git directory detected, crash reporting is turned off for developers.")
|
||||
return
|
||||
|
||||
try:
|
||||
error = (exception, value, tb)
|
||||
sentry_sdk.capture_exception(error=error)
|
||||
log.info("Crash report sent with event ID: {}".format(sentry_sdk.last_event_id()))
|
||||
except Exception as e:
|
||||
log.warning("Can't send crash report to Sentry: {}".format(e))
|
||||
|
||||
def _add_qt_information(self, tags):
|
||||
|
||||
def _add_qt_information(self, context):
|
||||
try:
|
||||
from .qt import QtCore
|
||||
from .qt import sip
|
||||
except ImportError:
|
||||
return context
|
||||
context["psutil:version"] = psutil.__version__
|
||||
context["pyqt:version"] = QtCore.PYQT_VERSION_STR
|
||||
context["qt:version"] = QtCore.QT_VERSION_STR
|
||||
context["sip:version"] = sip.SIP_VERSION_STR
|
||||
return context
|
||||
return tags
|
||||
tags["pyqt:version"] = QtCore.PYQT_VERSION_STR
|
||||
tags["qt:version"] = QtCore.QT_VERSION_STR
|
||||
tags["sip:version"] = sip.SIP_VERSION_STR
|
||||
return tags
|
||||
|
||||
@classmethod
|
||||
def instance(cls):
|
||||
|
||||
@@ -72,9 +72,6 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
self.uiCreateVersionPushButton.clicked.connect(self._createVersionPushButtonClickedSlot)
|
||||
self.allowCustomFiles.clicked.connect(self._allowCustomFilesChangedSlot)
|
||||
|
||||
#FIXME: deactivate the create version feature (confusing and maybe not necessary, TBD)
|
||||
self.uiCreateVersionPushButton.hide()
|
||||
|
||||
# directories where to search for images
|
||||
images_directories = list()
|
||||
|
||||
@@ -478,7 +475,7 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
Allow user to create a new version of an appliance
|
||||
"""
|
||||
|
||||
new_version, ok = QtWidgets.QInputDialog.getText(self, "Creating a new version", "Creating a new version allows to import unknown files to use with this appliance.\nPlease share your experience on the GNS3 community if this version works.\n\nVersion name:", QtWidgets.QLineEdit.Normal)
|
||||
new_version, ok = QtWidgets.QInputDialog.getText(self, "Creating a new version", "Create a new version for this appliance.\nPlease share your experience on the GNS3 community if this version works.\n\nVersion name:", QtWidgets.QLineEdit.Normal)
|
||||
if ok:
|
||||
try:
|
||||
self._appliance.create_new_version(new_version)
|
||||
@@ -616,7 +613,7 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
return
|
||||
for image in appliance_configuration["images"]:
|
||||
if image["location"] == "local":
|
||||
if self._compute_id == "local" and image["path"].startswith(ImageManager.instance().getDirectory()):
|
||||
if not Controller.instance().isRemote() and self._compute_id == "local" and image["path"].startswith(ImageManager.instance().getDirectory()):
|
||||
log.debug("{} is already on the local server".format(image["path"]))
|
||||
return
|
||||
image = Image(self._appliance.emulator(), image["path"], filename=image["filename"])
|
||||
|
||||
@@ -49,7 +49,6 @@ class ConfigurationDialog(QtWidgets.QDialog, Ui_configurationDialog):
|
||||
self._settings = settings
|
||||
self._configuration_page = configuration_page
|
||||
|
||||
@property
|
||||
def settings(self):
|
||||
return self._settings
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ class ConsoleCommandDialog(QtWidgets.QDialog, Ui_uiConsoleCommandDialog):
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
if console_type == "spice+agent":
|
||||
# special case for spice+agent, use the spice console type
|
||||
console_type = "spice"
|
||||
self._console_type = console_type
|
||||
self._current = current
|
||||
|
||||
@@ -63,7 +66,7 @@ class ConsoleCommandDialog(QtWidgets.QDialog, Ui_uiConsoleCommandDialog):
|
||||
elif self._console_type == "vnc":
|
||||
self._consoles = copy.copy(PRECONFIGURED_VNC_CONSOLE_COMMANDS)
|
||||
self._consoles.update(self._settings[self._console_type])
|
||||
elif self._console_type.startswith("spice"):
|
||||
elif self._console_type == "spice":
|
||||
self._consoles = copy.copy(PRECONFIGURED_SPICE_CONSOLE_COMMANDS)
|
||||
self._consoles.update(self._settings[self._console_type])
|
||||
|
||||
@@ -121,8 +124,8 @@ class ConsoleCommandDialog(QtWidgets.QDialog, Ui_uiConsoleCommandDialog):
|
||||
dialog = ConsoleCommandDialog(parent, console_type=console_type, current=current)
|
||||
dialog.show()
|
||||
if dialog.exec_():
|
||||
return (True, dialog.uiCommandPlainTextEdit.toPlainText().replace("\n", " "))
|
||||
return (False, None)
|
||||
return True, dialog.uiCommandPlainTextEdit.toPlainText().replace("\n", " ")
|
||||
return False, None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -108,14 +108,19 @@ class EditProjectDialog(QtWidgets.QDialog, Ui_EditProjectDialog):
|
||||
"""
|
||||
|
||||
if result:
|
||||
self._project.setName(self.uiProjectNameLineEdit.text())
|
||||
self._project.setAutoOpen(self.uiProjectAutoOpenCheckBox.isChecked())
|
||||
self._project.setAutoClose(not self.uiProjectAutoCloseCheckBox.isChecked())
|
||||
self._project.setAutoStart(self.uiProjectAutoStartCheckBox.isChecked())
|
||||
self._project.setSceneHeight(self.uiSceneHeightSpinBox.value())
|
||||
self._project.setSceneWidth(self.uiSceneWidthSpinBox.value())
|
||||
self._project.setNodeGridSize(self.uiNodeGridSizeSpinBox.value())
|
||||
self._project.setDrawingGridSize(self.uiDrawingGridSizeSpinBox.value())
|
||||
self._project.setVariables(self._cleanVariables())
|
||||
self._project.update()
|
||||
node_grid_size = self.uiNodeGridSizeSpinBox.value()
|
||||
drawing_grid_size = self.uiDrawingGridSizeSpinBox.value()
|
||||
if node_grid_size % drawing_grid_size != 0:
|
||||
QtWidgets.QMessageBox.critical(self, "Grid sizes", "Invalid grid sizes which will create overlapping lines")
|
||||
else:
|
||||
self._project.setNodeGridSize(node_grid_size)
|
||||
self._project.setDrawingGridSize(drawing_grid_size)
|
||||
self._project.setName(self.uiProjectNameLineEdit.text())
|
||||
self._project.setAutoOpen(self.uiProjectAutoOpenCheckBox.isChecked())
|
||||
self._project.setAutoClose(not self.uiProjectAutoCloseCheckBox.isChecked())
|
||||
self._project.setAutoStart(self.uiProjectAutoStartCheckBox.isChecked())
|
||||
self._project.setSceneHeight(self.uiSceneHeightSpinBox.value())
|
||||
self._project.setSceneWidth(self.uiSceneWidthSpinBox.value())
|
||||
self._project.setVariables(self._cleanVariables())
|
||||
self._project.update()
|
||||
super().done(result)
|
||||
|
||||
@@ -128,7 +128,7 @@ class PreferencesDialog(QtWidgets.QDialog, Ui_PreferencesDialog):
|
||||
|
||||
# Class name, changed signal
|
||||
widget_to_watch = {
|
||||
#QtWidgets.QLineEdit: "textChanged",
|
||||
QtWidgets.QLineEdit: "textChanged",
|
||||
QtWidgets.QPlainTextEdit: "textChanged",
|
||||
# QtWidgets.QTreeWidget: "itemChanged",
|
||||
QtWidgets.QComboBox: "currentIndexChanged",
|
||||
|
||||
@@ -135,17 +135,20 @@ class ProjectDialog(QtWidgets.QDialog, Ui_ProjectDialog):
|
||||
new_project_name)
|
||||
name = name.strip()
|
||||
if reply and len(name) > 0:
|
||||
|
||||
reset_mac_addresses = self.uiResetMacAddressesCheckBox.isChecked()
|
||||
|
||||
if Controller.instance().isRemote():
|
||||
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id),
|
||||
self._duplicateCallback,
|
||||
body={"name": name},
|
||||
body={"name": name, "reset_mac_addresses": reset_mac_addresses},
|
||||
progressText="Duplicating project '{}'...".format(name),
|
||||
timeout=None)
|
||||
else:
|
||||
project_location = os.path.join(Topology.instance().projectsDirPath(), name)
|
||||
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id),
|
||||
self._duplicateCallback,
|
||||
body={"name": name, "path": project_location},
|
||||
body={"name": name, "path": project_location, "reset_mac_addresses": reset_mac_addresses},
|
||||
progressText="Duplicating project '{}'...".format(name),
|
||||
timeout=None)
|
||||
|
||||
@@ -234,13 +237,13 @@ class ProjectDialog(QtWidgets.QDialog, Ui_ProjectDialog):
|
||||
"""
|
||||
|
||||
menu = QtWidgets.QMenu()
|
||||
menu.triggered.connect(self._menuTriggeredSlot)
|
||||
if Controller.instance().isRemote():
|
||||
for action in self._main_window.recent_project_actions:
|
||||
menu.addAction(action)
|
||||
else:
|
||||
for action in self._main_window.recent_file_actions:
|
||||
menu.addAction(action)
|
||||
menu.triggered.connect(self._menuTriggeredSlot)
|
||||
menu.exec_(QtGui.QCursor.pos())
|
||||
|
||||
def _overwriteProjectCallback(self, result, error=False, **kwargs):
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
|
||||
from gns3.qt import QtCore, QtWidgets
|
||||
@@ -54,9 +55,19 @@ class ExportProjectWizard(QtWidgets.QWizard, Ui_ExportProjectWizard):
|
||||
self.uiCompressionComboBox.setCurrentIndex(1)
|
||||
self.helpRequested.connect(self._showHelpSlot)
|
||||
self.uiPathBrowserToolButton.clicked.connect(self._pathBrowserSlot)
|
||||
self._loadReadme()
|
||||
|
||||
readme_text = "Project: '{}' created on {}\nAuthor: John Doe <john.doe@example.com>\n\nNo project description was given".format(self._project.name(), datetime.date.today())
|
||||
self.uiReadmeTextEdit.setPlainText(readme_text)
|
||||
def _loadReadme(self):
|
||||
|
||||
self._project.get("/files/README.txt", self._loadedReadme)
|
||||
|
||||
def _loadedReadme(self, result, error=False, raw_body=None, context={}, **kwargs):
|
||||
|
||||
if not error:
|
||||
self.uiReadmeTextEdit.setPlainText(raw_body.decode("utf-8", errors="replace"))
|
||||
else:
|
||||
readme_text = "Project: '{}' created on {}\nAuthor: John Doe <john.doe@example.com>\n\nNo project description was given".format(self._project.name(), datetime.date.today())
|
||||
self.uiReadmeTextEdit.setPlainText(readme_text)
|
||||
|
||||
def _pathBrowserSlot(self):
|
||||
|
||||
@@ -123,8 +134,12 @@ class ExportProjectWizard(QtWidgets.QWizard, Ui_ExportProjectWizard):
|
||||
include_snapshots = "yes"
|
||||
else:
|
||||
include_snapshots = "no"
|
||||
if self.uiResetMacAddressesCheckBox.isChecked():
|
||||
reset_mac_addresses = "yes"
|
||||
else:
|
||||
reset_mac_addresses = "no"
|
||||
compression = self.uiCompressionComboBox.currentData()
|
||||
export_worker = ExportProjectWorker(self._project, self._path, include_images, include_snapshots, compression)
|
||||
export_worker = ExportProjectWorker(self._project, self._path, include_images, include_snapshots, reset_mac_addresses, compression)
|
||||
progress_dialog = ProgressDialog(export_worker, "Exporting project", "Exporting portable project files...", "Cancel", parent=self, create_thread=False)
|
||||
progress_dialog.show()
|
||||
progress_dialog.exec_()
|
||||
|
||||
@@ -37,9 +37,7 @@ class ProjectWelcomeDialog(QtWidgets.QDialog, Ui_ProjectWelcomeDialog):
|
||||
self.uiOkButton.clicked.connect(self._okButtonClickedSlot)
|
||||
self.gridLayout.setAlignment(QtCore.Qt.AlignTop)
|
||||
self.label.setOpenExternalLinks(True)
|
||||
|
||||
self._variables = self._getVariables(project)
|
||||
|
||||
self._loadReadme()
|
||||
self._addMisingVariablesEdits()
|
||||
|
||||
@@ -50,10 +48,11 @@ class ProjectWelcomeDialog(QtWidgets.QDialog, Ui_ProjectWelcomeDialog):
|
||||
return variables
|
||||
|
||||
def _addMisingVariablesEdits(self):
|
||||
missing = [v for v in self._variables if v.get("value", "").strip() == ""]
|
||||
#TODO: refactor this to use a QListWidget
|
||||
missing = [v for v in self._variables if v.get("name") and v.get("value", "").strip() == ""]
|
||||
for i, variable in enumerate(missing, start=0):
|
||||
nameLabel = QtWidgets.QLabel()
|
||||
nameLabel.setText(variable.get("name", ""))
|
||||
nameLabel.setText(variable.get("name") + ":")
|
||||
self.gridLayout.addWidget(nameLabel, i, 0)
|
||||
|
||||
valueEdit = QtWidgets.QLineEdit()
|
||||
@@ -72,13 +71,13 @@ class ProjectWelcomeDialog(QtWidgets.QDialog, Ui_ProjectWelcomeDialog):
|
||||
variable["value"] = text
|
||||
|
||||
def _okButtonClickedSlot(self):
|
||||
missing = [v for v in self._variables if v.get("value", "").strip() == ""]
|
||||
missing = [v for v in self._variables if v.get("name") and v.get("value", "").strip() == ""]
|
||||
if len(missing) > 0:
|
||||
reply = QtWidgets.QMessageBox.warning(
|
||||
self, 'Missing values',
|
||||
'Are you sure you want to continue without providing missing values?',
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
reply = QtWidgets.QMessageBox.warning(self,
|
||||
"Missing values",
|
||||
"Are you sure you want to continue without providing missing values?",
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
if reply == QtWidgets.QMessageBox.No:
|
||||
return
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class SetupWizard(QtWidgets.QWizard, Ui_SetupWizard):
|
||||
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
self.adjustSize()
|
||||
|
||||
self._gns3_vm_settings = {
|
||||
"enable": True,
|
||||
@@ -51,7 +52,8 @@ class SetupWizard(QtWidgets.QWizard, Ui_SetupWizard):
|
||||
"engine": "vmware",
|
||||
"vcpus": 1,
|
||||
"ram": 2048,
|
||||
"vmname": "GNS3 VM"
|
||||
"vmname": "GNS3 VM",
|
||||
"port": 80
|
||||
}
|
||||
|
||||
self.setWizardStyle(QtWidgets.QWizard.ModernStyle)
|
||||
@@ -83,10 +85,15 @@ class SetupWizard(QtWidgets.QWizard, Ui_SetupWizard):
|
||||
if address.protocol() in [QtNetwork.QAbstractSocket.IPv4Protocol, QtNetwork.QAbstractSocket.IPv6Protocol]:
|
||||
address_string = address.toString()
|
||||
if address_string.startswith("169.254") or address_string.startswith("fe80"):
|
||||
# ignore link-local addresses
|
||||
# ignore link-local addresses, could not use https://doc.qt.io/qt-5/qhostaddress.html#isLinkLocal
|
||||
# because it was introduced in Qt 5.11
|
||||
continue
|
||||
self.uiLocalServerHostComboBox.addItem(address_string, address_string)
|
||||
|
||||
self.uiLocalServerHostComboBox.addItem("localhost", "localhost") # local host
|
||||
self.uiLocalServerHostComboBox.addItem("::", "::") # all IPv6 addresses
|
||||
self.uiLocalServerHostComboBox.addItem("0.0.0.0", "0.0.0.0") # all IPv4 addresses
|
||||
|
||||
if sys.platform.startswith("darwin"):
|
||||
self.uiVMwareBannerButton.setIcon(QtGui.QIcon(":/images/vmware_fusion_banner.png"))
|
||||
else:
|
||||
@@ -141,7 +148,6 @@ class SetupWizard(QtWidgets.QWizard, Ui_SetupWizard):
|
||||
Slot to refresh the VirtualBox VMs list.
|
||||
"""
|
||||
|
||||
QtWidgets.QMessageBox.warning(self, "GNS3 VM on VirtualBox", "VirtualBox doesn't support nested virtualization, this means running Qemu based VM could be very slow")
|
||||
download_url = "https://github.com/GNS3/gns3-gui/releases/download/v{version}/GNS3.VM.VirtualBox.{version}.zip".format(version=__version__)
|
||||
self.uiGNS3VMDownloadLinkUrlLabel.setText('If you don\'t have the GNS3 Virtual Machine you can <a href="{download_url}">download it here</a>.<br>And import the VM in the virtualization software and hit refresh.'.format(download_url=download_url))
|
||||
self.uiVmwareRadioButton.setChecked(False)
|
||||
@@ -414,11 +420,8 @@ class SetupWizard(QtWidgets.QWizard, Ui_SetupWizard):
|
||||
settings["hide_setup_wizard"] = True
|
||||
else:
|
||||
local_server_settings = LocalServer.instance().localServerSettings()
|
||||
if local_server_settings["host"] is None:
|
||||
local_server_settings["host"] = DEFAULT_LOCAL_SERVER_HOST
|
||||
LocalServer.instance().updateLocalServerSettings(local_server_settings)
|
||||
settings["hide_setup_wizard"] = self.uiShowCheckBox.isChecked()
|
||||
|
||||
LocalServer.instance().updateLocalServerSettings(local_server_settings)
|
||||
settings["hide_setup_wizard"] = not self.uiShowCheckBox.isChecked()
|
||||
self.parentWidget().setSettings(settings)
|
||||
super().done(result)
|
||||
|
||||
|
||||
@@ -460,25 +460,15 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
else:
|
||||
item.setSelected(True)
|
||||
elif is_not_link and is_not_logo and event.button() == QtCore.Qt.RightButton and not self._adding_link:
|
||||
if item and not sip.isdeleted(item):
|
||||
# Prevent right clicking on a selected item from de-selecting all other items
|
||||
if not item.isSelected():
|
||||
if not event.modifiers() & QtCore.Qt.ControlModifier:
|
||||
for it in self.scene().items():
|
||||
it.setSelected(False)
|
||||
item.setSelected(True)
|
||||
self._showDeviceContextualMenu(QtGui.QCursor.pos())
|
||||
else:
|
||||
self._showDeviceContextualMenu(QtGui.QCursor.pos())
|
||||
# when more than one item is selected display the contextual menu even if mouse is not above an item
|
||||
elif len(self.scene().selectedItems()) > 1:
|
||||
self._showDeviceContextualMenu(QtGui.QCursor.pos())
|
||||
pass #TODO: remove this without creating a bug...
|
||||
elif is_not_link and self._adding_link and event.button() == QtCore.Qt.RightButton:
|
||||
# send a escape key to the main window to cancel the link addition
|
||||
key = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Escape, QtCore.Qt.NoModifier)
|
||||
QtWidgets.QApplication.sendEvent(self._main_window, key)
|
||||
elif item and isinstance(item, NodeItem) and self._adding_link and event.button() == QtCore.Qt.LeftButton:
|
||||
self._userNodeLinking(event, item)
|
||||
#context_event = QtGui.QContextMenuEvent(QtGui.QContextMenuEvent.Mouse, event.pos())
|
||||
#QtWidgets.QApplication.sendEvent(self, context_event)
|
||||
elif event.button() == QtCore.Qt.LeftButton and self._adding_note:
|
||||
pos = self.mapToScene(event.pos())
|
||||
note = self.createDrawingItem("text", pos.x(), pos.y(), 2)
|
||||
@@ -512,6 +502,46 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
|
||||
self.toggleUiDeviceMenu()
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
"""
|
||||
Handles all context menu events.
|
||||
|
||||
:param event: QContextMenuEvent instance
|
||||
"""
|
||||
|
||||
is_not_link = True
|
||||
is_not_logo = True
|
||||
|
||||
item = self.itemAt(event.pos())
|
||||
if item and sip.isdeleted(item):
|
||||
return
|
||||
|
||||
if item and (isinstance(item, LinkItem) or isinstance(item.parentItem(), LinkItem)):
|
||||
is_not_link = False
|
||||
if item and (isinstance(item, LogoItem) or isinstance(item.parentItem(), LogoItem)):
|
||||
is_not_logo = False
|
||||
else:
|
||||
for it in self.scene().items():
|
||||
if isinstance(it, LinkItem):
|
||||
it.setHovered(False)
|
||||
|
||||
if is_not_link and is_not_logo and not self._adding_link:
|
||||
if item and not sip.isdeleted(item):
|
||||
# Prevent right clicking on a selected item from de-selecting all other items
|
||||
if not item.isSelected():
|
||||
if not event.modifiers() & QtCore.Qt.ControlModifier:
|
||||
for it in self.scene().items():
|
||||
it.setSelected(False)
|
||||
item.setSelected(True)
|
||||
self._showDeviceContextualMenu(event.globalPos())
|
||||
# when more than one item is selected display the contextual menu even if mouse is not above an item
|
||||
elif len(self.scene().selectedItems()) > 1:
|
||||
self._showDeviceContextualMenu(event.globalPos())
|
||||
#elif item and isinstance(item, NodeItem) and self._adding_link:
|
||||
# self._userNodeLinking(event, item)
|
||||
else:
|
||||
super().contextMenuEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
"""
|
||||
Handles all mouse release events.
|
||||
@@ -627,7 +657,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
if not self._adding_link:
|
||||
if isinstance(item, NodeItem) and item.node().initialized():
|
||||
item.setSelected(True)
|
||||
if item.node().status() == Node.stopped or item.node().isAlwaysOn():
|
||||
if item.node().status() == Node.stopped or item.node().consoleType() == "none":
|
||||
self.configureSlot()
|
||||
return
|
||||
else:
|
||||
@@ -724,6 +754,11 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
self.populateDeviceContextualMenu(menu)
|
||||
menu.exec_(pos)
|
||||
menu.clear()
|
||||
# Make sure to deselect all items.
|
||||
# This is to prevent a bug on Windows
|
||||
# see https://github.com/GNS3/gns3-gui/issues/2986
|
||||
for it in self.scene().items():
|
||||
it.setSelected(False)
|
||||
|
||||
def populateDeviceContextualMenu(self, menu):
|
||||
"""
|
||||
@@ -988,6 +1023,9 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
if isinstance(item, NodeItem) and item.node().initialized():
|
||||
new_hostname, ok = QtWidgets.QInputDialog.getText(self, "Change hostname", "Hostname:", QtWidgets.QLineEdit.Normal, item.node().name())
|
||||
if ok:
|
||||
if not new_hostname.strip():
|
||||
QtWidgets.QMessageBox.critical(self, "Change hostname", "Hostname cannot be blank")
|
||||
continue
|
||||
if hasattr(item.node(), "validateHostname"):
|
||||
if not item.node().validateHostname(new_hostname):
|
||||
QtWidgets.QMessageBox.critical(self, "Change hostname", "Invalid name detected for this node: {}".format(new_hostname))
|
||||
@@ -1054,8 +1092,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
|
||||
# TightVNC has lack support of IPv6 host at this moment
|
||||
if "vncviewer" in node.consoleCommand() and ":" in node.consoleHost():
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, "TightVNC", "TightVNC (vncviewer) may not start because of lack of IPv6 support.")
|
||||
QtWidgets.QMessageBox.warning(self, "TightVNC", "TightVNC (vncviewer) may not start because of lack of IPv6 support.")
|
||||
|
||||
try:
|
||||
node.openConsole(aux=aux)
|
||||
@@ -1096,11 +1133,21 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
|
||||
def consoleFromAllItems(self):
|
||||
"""
|
||||
Console from all scene items, except builtin devices.
|
||||
Console from all scene items with console type different than "none"
|
||||
"""
|
||||
|
||||
items = [item for item in self.scene().items()
|
||||
if not (isinstance(item, NodeItem) and isinstance(item.node().module(), Builtin))]
|
||||
if isinstance(item, NodeItem) and item.node().consoleType() != "none"]
|
||||
nb_items = len(items)
|
||||
if nb_items > 10:
|
||||
proceed = QtWidgets.QMessageBox.question(self,
|
||||
"Console to all nodes",
|
||||
"You are about to open console windows to {} nodes. Are you sure?".format(nb_items),
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if proceed == QtWidgets.QMessageBox.No:
|
||||
return
|
||||
self.consoleFromItems(items)
|
||||
|
||||
def consoleActionSlot(self):
|
||||
@@ -1116,24 +1163,20 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
Allow user to use a custom console for this VM
|
||||
"""
|
||||
|
||||
current_cmd = None
|
||||
console_type = "telnet"
|
||||
for item in self.scene().selectedItems():
|
||||
if isinstance(item, NodeItem) and item.node().console() is not None and item.node().initialized() and item.node().status() == Node.started:
|
||||
if isinstance(item, NodeItem) and item.node().console() is not None and item.node().initialized():
|
||||
if item.node().consoleType() not in ("telnet", "serial", "vnc", "spice", "spice+agent"):
|
||||
continue
|
||||
current_cmd = item.node().consoleCommand()
|
||||
console_type = item.node().consoleType()
|
||||
|
||||
(ok, cmd) = ConsoleCommandDialog.getCommand(self, console_type=console_type, current=current_cmd)
|
||||
if ok:
|
||||
for item in self.scene().selectedItems():
|
||||
if isinstance(item, NodeItem) and item.node().console() is not None and item.node().initialized() and item.node().status() == Node.started:
|
||||
node = item.node()
|
||||
if node.consoleType() not in ("telnet", "serial", "vnc", "spice", "spice+agent"):
|
||||
continue
|
||||
(ok, cmd) = ConsoleCommandDialog.getCommand(self, console_type=console_type, current=current_cmd)
|
||||
if ok:
|
||||
try:
|
||||
node.openConsole(command=cmd)
|
||||
if item.node().status() != Node.started:
|
||||
QtWidgets.QMessageBox.warning(self, "Console", "This node must be started before a console can be opened")
|
||||
continue
|
||||
item.node().openConsole(command=cmd)
|
||||
except (OSError, ValueError) as e:
|
||||
QtWidgets.QMessageBox.critical(self, "Console", "Cannot start console application: {}".format(e))
|
||||
|
||||
@@ -1508,6 +1551,9 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
QtWidgets.QMessageBox.critical(self, "Delete", "Cannot delete node '{}' because it is locked".format(node.name()))
|
||||
return
|
||||
selected_nodes.append(node)
|
||||
if isinstance(item, DrawingItem) and item.locked():
|
||||
QtWidgets.QMessageBox.critical(self, "Delete", "Cannot delete drawing because it is locked")
|
||||
return
|
||||
if selected_nodes:
|
||||
if len(selected_nodes) > 1:
|
||||
question = "Do you want to permanently delete these {} nodes?".format(len(selected_nodes))
|
||||
|
||||
@@ -727,7 +727,7 @@ class HTTPClient(QtCore.QObject):
|
||||
e = HttpBadRequest(body)
|
||||
raise e
|
||||
|
||||
def getSynchronous(self, method, endpoint, prefix="/v2", timeout=2):
|
||||
def getSynchronous(self, method, endpoint, prefix="/v2", timeout=5):
|
||||
"""
|
||||
Synchronous check if a server is running
|
||||
|
||||
@@ -764,18 +764,17 @@ class HTTPClient(QtCore.QObject):
|
||||
status = response.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute)
|
||||
if response.error() != QtNetwork.QNetworkReply.NoError:
|
||||
log.debug("Error while connecting to local server {}".format(response.errorString()))
|
||||
return status, None
|
||||
else:
|
||||
content_type = response.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
|
||||
if status == 200:
|
||||
if content_type == "application/json":
|
||||
content = bytes(response.readAll())
|
||||
if status == 200 and content_type == "application/json":
|
||||
content = bytes(response.readAll())
|
||||
try:
|
||||
json_data = json.loads(content.decode("utf-8"))
|
||||
except (UnicodeEncodeError, ValueError) as e:
|
||||
log.warning("Could not read JSON data returned from {}: {}".format(url, e))
|
||||
else:
|
||||
return status, json_data
|
||||
else:
|
||||
return status, None
|
||||
|
||||
return 0, None
|
||||
return status, None
|
||||
|
||||
@classmethod
|
||||
def fromUrl(cls, url, network_manager=None, base_settings=None):
|
||||
|
||||
@@ -212,14 +212,13 @@ class DrawingItem:
|
||||
self._project.delete("/drawings/" + self._id, None, body=self.__json__())
|
||||
|
||||
def itemChange(self, change, value):
|
||||
if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged and self.isActive() and self._main_window.uiSnapToGridAction.isChecked():
|
||||
|
||||
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isActive() and self._main_window.uiSnapToGridAction.isChecked():
|
||||
grid_size = self._graphics_view.drawingGridSize()
|
||||
mid_x = self.boundingRect().width() / 2
|
||||
tmp_x = (grid_size * round((self.x() + mid_x) / grid_size)) - mid_x
|
||||
value.setX((grid_size * round((value.x() + mid_x) / grid_size)) - mid_x)
|
||||
mid_y = self.boundingRect().height() / 2
|
||||
tmp_y = (grid_size * round((self.y() + mid_y) / grid_size)) - mid_y
|
||||
if tmp_x != self.x() and tmp_y != self.y():
|
||||
self.setPos(tmp_x, tmp_y)
|
||||
value.setY((grid_size * round((value.y()+mid_y)/grid_size)) - mid_y)
|
||||
|
||||
if change == QtWidgets.QGraphicsItem.ItemSelectedChange:
|
||||
if not value:
|
||||
|
||||
@@ -280,23 +280,31 @@ class LinkItem(QtWidgets.QGraphicsPathItem):
|
||||
:param: QGraphicsSceneMouseEvent instance
|
||||
"""
|
||||
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
if self._adding_flag:
|
||||
# send a escape key to the main window to cancel the link addition
|
||||
from ..main_window import MainWindow
|
||||
key = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Escape, QtCore.Qt.NoModifier)
|
||||
QtWidgets.QApplication.sendEvent(MainWindow.instance(), key)
|
||||
return
|
||||
if event.button() == QtCore.Qt.RightButton and self._adding_flag:
|
||||
# send a escape key to the main window to cancel the link addition
|
||||
from ..main_window import MainWindow
|
||||
key = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Escape, QtCore.Qt.NoModifier)
|
||||
QtWidgets.QApplication.sendEvent(MainWindow.instance(), key)
|
||||
return
|
||||
else:
|
||||
super().mousePressEvent(event)
|
||||
|
||||
if not sip_is_deleted(self):
|
||||
# create the contextual menu
|
||||
self.setAcceptHoverEvents(False)
|
||||
menu = QtWidgets.QMenu()
|
||||
self.populateLinkContextualMenu(menu)
|
||||
menu.exec_(QtGui.QCursor.pos())
|
||||
self.setAcceptHoverEvents(True)
|
||||
self._hovered = False
|
||||
self.adjust()
|
||||
def contextMenuEvent(self, event):
|
||||
"""
|
||||
Handles all context menu events.
|
||||
|
||||
:param event: QContextMenuEvent instance
|
||||
"""
|
||||
|
||||
if not sip_is_deleted(self):
|
||||
# create the contextual menu
|
||||
self.setAcceptHoverEvents(False)
|
||||
menu = QtWidgets.QMenu()
|
||||
self.populateLinkContextualMenu(menu)
|
||||
menu.exec_(QtGui.QCursor.pos())
|
||||
self.setAcceptHoverEvents(True)
|
||||
self._hovered = False
|
||||
self.adjust()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
|
||||
@@ -488,7 +488,7 @@ class LocalConfig(QtCore.QObject):
|
||||
if pid != my_pid:
|
||||
try:
|
||||
process = psutil.Process(pid=pid)
|
||||
ps_name = process.name()
|
||||
ps_name = process.name().lower()
|
||||
except (OSError, psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
pass
|
||||
else:
|
||||
|
||||
@@ -31,7 +31,7 @@ import subprocess
|
||||
|
||||
|
||||
from gns3.qt import QtWidgets, QtCore, qslot
|
||||
from gns3.settings import LOCAL_SERVER_SETTINGS
|
||||
from gns3.settings import LOCAL_SERVER_SETTINGS, DEFAULT_LOCAL_SERVER_HOST
|
||||
from gns3.local_config import LocalConfig
|
||||
from gns3.local_server_config import LocalServerConfig
|
||||
from gns3.utils.wait_for_connection_worker import WaitForConnectionWorker
|
||||
@@ -130,6 +130,7 @@ class LocalServer(QtCore.QObject):
|
||||
import win32serviceutil
|
||||
except ImportError as e:
|
||||
log.error("Could not check if the {} service is running: {}".format(service_name, e))
|
||||
return
|
||||
|
||||
try:
|
||||
if win32serviceutil.QueryServiceStatus(service_name, None)[1] != win32service.SERVICE_RUNNING:
|
||||
@@ -245,6 +246,9 @@ class LocalServer(QtCore.QObject):
|
||||
"""
|
||||
Update the local server settings. Keep the key not in new_settings
|
||||
"""
|
||||
|
||||
if "host" in new_settings and new_settings["host"] is None:
|
||||
new_settings["host"] = DEFAULT_LOCAL_SERVER_HOST
|
||||
old_settings = copy.copy(self._settings)
|
||||
if not self._settings:
|
||||
self._settings = new_settings
|
||||
@@ -368,9 +372,13 @@ class LocalServer(QtCore.QObject):
|
||||
|
||||
self._checkUbridgePermissions()
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
if not self._checkWindowsService("npf") and not self._checkWindowsService("npcap"):
|
||||
log.warning("The NPF or NPCAP service is not installed, please install Winpcap or Npcap and reboot.")
|
||||
if sys.platform.startswith("win"):
|
||||
import pywintypes
|
||||
try:
|
||||
if not self._checkWindowsService("npf") and not self._checkWindowsService("npcap"):
|
||||
log.warning("The NPF or NPCAP service is not installed, please install Winpcap or Npcap and reboot.")
|
||||
except pywintypes.error as e:
|
||||
log.warning("Could not check if the NPF or Npcap service is running: {}".format(e.strerror))
|
||||
|
||||
self._port = self._settings["port"]
|
||||
# check the local server path
|
||||
@@ -513,7 +521,7 @@ class LocalServer(QtCore.QObject):
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
status, json_data = HTTPClient(self._settings).getSynchronous("GET", "/version", timeout=2)
|
||||
status, json_data = HTTPClient(self._settings).getSynchronous("GET", "/version")
|
||||
if status == 401: # Auth issue that need to be solved later
|
||||
return True
|
||||
elif json_data is None:
|
||||
|
||||
@@ -84,6 +84,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self._settings = {}
|
||||
|
||||
self.setupUi(self)
|
||||
self.setUnifiedTitleAndToolBarOnMac(True)
|
||||
|
||||
self._notif_dialog = NotifDialog(self)
|
||||
# Setup logger
|
||||
@@ -190,7 +191,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self.uiSnapshotAction,
|
||||
self.uiEditProjectAction,
|
||||
self.uiDeleteProjectAction,
|
||||
self.uiImportExportConfigsAction
|
||||
self.uiImportExportConfigsAction,
|
||||
self.uiLockAllAction
|
||||
]
|
||||
|
||||
# This widgets are not enabled if it's a remote controller (no access to the local file system)
|
||||
@@ -239,7 +241,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self.uiLockAllAction.triggered.connect(self._lockActionSlot)
|
||||
|
||||
# tool menu connections
|
||||
self.uiWebInterfaceAction.triggered.connect(self._openLightWebInterfaceActionSlot)
|
||||
self.uiWebUIAction.triggered.connect(self._openWebInterfaceActionSlot)
|
||||
|
||||
# control menu connections
|
||||
@@ -322,10 +323,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
LocalConfig.instance().saveSectionSettings(self.__class__.__name__, self._settings)
|
||||
self.settings_updated_signal.emit()
|
||||
|
||||
def _openLightWebInterfaceActionSlot(self):
|
||||
if Controller.instance().connected():
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl(Controller.instance().httpClient().fullUrl()))
|
||||
|
||||
def _openWebInterfaceActionSlot(self):
|
||||
if Controller.instance().connected():
|
||||
base_url = Controller.instance().httpClient().fullUrl()
|
||||
@@ -363,15 +360,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
:return: None
|
||||
"""
|
||||
|
||||
for item in self.uiGraphicsView.items():
|
||||
if not isinstance(item, LinkItem) and not isinstance(item, LabelItem) and not isinstance(item, SvgIconItem):
|
||||
if self.uiLockAllAction.isChecked() and not item.locked():
|
||||
item.setLocked(True)
|
||||
elif not self.uiLockAllAction.isChecked() and item.locked():
|
||||
item.setLocked(False)
|
||||
if item.parentItem() is None:
|
||||
item.updateNode()
|
||||
item.update()
|
||||
if self.uiGraphicsView.isEnabled():
|
||||
for item in self.uiGraphicsView.items():
|
||||
if not isinstance(item, LinkItem) and not isinstance(item, LabelItem) and not isinstance(item, SvgIconItem):
|
||||
if self.uiLockAllAction.isChecked() and not item.locked():
|
||||
item.setLocked(True)
|
||||
elif not self.uiLockAllAction.isChecked() and item.locked():
|
||||
item.setLocked(False)
|
||||
if item.parentItem() is None:
|
||||
item.updateNode()
|
||||
item.update()
|
||||
|
||||
def analyticsClient(self):
|
||||
"""
|
||||
@@ -433,7 +431,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self._newProjectActionSlot()
|
||||
else:
|
||||
directory = self._project_dir
|
||||
if self._project_dir and not os.path.exists(self._project_dir):
|
||||
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)",
|
||||
@@ -913,7 +911,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
Slot to launch a browser pointing to the documentation page.
|
||||
"""
|
||||
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://gns3.com/support/docs"))
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://docs.gns3.com/"))
|
||||
|
||||
def _checkForUpdateActionSlot(self, silent=False):
|
||||
"""
|
||||
@@ -1057,11 +1055,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
|
||||
with Progress.instance().context(min_duration=0):
|
||||
dialog = PreferencesDialog(self)
|
||||
dialog.restoreGeometry(QtCore.QByteArray().fromBase64(self._settings["preferences_dialog_geometry"].encode()))
|
||||
#dialog.restoreGeometry(QtCore.QByteArray().fromBase64(self._settings["preferences_dialog_geometry"].encode()))
|
||||
dialog.show()
|
||||
dialog.exec_()
|
||||
self._settings["preferences_dialog_geometry"] = bytes(dialog.saveGeometry().toBase64()).decode()
|
||||
self.setSettings(self._settings)
|
||||
#self._settings["preferences_dialog_geometry"] = bytes(dialog.saveGeometry().toBase64()).decode()
|
||||
#self.setSettings(self._settings)
|
||||
|
||||
def _editReadmeActionSlot(self):
|
||||
"""
|
||||
@@ -1187,8 +1185,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
|
||||
# restore debug level
|
||||
if self._settings["debug_level"]:
|
||||
print("Activating debugging (use command 'debug 0' to deactivate)")
|
||||
root = logging.getLogger()
|
||||
root.addHandler(logging.StreamHandler(sys.stdout))
|
||||
root.setLevel(logging.DEBUG)
|
||||
|
||||
# restore the style
|
||||
self._setStyle(self._settings.get("style"))
|
||||
|
||||
@@ -130,7 +130,7 @@ class Dynamips(Module):
|
||||
for router in self._settings.get("routers"):
|
||||
router_settings = IOS_ROUTER_SETTINGS.copy()
|
||||
router_settings.update(router)
|
||||
if not router_settings.get("chassis"):
|
||||
if router_settings.get("chassis"):
|
||||
del router_settings["chassis"]
|
||||
templates.append(Template(router_settings))
|
||||
TemplateManager.instance().updateList(templates)
|
||||
|
||||
@@ -210,7 +210,7 @@ class IOUDevicePreferencesPage(QtWidgets.QWidget, Ui_IOUDevicePreferencesPageWid
|
||||
del self._iou_devices[key]
|
||||
item.setText(0, iou_device["name"])
|
||||
item.setData(0, QtCore.Qt.UserRole, new_key)
|
||||
self._refreshInfo(dialog.settings)
|
||||
self._refreshInfo(dialog.settings())
|
||||
|
||||
def _iouDeviceDeleteSlot(self):
|
||||
"""
|
||||
|
||||
@@ -98,7 +98,10 @@ class IOUPreferencesPage(QtWidgets.QWidget, Ui_IOUPreferencesPageWidget):
|
||||
:param settings: IOU settings
|
||||
"""
|
||||
|
||||
self.IOULicenceTextEdit.setPlainText(settings["iourc_content"])
|
||||
if settings["iourc_content"]:
|
||||
self.IOULicenceTextEdit.blockSignals(True)
|
||||
self.IOULicenceTextEdit.setPlainText(settings["iourc_content"])
|
||||
self.IOULicenceTextEdit.blockSignals(False)
|
||||
self.uiLicensecheckBox.setChecked(settings["license_check"])
|
||||
|
||||
def loadPreferences(self):
|
||||
@@ -106,7 +109,10 @@ class IOUPreferencesPage(QtWidgets.QWidget, Ui_IOUPreferencesPageWidget):
|
||||
Loads IOU preferences.
|
||||
"""
|
||||
|
||||
Controller.instance().get("/iou_license", self._getSettingsCallback)
|
||||
if Controller.instance().connected():
|
||||
Controller.instance().get("/iou_license", self._getSettingsCallback)
|
||||
else:
|
||||
log.error("Cannot load the IOU license in the preferences dialog: not connected to the controller")
|
||||
|
||||
@qslot
|
||||
def _getSettingsCallback(self, result, error=False, **kwargs):
|
||||
@@ -115,7 +121,7 @@ class IOUPreferencesPage(QtWidgets.QWidget, Ui_IOUPreferencesPageWidget):
|
||||
return
|
||||
if error:
|
||||
if "message" in result:
|
||||
log.error("Error while getting settings : {}".format(result["message"]))
|
||||
log.error("Error while getting the IOU license information: {}".format(result["message"]))
|
||||
return
|
||||
self._old_settings = copy.copy(result)
|
||||
self._populateWidgets(result)
|
||||
|
||||
@@ -47,7 +47,6 @@ class QemuVMWizard(VMWithImagesWizard, Ui_QemuVMWizard):
|
||||
|
||||
# Mandatory fields
|
||||
self.uiNameWizardPage.registerField("vm_name*", self.uiNameLineEdit)
|
||||
self.uiDiskWizardPage.registerField("hda_disk_image*", self.uiHdaDiskImageLineEdit)
|
||||
self.uiInitrdKernelImageWizardPage.registerField("initrd*", self.uiInitrdImageLineEdit)
|
||||
self.uiInitrdKernelImageWizardPage.registerField("kernel_image*", self.uiKernelImageLineEdit)
|
||||
|
||||
@@ -153,17 +152,19 @@ class QemuVMWizard(VMWithImagesWizard, Ui_QemuVMWizard):
|
||||
"qemu_path": qemu_path,
|
||||
"compute_id": self._compute_id,
|
||||
"category": Node.end_devices,
|
||||
"hda_disk_image": self.uiHdaDiskImageLineEdit.text(),
|
||||
"console_type": console_type
|
||||
}
|
||||
|
||||
if self.uiHdaDiskImageLineEdit.text().strip():
|
||||
settings["hda_disk_image"] = self.uiHdaDiskImageLineEdit.text().strip()
|
||||
|
||||
if self.uiLegacyASACheckBox.isChecked():
|
||||
# special settings for legacy ASA VM
|
||||
settings["adapters"] = 4
|
||||
settings["initrd"] = self.uiInitrdImageLineEdit.text()
|
||||
settings["kernel_image"] = self.uiKernelImageLineEdit.text()
|
||||
settings["kernel_command_line"] = "ide_generic.probe_mask=0x01 ide_core.chs=0.0:980,16,32 auto nousb console=ttyS0,9600 bigphysarea=65536 ide1=noprobe no-hlt -net nic"
|
||||
settings["options"] = "-no-kvm -icount auto -hdachs 980,16,32"
|
||||
settings["options"] = "-no-kvm -icount auto"
|
||||
if not sys.platform.startswith("darwin"):
|
||||
settings["cpu_throttling"] = 80 # limit to 80% CPU usage
|
||||
settings["process_priority"] = "low"
|
||||
|
||||
@@ -78,7 +78,7 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
self.uiHdcDiskImageResizeToolButton.clicked.connect(self._hdcDiskImageResizeSlot)
|
||||
self.uiHddDiskImageResizeToolButton.clicked.connect(self._hddDiskImageResizeSlot)
|
||||
|
||||
disk_interfaces = ["ide", "sata", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"]
|
||||
disk_interfaces = ["ide", "sata", "nvme", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"]
|
||||
self.uiHdaDiskInterfaceComboBox.addItems(disk_interfaces)
|
||||
self.uiHdbDiskInterfaceComboBox.addItems(disk_interfaces)
|
||||
self.uiHdcDiskInterfaceComboBox.addItems(disk_interfaces)
|
||||
@@ -99,8 +99,13 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
for name, option_name in Node.onCloseOptions().items():
|
||||
self.uiOnCloseComboBox.addItem(name, option_name)
|
||||
|
||||
# Supported NIC models: e1000, e1000-82544gc, e1000-82545em, e1000e, i82550, i82551, i82557a, i82557b, i82557c, i82558a
|
||||
# i82558b, i82559a, i82559b, i82559c, i82559er, i82562, i82801, ne2k_pci, pcnet, rocker, rtl8139, virtio-net-pci, vmxnet3
|
||||
self._legacy_devices = ("e1000", "i82551", "i82557b", "i82559er", "ne2k_pci", "pcnet", "rtl8139", "virtio")
|
||||
self._qemu_network_devices = OrderedDict([("e1000", "Intel Gigabit Ethernet"),
|
||||
("e1000-82544gc", "Intel 82544GC Gigabit Ethernet"),
|
||||
("e1000-82545em", "Intel 82545EM Gigabit Ethernet"),
|
||||
("e1000e", "Intel PCIe Gigabit Ethernet"),
|
||||
("i82550", "Intel i82550 Ethernet"),
|
||||
("i82551", "Intel i82551 Ethernet"),
|
||||
("i82557a", "Intel i82557A Ethernet"),
|
||||
@@ -116,6 +121,7 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
("i82801", "Intel i82801 Ethernet"),
|
||||
("ne2k_pci", "NE2000 Ethernet"),
|
||||
("pcnet", "AMD PCNet Ethernet"),
|
||||
("rocker", "Rocker L2 switch device"),
|
||||
("rtl8139", "Realtek 8139 Ethernet"),
|
||||
("virtio", "Legacy paravirtualized Network I/O"),
|
||||
("virtio-net-pci", "Paravirtualized Network I/O"),
|
||||
@@ -391,11 +397,19 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
|
||||
try:
|
||||
ports = StandardPortNameFactory(adapters, first_port_name, port_name_format, port_segment_size)
|
||||
except (ValueError, KeyError):
|
||||
except (IndexError, ValueError, KeyError):
|
||||
QtWidgets.QMessageBox.critical(self, "Invalid format", "Invalid port name format")
|
||||
return
|
||||
|
||||
dialog = CustomAdaptersConfigurationDialog(ports, self._custom_adapters, default_adapter, self._qemu_network_devices, base_mac_address, parent=self)
|
||||
if self.uiLegacyNetworkingCheckBox.isChecked():
|
||||
network_devices = {}
|
||||
for nic, desc in self._qemu_network_devices.items():
|
||||
if nic in self._legacy_devices:
|
||||
network_devices[nic] = desc
|
||||
else:
|
||||
network_devices = self._qemu_network_devices
|
||||
|
||||
dialog = CustomAdaptersConfigurationDialog(ports, self._custom_adapters, default_adapter, network_devices, base_mac_address, parent=self)
|
||||
dialog.show()
|
||||
dialog.exec_()
|
||||
|
||||
@@ -511,6 +525,7 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
self._custom_adapters = settings["custom_adapters"].copy()
|
||||
|
||||
self.uiLegacyNetworkingCheckBox.setChecked(settings["legacy_networking"])
|
||||
self.uiReplicateNetworkConnectionStateCheckBox.setChecked(settings["replicate_network_connection_state"])
|
||||
|
||||
# load the MAC address setting
|
||||
self.uiMacAddrLineEdit.setInputMask("HH:HH:HH:HH:HH:HH;_")
|
||||
@@ -642,6 +657,8 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
raise ConfigurationError()
|
||||
|
||||
settings["adapters"] = adapters
|
||||
settings["legacy_networking"] = self.uiLegacyNetworkingCheckBox.isChecked()
|
||||
settings["replicate_network_connection_state"] = self.uiReplicateNetworkConnectionStateCheckBox.isChecked()
|
||||
settings["custom_adapters"] = self._custom_adapters.copy()
|
||||
settings["on_close"] = self.uiOnCloseComboBox.itemData(self.uiOnCloseComboBox.currentIndex())
|
||||
settings["cpus"] = self.uiCPUSpinBox.value()
|
||||
|
||||
@@ -71,6 +71,7 @@ class QemuVM(Node):
|
||||
"adapter_type": QEMU_VM_SETTINGS["adapter_type"],
|
||||
"mac_address": QEMU_VM_SETTINGS["mac_address"],
|
||||
"legacy_networking": QEMU_VM_SETTINGS["legacy_networking"],
|
||||
"replicate_network_connection_state": QEMU_VM_SETTINGS["replicate_network_connection_state"],
|
||||
"platform": QEMU_VM_SETTINGS["platform"],
|
||||
"on_close": QEMU_VM_SETTINGS["on_close"],
|
||||
"cpu_throttling": QEMU_VM_SETTINGS["cpu_throttling"],
|
||||
|
||||
@@ -56,6 +56,7 @@ QEMU_VM_SETTINGS = {
|
||||
"adapter_type": "e1000",
|
||||
"mac_address": "",
|
||||
"legacy_networking": False,
|
||||
"replicate_network_connection_state": True,
|
||||
"on_close": "power_off",
|
||||
"platform": "",
|
||||
"cpu_throttling": 0,
|
||||
|
||||
@@ -526,6 +526,23 @@
|
||||
<string>Network</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="uiPortSegmentSizeLabel">
|
||||
<property name="text">
|
||||
<string>Segment size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QSpinBox" name="uiPortSegmentSizeSpinBox">
|
||||
<property name="maximum">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="uiAdaptersLabel">
|
||||
<property name="text">
|
||||
@@ -560,13 +577,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="uiPortSegmentSizeLabel">
|
||||
<property name="text">
|
||||
<string>Segment size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="uiMacAddrLabel">
|
||||
<property name="text">
|
||||
@@ -598,14 +608,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<item row="8" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiLegacyNetworkingCheckBox">
|
||||
<property name="text">
|
||||
<string>Use the legacy networking mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2">
|
||||
<item row="9" column="2">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -628,16 +638,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QSpinBox" name="uiPortSegmentSizeSpinBox">
|
||||
<property name="maximum">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QSpinBox" name="uiAdaptersSpinBox">
|
||||
<property name="sizePolicy">
|
||||
@@ -654,6 +654,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiReplicateNetworkConnectionStateCheckBox">
|
||||
<property name="text">
|
||||
<string>Replicate network connection states in Qemu</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="uiAdvancedSettingsTab">
|
||||
@@ -860,6 +867,8 @@
|
||||
<li>%vm-id% =VM ID</li>
|
||||
<li>%project-id% = project ID</li>
|
||||
<li>%project-path% = project path</li>
|
||||
<li>%console-port% = console port number</li>
|
||||
<li>%guest-cid% = unique ID from 3 to 65535</li>
|
||||
</ul>
|
||||
</body></html></string>
|
||||
</property>
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
# 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.11.3
|
||||
# Created by: PyQt5 UI code generator 5.13.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_QemuVMConfigPageWidget(object):
|
||||
def setupUi(self, QemuVMConfigPageWidget):
|
||||
QemuVMConfigPageWidget.setObjectName("QemuVMConfigPageWidget")
|
||||
@@ -268,6 +270,14 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
self.uiNetworkTab.setObjectName("uiNetworkTab")
|
||||
self.gridLayout_5 = QtWidgets.QGridLayout(self.uiNetworkTab)
|
||||
self.gridLayout_5.setObjectName("gridLayout_5")
|
||||
self.uiPortSegmentSizeLabel = QtWidgets.QLabel(self.uiNetworkTab)
|
||||
self.uiPortSegmentSizeLabel.setObjectName("uiPortSegmentSizeLabel")
|
||||
self.gridLayout_5.addWidget(self.uiPortSegmentSizeLabel, 3, 0, 1, 1)
|
||||
self.uiPortSegmentSizeSpinBox = QtWidgets.QSpinBox(self.uiNetworkTab)
|
||||
self.uiPortSegmentSizeSpinBox.setMaximum(128)
|
||||
self.uiPortSegmentSizeSpinBox.setSingleStep(4)
|
||||
self.uiPortSegmentSizeSpinBox.setObjectName("uiPortSegmentSizeSpinBox")
|
||||
self.gridLayout_5.addWidget(self.uiPortSegmentSizeSpinBox, 3, 1, 1, 2)
|
||||
self.uiAdaptersLabel = QtWidgets.QLabel(self.uiNetworkTab)
|
||||
self.uiAdaptersLabel.setObjectName("uiAdaptersLabel")
|
||||
self.gridLayout_5.addWidget(self.uiAdaptersLabel, 0, 0, 1, 1)
|
||||
@@ -284,9 +294,6 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
self.uiPortNameFormatLineEdit.setText("")
|
||||
self.uiPortNameFormatLineEdit.setObjectName("uiPortNameFormatLineEdit")
|
||||
self.gridLayout_5.addWidget(self.uiPortNameFormatLineEdit, 2, 1, 1, 2)
|
||||
self.uiPortSegmentSizeLabel = QtWidgets.QLabel(self.uiNetworkTab)
|
||||
self.uiPortSegmentSizeLabel.setObjectName("uiPortSegmentSizeLabel")
|
||||
self.gridLayout_5.addWidget(self.uiPortSegmentSizeLabel, 3, 0, 1, 1)
|
||||
self.uiMacAddrLabel = QtWidgets.QLabel(self.uiNetworkTab)
|
||||
self.uiMacAddrLabel.setObjectName("uiMacAddrLabel")
|
||||
self.gridLayout_5.addWidget(self.uiMacAddrLabel, 4, 0, 1, 1)
|
||||
@@ -304,9 +311,9 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
self.gridLayout_5.addWidget(self.uiCustomAdaptersConfigurationPushButton, 6, 1, 1, 2)
|
||||
self.uiLegacyNetworkingCheckBox = QtWidgets.QCheckBox(self.uiNetworkTab)
|
||||
self.uiLegacyNetworkingCheckBox.setObjectName("uiLegacyNetworkingCheckBox")
|
||||
self.gridLayout_5.addWidget(self.uiLegacyNetworkingCheckBox, 7, 0, 1, 3)
|
||||
self.gridLayout_5.addWidget(self.uiLegacyNetworkingCheckBox, 8, 0, 1, 3)
|
||||
spacerItem3 = QtWidgets.QSpacerItem(20, 261, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_5.addItem(spacerItem3, 8, 2, 1, 1)
|
||||
self.gridLayout_5.addItem(spacerItem3, 9, 2, 1, 1)
|
||||
self.uiAdapterTypesComboBox = QtWidgets.QComboBox(self.uiNetworkTab)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -315,11 +322,6 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
self.uiAdapterTypesComboBox.setSizePolicy(sizePolicy)
|
||||
self.uiAdapterTypesComboBox.setObjectName("uiAdapterTypesComboBox")
|
||||
self.gridLayout_5.addWidget(self.uiAdapterTypesComboBox, 5, 1, 1, 2)
|
||||
self.uiPortSegmentSizeSpinBox = QtWidgets.QSpinBox(self.uiNetworkTab)
|
||||
self.uiPortSegmentSizeSpinBox.setMaximum(128)
|
||||
self.uiPortSegmentSizeSpinBox.setSingleStep(4)
|
||||
self.uiPortSegmentSizeSpinBox.setObjectName("uiPortSegmentSizeSpinBox")
|
||||
self.gridLayout_5.addWidget(self.uiPortSegmentSizeSpinBox, 3, 1, 1, 2)
|
||||
self.uiAdaptersSpinBox = QtWidgets.QSpinBox(self.uiNetworkTab)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -330,6 +332,9 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
self.uiAdaptersSpinBox.setMaximum(275)
|
||||
self.uiAdaptersSpinBox.setObjectName("uiAdaptersSpinBox")
|
||||
self.gridLayout_5.addWidget(self.uiAdaptersSpinBox, 0, 1, 1, 2)
|
||||
self.uiReplicateNetworkConnectionStateCheckBox = QtWidgets.QCheckBox(self.uiNetworkTab)
|
||||
self.uiReplicateNetworkConnectionStateCheckBox.setObjectName("uiReplicateNetworkConnectionStateCheckBox")
|
||||
self.gridLayout_5.addWidget(self.uiReplicateNetworkConnectionStateCheckBox, 7, 0, 1, 3)
|
||||
self.uiQemutabWidget.addTab(self.uiNetworkTab, "")
|
||||
self.uiAdvancedSettingsTab = QtWidgets.QWidget()
|
||||
self.uiAdvancedSettingsTab.setObjectName("uiAdvancedSettingsTab")
|
||||
@@ -502,16 +507,17 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
self.uiCdromImageLabel.setText(_translate("QemuVMConfigPageWidget", "Image:"))
|
||||
self.uiCdromImageToolButton.setText(_translate("QemuVMConfigPageWidget", "&Browse..."))
|
||||
self.uiQemutabWidget.setTabText(self.uiQemutabWidget.indexOf(self.uiCdromTab), _translate("QemuVMConfigPageWidget", "CD/DVD"))
|
||||
self.uiPortSegmentSizeLabel.setText(_translate("QemuVMConfigPageWidget", "Segment size:"))
|
||||
self.uiAdaptersLabel.setText(_translate("QemuVMConfigPageWidget", "Adapters:"))
|
||||
self.uiFirstPortNameLabel.setText(_translate("QemuVMConfigPageWidget", "First port name:"))
|
||||
self.uiPortNameFormatLabel.setToolTip(_translate("QemuVMConfigPageWidget", "<html><head/><body><p>{0} - the port number, from 0 to the number of adapters-1.</p><p>{1} - the segment number, from 0 to the number of segments-1.</p><p>{port0} - named alias for {0}.</p><p>{port1} - the port number, from 1 to the number of adapters.</p><p>{segment0} - named alias for {1}.</p><p>{segment1} - the segment number, from 1 to the number of segments.</p></body></html>"))
|
||||
self.uiPortNameFormatLabel.setText(_translate("QemuVMConfigPageWidget", "Name format:"))
|
||||
self.uiPortSegmentSizeLabel.setText(_translate("QemuVMConfigPageWidget", "Segment size:"))
|
||||
self.uiMacAddrLabel.setText(_translate("QemuVMConfigPageWidget", "Base MAC:"))
|
||||
self.uiAdapterTypesLabel.setText(_translate("QemuVMConfigPageWidget", "Type:"))
|
||||
self.uiCustomAdaptersLabel.setText(_translate("QemuVMConfigPageWidget", "Custom adapters:"))
|
||||
self.uiCustomAdaptersConfigurationPushButton.setText(_translate("QemuVMConfigPageWidget", "&Configure custom adapters"))
|
||||
self.uiLegacyNetworkingCheckBox.setText(_translate("QemuVMConfigPageWidget", "Use the legacy networking mode"))
|
||||
self.uiReplicateNetworkConnectionStateCheckBox.setText(_translate("QemuVMConfigPageWidget", "Replicate network connection states in Qemu"))
|
||||
self.uiQemutabWidget.setTabText(self.uiQemutabWidget.indexOf(self.uiNetworkTab), _translate("QemuVMConfigPageWidget", "Network"))
|
||||
self.uiLinuxBootGroupBox.setTitle(_translate("QemuVMConfigPageWidget", "Linux boot specific settings"))
|
||||
self.uiKernelCommandLineLabel.setText(_translate("QemuVMConfigPageWidget", "Kernel command line:"))
|
||||
@@ -541,9 +547,10 @@ class Ui_QemuVMConfigPageWidget(object):
|
||||
"<li>%vm-id% =VM ID</li>\n"
|
||||
"<li>%project-id% = project ID</li>\n"
|
||||
"<li>%project-path% = project path</li>\n"
|
||||
"<li>%console-port% = console port number</li>\n"
|
||||
"<li>%guest-cid% = unique ID from 3 to 65535</li>\n"
|
||||
"</ul>\n"
|
||||
"</body></html>"))
|
||||
self.uiBaseVMCheckBox.setText(_translate("QemuVMConfigPageWidget", "Use as a linked base VM"))
|
||||
self.uiQemutabWidget.setTabText(self.uiQemutabWidget.indexOf(self.uiAdvancedSettingsTab), _translate("QemuVMConfigPageWidget", "Advanced"))
|
||||
self.uiQemutabWidget.setTabText(self.uiQemutabWidget.indexOf(self.uiUsageTab), _translate("QemuVMConfigPageWidget", "Usage"))
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ class VirtualBoxVMConfigurationPage(QtWidgets.QWidget, Ui_virtualBoxVMConfigPage
|
||||
|
||||
try:
|
||||
ports = StandardPortNameFactory(adapters, first_port_name, port_name_format, port_segment_size)
|
||||
except (ValueError, KeyError):
|
||||
except (IndexError, ValueError, KeyError):
|
||||
QtWidgets.QMessageBox.critical(self, "Invalid format", "Invalid port name format")
|
||||
return
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ class VMwareVMConfigurationPage(QtWidgets.QWidget, Ui_VMwareVMConfigPageWidget):
|
||||
|
||||
try:
|
||||
ports = StandardPortNameFactory(adapters, first_port_name, port_name_format, port_segment_size)
|
||||
except (ValueError, KeyError):
|
||||
except (IndexError, ValueError, KeyError):
|
||||
QtWidgets.QMessageBox.critical(self, "Invalid format", "Invalid port name format")
|
||||
return
|
||||
|
||||
|
||||
18
gns3/node.py
@@ -628,12 +628,12 @@ class Node(BaseNode):
|
||||
from .main_window import MainWindow
|
||||
general_settings = MainWindow.instance().settings()
|
||||
|
||||
if console_type != "telnet":
|
||||
if not console_type:
|
||||
console_type = self.consoleType()
|
||||
if console_type == "vnc":
|
||||
return general_settings["vnc_console_command"]
|
||||
if console_type.startswith("spice"):
|
||||
return general_settings["spice_console_command"]
|
||||
if console_type == "vnc":
|
||||
return general_settings["vnc_console_command"]
|
||||
elif console_type.startswith("spice"):
|
||||
return general_settings["spice_console_command"]
|
||||
return general_settings["telnet_console_command"]
|
||||
|
||||
def consoleType(self):
|
||||
@@ -641,7 +641,7 @@ class Node(BaseNode):
|
||||
Get the console type (serial, telnet or VNC)
|
||||
"""
|
||||
|
||||
console_type = "telnet"
|
||||
console_type = "none"
|
||||
if "console_type" in self.settings():
|
||||
return self.settings()["console_type"]
|
||||
return console_type
|
||||
@@ -704,10 +704,10 @@ class Node(BaseNode):
|
||||
nodeTelnetConsole(self, console_port, command)
|
||||
elif console_type == "vnc":
|
||||
from .vnc_console import vncConsole
|
||||
vncConsole(self.consoleHost(), console_port, command)
|
||||
vncConsole(self, console_port, command)
|
||||
elif console_type.startswith("spice"):
|
||||
from .spice_console import spiceConsole
|
||||
spiceConsole(self.consoleHost(), console_port, command)
|
||||
spiceConsole(self, console_port, command)
|
||||
elif console_type == "http" or console_type == "https":
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl("{console_type}://{host}:{port}{path}".format(console_type=console_type, host=self.consoleHost(), port=console_port, path=self.consoleHttpPath())))
|
||||
|
||||
@@ -765,7 +765,7 @@ class Node(BaseNode):
|
||||
with open(context["path"], "wb+") as f:
|
||||
f.write(raw_body)
|
||||
except OSError as e:
|
||||
log.erro("Can't write %s: %s", context["path"], str(e))
|
||||
log.error("Cannot export file '{}': {}".format(context["path"], e))
|
||||
|
||||
def exportConfigsToDirectory(self, directory):
|
||||
"""
|
||||
|
||||
@@ -142,19 +142,14 @@ class NodesView(QtWidgets.QTreeWidget):
|
||||
# when we're in docked mode, outside main window
|
||||
return self.window().parent()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
def contextMenuEvent(self, event):
|
||||
"""
|
||||
Handles all mouse press events.
|
||||
Handles all context menu events.
|
||||
|
||||
:param: QMouseEvent instance
|
||||
:param event: QContextMenuEvent instance
|
||||
"""
|
||||
|
||||
# Check that an item has been selected and right click
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
self._showContextualMenu()
|
||||
event.accept()
|
||||
return
|
||||
super().mousePressEvent(event)
|
||||
self._showContextualMenu(event.globalPos())
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
@@ -181,7 +176,7 @@ class NodesView(QtWidgets.QTreeWidget):
|
||||
drag.exec_(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
|
||||
def _showContextualMenu(self):
|
||||
def _showContextualMenu(self, pos):
|
||||
|
||||
menu = QtWidgets.QMenu()
|
||||
refresh_action = QtWidgets.QAction("Refresh templates", menu)
|
||||
@@ -207,7 +202,7 @@ class NodesView(QtWidgets.QTreeWidget):
|
||||
delete_action.triggered.connect(qpartial(self._deleteSlot, template))
|
||||
menu.addAction(delete_action)
|
||||
|
||||
menu.exec_(QtGui.QCursor.pos())
|
||||
menu.exec_(pos)
|
||||
|
||||
def _configurationSlot(self, template, configuration_page, source):
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class PacketCapture:
|
||||
def __init__(self):
|
||||
self._tail_process = {}
|
||||
self._capture_reader_process = {}
|
||||
# Auto start the capture program for th link
|
||||
# Auto start the capture program for this link
|
||||
self._autostart = {}
|
||||
|
||||
Topology.instance().project_changed_signal.connect(self.killAllCapture)
|
||||
@@ -47,6 +47,7 @@ class PacketCapture:
|
||||
"""
|
||||
Kill all running captures (for example when change project)
|
||||
"""
|
||||
|
||||
for process in list(self._tail_process.values()):
|
||||
try:
|
||||
process.kill()
|
||||
|
||||
@@ -421,8 +421,6 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
|
||||
|
||||
new_graphics_view_settings = {"scene_width": self.uiSceneWidthSpinBox.value(),
|
||||
"scene_height": self.uiSceneHeightSpinBox.value(),
|
||||
"grid_size": self.uiNodeGridSizeSpinBox.value(),
|
||||
"drawing_grid_size": self.uiDrawingGridSizeSpinBox.value(),
|
||||
"draw_rectangle_selected_item": self.uiRectangleSelectedItemCheckBox.isChecked(),
|
||||
"draw_link_status_points": self.uiDrawLinkStatusPointsCheckBox.isChecked(),
|
||||
"show_interface_labels_on_new_project": self.uiShowInterfaceLabelsOnNewProject.isChecked(),
|
||||
@@ -433,4 +431,12 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
|
||||
"default_label_color": self._default_label_color.name(),
|
||||
"default_note_font": self.uiDefaultNoteStylePlainTextEdit.font().toString(),
|
||||
"default_note_color": self._default_note_color.name()}
|
||||
|
||||
node_grid_size = self.uiNodeGridSizeSpinBox.value()
|
||||
drawing_grid_size = self.uiDrawingGridSizeSpinBox.value()
|
||||
if node_grid_size % drawing_grid_size != 0:
|
||||
QtWidgets.QMessageBox.critical(self, "Grid sizes", "Invalid grid sizes which will create overlapping lines")
|
||||
else:
|
||||
new_graphics_view_settings["grid_size"] = node_grid_size
|
||||
new_graphics_view_settings["drawing_grid_size"] = drawing_grid_size
|
||||
MainWindow.instance().uiGraphicsView.setSettings(new_graphics_view_settings)
|
||||
|
||||
@@ -66,13 +66,23 @@ class GNS3VMPreferencesPage(QtWidgets.QWidget, Ui_GNS3VMPreferencesPageWidget):
|
||||
self.uiRamLabel.setVisible(engine["support_ram"])
|
||||
self.uiRamSpinBox.setVisible(engine["support_ram"])
|
||||
self.uiCpuSpinBox.setVisible(engine["support_ram"])
|
||||
if engine_id == "remote":
|
||||
self.uiPortLabel.setVisible(False)
|
||||
self.uiPortSpinBox.setVisible(False)
|
||||
else:
|
||||
self.uiPortLabel.setVisible(True)
|
||||
self.uiPortSpinBox.setVisible(True)
|
||||
self._refreshVMSlot(ignore_error=True)
|
||||
|
||||
def loadPreferences(self):
|
||||
"""
|
||||
Loads the preference from controller.
|
||||
"""
|
||||
Controller.instance().get("/gns3vm", self._getSettingsCallback)
|
||||
|
||||
if Controller.instance().connected():
|
||||
Controller.instance().get("/gns3vm", self._getSettingsCallback)
|
||||
else:
|
||||
log.error("Cannot load the GNS3 VM settings in the preferences dialog: not connected to the controller")
|
||||
|
||||
@qslot
|
||||
def _getSettingsCallback(self, result, error=False, **kwargs):
|
||||
@@ -81,12 +91,13 @@ class GNS3VMPreferencesPage(QtWidgets.QWidget, Ui_GNS3VMPreferencesPageWidget):
|
||||
return
|
||||
if error:
|
||||
if "message" in result:
|
||||
log.error("Error while getting settings : {}".format(result["message"]))
|
||||
log.error("Error while getting the GNS3 VM settings : {}".format(result["message"]))
|
||||
return
|
||||
self._old_settings = copy.copy(result)
|
||||
self._settings = result
|
||||
self.uiRamSpinBox.setValue(self._settings["ram"])
|
||||
self.uiCpuSpinBox.setValue(self._settings["vcpus"])
|
||||
self.uiPortSpinBox.setValue(self._settings.get("port", 3080))
|
||||
self.uiEnableVMCheckBox.setChecked(self._settings["enable"])
|
||||
if self._settings["when_exit"] == "keep":
|
||||
self.uiWhenExitKeepRadioButton.setChecked(True)
|
||||
@@ -164,7 +175,8 @@ class GNS3VMPreferencesPage(QtWidgets.QWidget, Ui_GNS3VMPreferencesPageWidget):
|
||||
"when_exit": when_exit,
|
||||
"engine": self.uiGNS3VMEngineComboBox.currentData(),
|
||||
"ram": self.uiRamSpinBox.value(),
|
||||
"vcpus": self.uiCpuSpinBox.value()
|
||||
"vcpus": self.uiCpuSpinBox.value(),
|
||||
"port": self.uiPortSpinBox.value()
|
||||
}
|
||||
if self._old_settings != settings:
|
||||
Controller.instance().put("/gns3vm", self._saveSettingsCallback, settings, timeout=60 * 5)
|
||||
|
||||
@@ -67,6 +67,7 @@ class ServerPreferencesPage(QtWidgets.QWidget, Ui_ServerPreferencesPageWidget):
|
||||
# ignore link-local addresses
|
||||
continue
|
||||
self.uiLocalServerHostComboBox.addItem(address_string, address_string)
|
||||
self.uiLocalServerHostComboBox.addItem("localhost", "localhost") # local host
|
||||
self.uiLocalServerHostComboBox.addItem("::", "::") # all IPv6 addresses
|
||||
self.uiLocalServerHostComboBox.addItem("0.0.0.0", "0.0.0.0") # all IPv4 addresses
|
||||
|
||||
|
||||
@@ -520,10 +520,12 @@ class Project(QtCore.QObject):
|
||||
def load(self, path=None):
|
||||
if not path:
|
||||
path = self.path()
|
||||
if path:
|
||||
if not Controller.instance().isRemote() and path:
|
||||
# load a local project from file
|
||||
body = {"path": path}
|
||||
Controller.instance().post("/projects/load", self._projectOpenCallback, body=body, timeout=None)
|
||||
else:
|
||||
# open a local/remote project
|
||||
self.post("/open", self._projectOpenCallback, timeout=None)
|
||||
|
||||
def _projectOpenCallback(self, result, error=False, **kwargs):
|
||||
|
||||
@@ -115,10 +115,13 @@ class LogQMessageBox(QtWidgets.QMessageBox):
|
||||
show a stack trace when a critical message box is shown in debug mode
|
||||
"""
|
||||
@staticmethod
|
||||
def critical(parent, title, message, *args):
|
||||
def critical(parent, title, message, *args, show_stack_info=False):
|
||||
if message.startswith("QXcbConnection"): # Qt noise not relevant
|
||||
return
|
||||
LogQMessageBox._get_logger().critical(re.sub(r"<[^<]+?>", "", message), stack_info=LogQMessageBox.stack_info())
|
||||
stack_info = None
|
||||
if show_stack_info:
|
||||
stack_info = LogQMessageBox.stack_info()
|
||||
LogQMessageBox._get_logger().critical(re.sub(r"<[^<]+?>", "", message), stack_info=stack_info)
|
||||
if parent is False:
|
||||
# special case to display a QMessageBox before the main window is created.
|
||||
parent = None
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import json
|
||||
import copy
|
||||
import os
|
||||
import collections
|
||||
import collections.abc
|
||||
import jsonschema
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class ApplianceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Appliance(collections.Mapping):
|
||||
class Appliance(collections.abc.Mapping):
|
||||
|
||||
def __init__(self, registry, path):
|
||||
"""
|
||||
@@ -119,7 +119,7 @@ class Appliance(collections.Mapping):
|
||||
"""
|
||||
Duplicate a version in order to create a new version
|
||||
"""
|
||||
if 'versions' not in self._appliance.keys() or len(self._appliance["versions"]) == 0:
|
||||
if 'versions' not in self._appliance.keys() or not self._appliance["versions"]:
|
||||
raise ApplianceError("Your appliance file doesn't contain any versions")
|
||||
|
||||
ref = self._appliance["versions"][0]
|
||||
|
||||
@@ -266,6 +266,9 @@
|
||||
"adapter_type": {
|
||||
"enum": [
|
||||
"e1000",
|
||||
"e1000-82544gc",
|
||||
"e1000-82545em",
|
||||
"e1000e",
|
||||
"i82550",
|
||||
"i82551",
|
||||
"i82557a",
|
||||
@@ -281,6 +284,7 @@
|
||||
"i82801",
|
||||
"ne2k_pci",
|
||||
"pcnet",
|
||||
"rocker",
|
||||
"rtl8139",
|
||||
"virtio",
|
||||
"virtio-net-pci",
|
||||
@@ -301,19 +305,19 @@
|
||||
"title": "Number of Virtual CPU"
|
||||
},
|
||||
"hda_disk_interface": {
|
||||
"enum": ["ide", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "sata"],
|
||||
"enum": ["ide", "sata", "nvme","scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"],
|
||||
"title": "Disk interface for the installed hda_disk_image"
|
||||
},
|
||||
"hdb_disk_interface": {
|
||||
"enum": ["ide", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "sata"],
|
||||
"enum": ["ide", "sata", "nvme", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"],
|
||||
"title": "Disk interface for the installed hdb_disk_image"
|
||||
},
|
||||
"hdc_disk_interface": {
|
||||
"enum": ["ide", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "sata"],
|
||||
"enum": ["ide", "sata", "nvme", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"],
|
||||
"title": "Disk interface for the installed hdc_disk_image"
|
||||
},
|
||||
"hdd_disk_interface": {
|
||||
"enum": ["ide", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "sata"],
|
||||
"enum": ["ide", "sata", "nvme", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"],
|
||||
"title": "Disk interface for the installed hdd_disk_image"
|
||||
},
|
||||
"arch": {
|
||||
|
||||
@@ -40,7 +40,7 @@ DEFAULT_CONFIGS_PATH = os.path.normpath(os.path.expanduser("~/GNS3/configs"))
|
||||
# Default appliances location
|
||||
DEFAULT_APPLIANCES_PATH = os.path.normpath(os.path.expanduser("~/GNS3/appliances"))
|
||||
|
||||
DEFAULT_LOCAL_SERVER_HOST = "127.0.0.1"
|
||||
DEFAULT_LOCAL_SERVER_HOST = "localhost"
|
||||
DEFAULT_LOCAL_SERVER_PORT = 3080
|
||||
DEFAULT_DELAY_CONSOLE_ALL = 500
|
||||
|
||||
@@ -58,7 +58,8 @@ if sys.platform.startswith("win"):
|
||||
PRECONFIGURED_TELNET_CONSOLE_COMMANDS = {'Putty (normal standalone version)': 'putty_standalone.exe -telnet %h %p -loghost "%d"',
|
||||
'Putty (custom deprecated version)': 'putty.exe -telnet %h %p -wt "%d" -gns3 5 -skin 4',
|
||||
'MobaXterm': r'"{}\Mobatek\MobaXterm Personal Edition\MobaXterm.exe" -newtab "telnet %h %p"'.format(program_files_x86),
|
||||
'Royal TS': r'{}\code4ward.net\Royal TS V3\RTS3App.exe /connectadhoc:%h /adhoctype:terminal /p:IsTelnetConnection="true" /p:ConnectionType="telnet;Telnet Connection" /p:Port="%p" /p:Name="%d"'.format(program_files),
|
||||
'Royal TS V3': r'{}\code4ward.net\Royal TS V3\RTS3App.exe /connectadhoc:%h /adhoctype:terminal /p:IsTelnetConnection="true" /p:ConnectionType="telnet;Telnet Connection" /p:Port="%p" /p:Name="%d"'.format(program_files),
|
||||
'Royal TS V5': r'"{}\Royal TS V5\RoyalTS.exe" /protocol:terminal /using:adhoc /uri:"%h" /property:Port="%p" /property:IsTelnetConnection="true" /property:Name="%d"'.format(program_files_x86),
|
||||
'SuperPutty': r'SuperPutty.exe -telnet "%h -P %p -wt \"%d\""',
|
||||
'SecureCRT': r'"{}\VanDyke Software\SecureCRT\SecureCRT.exe" /N "%d" /T /TELNET %h %p'.format(program_files),
|
||||
'SecureCRT (personal profile)': r'"{}\AppData\Local\VanDyke Software\SecureCRT\SecureCRT.exe" /T /N "%d" /TELNET %h %p'.format(userprofile),
|
||||
@@ -197,7 +198,8 @@ else:
|
||||
PRECONFIGURED_VNC_CONSOLE_COMMANDS = {
|
||||
'TightVNC': 'vncviewer %h:%p',
|
||||
'Vinagre': 'vinagre %h::%p',
|
||||
'gvncviewer': 'gvncviewer %h:%P'
|
||||
'gvncviewer': 'gvncviewer %h:%P',
|
||||
'Remote Viewer': 'remote-viewer vnc://%h:%p'
|
||||
}
|
||||
|
||||
# default VNC console command on other systems
|
||||
@@ -293,7 +295,7 @@ GENERAL_SETTINGS = {
|
||||
"recent_projects": [],
|
||||
"geometry": "",
|
||||
"state": "",
|
||||
"preferences_dialog_geometry": "",
|
||||
#"preferences_dialog_geometry": "",
|
||||
"debug_level": 0,
|
||||
"multi_profiles": False,
|
||||
"hdpi": not sys.platform.startswith("linux"),
|
||||
@@ -330,7 +332,7 @@ GRAPHICS_VIEW_SETTINGS = {
|
||||
LOCAL_SERVER_SETTINGS = {
|
||||
"path": "gns3server",
|
||||
"ubridge_path": "ubridge",
|
||||
"host": None,
|
||||
"host": "localhost",
|
||||
"port": DEFAULT_LOCAL_SERVER_PORT,
|
||||
"images_path": DEFAULT_IMAGES_PATH,
|
||||
"projects_path": DEFAULT_PROJECTS_PATH,
|
||||
|
||||
@@ -24,23 +24,28 @@ import os
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
from .controller import Controller
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def spiceConsole(host, port, command):
|
||||
def spiceConsole(node, port, command):
|
||||
"""
|
||||
Start a SPICE console program.
|
||||
|
||||
:param host: host or IP address
|
||||
:param node: Node instance
|
||||
:param port: port number
|
||||
:param command: command to be executed
|
||||
"""
|
||||
|
||||
if len(command.strip(' ')) == 0:
|
||||
log.warning('SPICE client is not configured')
|
||||
log.error("SPICE client is not configured")
|
||||
return
|
||||
|
||||
name = node.name()
|
||||
host = node.consoleHost()
|
||||
|
||||
# ipv6 support
|
||||
if ":" in host:
|
||||
host = "[{}]".format(host)
|
||||
@@ -48,9 +53,12 @@ def spiceConsole(host, port, command):
|
||||
# replace the place-holders by the actual values
|
||||
command = command.replace("%h", host)
|
||||
command = command.replace("%p", str(port))
|
||||
command = command.replace("%d", name.replace('"', '\\"'))
|
||||
command = command.replace("%i", node.project().id())
|
||||
command = command.replace("%n", str(node.id()))
|
||||
|
||||
try:
|
||||
log.info('starting SPICE program "{}"'.format(command))
|
||||
log.debug('starting SPICE program "{}"'.format(command))
|
||||
if sys.platform.startswith("win"):
|
||||
# use the string on Windows
|
||||
subprocess.Popen(command)
|
||||
@@ -59,5 +67,4 @@ def spiceConsole(host, port, command):
|
||||
args = shlex.split(command)
|
||||
subprocess.Popen(args, env=os.environ)
|
||||
except (OSError, ValueError, subprocess.SubprocessError) as e:
|
||||
log.warning('could not start SPICE program "{}": {}'.format(command, e))
|
||||
raise
|
||||
log.error("Could not start SPICE program with command '{}': {}".format(command, e))
|
||||
|
||||
@@ -30,6 +30,10 @@ class Template:
|
||||
settings["template_id"] = str(uuid.uuid4())
|
||||
self._settings = copy.deepcopy(settings)
|
||||
|
||||
# The "appliance_id" setting has been replaced by "template_id" setting in version 2.2
|
||||
if "appliance_id" in self._settings:
|
||||
self._settings["template_id"] = self._settings.pop("appliance_id")
|
||||
|
||||
# The "node_type" setting has been replaced by "template_type" setting in version 2.2
|
||||
if "node_type" in self._settings:
|
||||
self._settings["template_type"] = self._settings.pop("node_type")
|
||||
@@ -38,6 +42,11 @@ class Template:
|
||||
if "server" in self._settings:
|
||||
self._settings["compute_id"] = self._settings.pop("server")
|
||||
|
||||
for setting in self._settings.copy():
|
||||
# remove deprecated settings
|
||||
if setting in ["enable_remote_console", "use_ubridge", "acpi_shutdown", "default_symbol", "hover_symbol"]:
|
||||
del self._settings[setting]
|
||||
|
||||
def id(self):
|
||||
"""
|
||||
Returns the template ID.
|
||||
|
||||
@@ -29,6 +29,7 @@ from .qt import QtCore, QtWidgets
|
||||
from .utils.progress_dialog import ProgressDialog
|
||||
from .utils.import_project_worker import ImportProjectWorker
|
||||
from .dialogs.project_export_wizard import ExportProjectWizard
|
||||
from .dialogs.file_editor_dialog import FileEditorDialog
|
||||
from .dialogs.project_welcome_dialog import ProjectWelcomeDialog
|
||||
|
||||
from .modules import MODULES
|
||||
@@ -144,7 +145,7 @@ class Topology(QtCore.QObject):
|
||||
self._main_window.uiGraphicsView.setDrawingGridSize(self._project.drawingGridSize())
|
||||
self._main_window.uiShowGridAction.setChecked(self._project.showGrid())
|
||||
self._main_window.showGrid(self._project.showGrid())
|
||||
if os.path.exists(project_file):
|
||||
if not Controller.instance().isRemote() and os.path.exists(project_file):
|
||||
self._main_window.updateRecentFileSettings(project_file)
|
||||
self._main_window.updateRecentFileActions()
|
||||
|
||||
@@ -243,6 +244,13 @@ class Topology(QtCore.QObject):
|
||||
self._main_window.uiStatusBar.showMessage("Project loaded {}".format(path), 2000)
|
||||
return True
|
||||
|
||||
def editReadme(self):
|
||||
if self.project() is None:
|
||||
return
|
||||
dialog = FileEditorDialog(self.project(), "README.txt", parent=self._main_window, default="Project title\n\nAuthor: Grace Hopper <grace@example.org>\n\nThis project is about...")
|
||||
dialog.show()
|
||||
dialog.exec_()
|
||||
|
||||
def _projectCreationErrorSlot(self, message):
|
||||
if self._project:
|
||||
self._project.project_creation_error_signal.disconnect(self._projectCreationErrorSlot)
|
||||
|
||||
@@ -267,19 +267,16 @@ class TopologySummaryView(QtWidgets.QTreeWidget):
|
||||
if item.link() == link:
|
||||
view.centerOn(item)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
def contextMenuEvent(self, event):
|
||||
"""
|
||||
Handles all mouse press events.
|
||||
Handles all context menu events.
|
||||
|
||||
:param event: QMouseEvent instance
|
||||
:param event: QContextMenuEvent instance
|
||||
"""
|
||||
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
self._showContextualMenu()
|
||||
else:
|
||||
super().mousePressEvent(event)
|
||||
self._showContextualMenu(event.globalPos())
|
||||
|
||||
def _showContextualMenu(self):
|
||||
def _showContextualMenu(self, pos):
|
||||
"""
|
||||
Contextual menu to expand and collapse the tree.
|
||||
"""
|
||||
@@ -322,6 +319,11 @@ class TopologySummaryView(QtWidgets.QTreeWidget):
|
||||
reset_all_filters.triggered.connect(self._resetAllFiltersSlot)
|
||||
menu.addAction(reset_all_filters)
|
||||
|
||||
resume_suspended_links = QtWidgets.QAction("Resume all suspended links", menu)
|
||||
resume_suspended_links.setIcon(get_icon("start.svg"))
|
||||
resume_suspended_links.triggered.connect(self._resumeAllLinksSlot)
|
||||
menu.addAction(resume_suspended_links)
|
||||
|
||||
current_item = self.currentItem()
|
||||
from .main_window import MainWindow
|
||||
view = MainWindow.instance().uiGraphicsView
|
||||
@@ -336,7 +338,7 @@ class TopologySummaryView(QtWidgets.QTreeWidget):
|
||||
item.populateLinkContextualMenu(menu)
|
||||
break
|
||||
|
||||
menu.exec_(QtGui.QCursor.pos())
|
||||
menu.exec_(pos)
|
||||
|
||||
@qslot
|
||||
def _expandAllSlot(self, *args):
|
||||
@@ -403,3 +405,13 @@ class TopologySummaryView(QtWidgets.QTreeWidget):
|
||||
filters = {}
|
||||
link.setFilters(filters)
|
||||
link.update()
|
||||
|
||||
@qslot
|
||||
def _resumeAllLinksSlot(self, *args):
|
||||
"""
|
||||
Resume all suspended links.
|
||||
"""
|
||||
|
||||
for link in self._topology.links():
|
||||
if link.suspended():
|
||||
link.toggleSuspend()
|
||||
|
||||
@@ -179,6 +179,17 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>uiProjectNameLineEdit</tabstop>
|
||||
<tabstop>uiSceneWidthSpinBox</tabstop>
|
||||
<tabstop>uiSceneHeightSpinBox</tabstop>
|
||||
<tabstop>uiNodeGridSizeSpinBox</tabstop>
|
||||
<tabstop>uiDrawingGridSizeSpinBox</tabstop>
|
||||
<tabstop>uiProjectAutoOpenCheckBox</tabstop>
|
||||
<tabstop>uiProjectAutoStartCheckBox</tabstop>
|
||||
<tabstop>uiProjectAutoCloseCheckBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
@@ -64,21 +64,21 @@
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="uiCompressionComboBox"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiIncludeImagesCheckBox">
|
||||
<property name="text">
|
||||
<string>Include base images</string>
|
||||
<string>&Include base images</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="uiIncludeSnapshotsCheckBox">
|
||||
<property name="text">
|
||||
<string>Include snapshots</string>
|
||||
<string>&Include snapshots</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<item row="6" column="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -91,6 +91,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="uiResetMacAddressesCheckBox">
|
||||
<property name="text">
|
||||
<string>&Reset MAC addresses</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWizardPage" name="uiProjectReadmeWizardPage">
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
# 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.9
|
||||
# Created by: PyQt5 UI code generator 5.13.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_ExportProjectWizard(object):
|
||||
def setupUi(self, ExportProjectWizard):
|
||||
ExportProjectWizard.setObjectName("ExportProjectWizard")
|
||||
@@ -43,12 +45,15 @@ class Ui_ExportProjectWizard(object):
|
||||
self.gridLayout.addWidget(self.uiCompressionComboBox, 1, 1, 1, 2)
|
||||
self.uiIncludeImagesCheckBox = QtWidgets.QCheckBox(self.uiExportOptionsWizardPage)
|
||||
self.uiIncludeImagesCheckBox.setObjectName("uiIncludeImagesCheckBox")
|
||||
self.gridLayout.addWidget(self.uiIncludeImagesCheckBox, 2, 0, 1, 3)
|
||||
self.gridLayout.addWidget(self.uiIncludeImagesCheckBox, 3, 0, 1, 3)
|
||||
self.uiIncludeSnapshotsCheckBox = QtWidgets.QCheckBox(self.uiExportOptionsWizardPage)
|
||||
self.uiIncludeSnapshotsCheckBox.setObjectName("uiIncludeSnapshotsCheckBox")
|
||||
self.gridLayout.addWidget(self.uiIncludeSnapshotsCheckBox, 3, 0, 1, 2)
|
||||
self.gridLayout.addWidget(self.uiIncludeSnapshotsCheckBox, 4, 0, 1, 2)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 247, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout.addItem(spacerItem, 4, 2, 1, 1)
|
||||
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)
|
||||
ExportProjectWizard.addPage(self.uiExportOptionsWizardPage)
|
||||
self.uiProjectReadmeWizardPage = QtWidgets.QWizardPage()
|
||||
self.uiProjectReadmeWizardPage.setObjectName("uiProjectReadmeWizardPage")
|
||||
@@ -70,8 +75,9 @@ class Ui_ExportProjectWizard(object):
|
||||
self.uiPathLabel.setText(_translate("ExportProjectWizard", "Path:"))
|
||||
self.uiPathBrowserToolButton.setText(_translate("ExportProjectWizard", "Browse..."))
|
||||
self.uiCompressionLabel.setText(_translate("ExportProjectWizard", "Compression:"))
|
||||
self.uiIncludeImagesCheckBox.setText(_translate("ExportProjectWizard", "Include base images"))
|
||||
self.uiIncludeSnapshotsCheckBox.setText(_translate("ExportProjectWizard", "Include snapshots"))
|
||||
self.uiIncludeImagesCheckBox.setText(_translate("ExportProjectWizard", "&Include base images"))
|
||||
self.uiIncludeSnapshotsCheckBox.setText(_translate("ExportProjectWizard", "&Include snapshots"))
|
||||
self.uiResetMacAddressesCheckBox.setText(_translate("ExportProjectWizard", "&Reset MAC addresses"))
|
||||
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"
|
||||
@@ -79,4 +85,3 @@ class Ui_ExportProjectWizard(object):
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:\'Ubuntu\'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'.SF NS Text\'; font-size:13pt;\"><br /></p></body></html>"))
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>931</width>
|
||||
<height>878</height>
|
||||
<width>941</width>
|
||||
<height>910</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -275,6 +275,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="uiImageDirectoriesListWidget">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -397,17 +400,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Command line replacements:</p>
|
||||
<ul>
|
||||
<li>%h = console IP or hostname</li>
|
||||
<li>%p = console port</li>
|
||||
<li>%P = VNC display</li>
|
||||
<li>%s = path of the serial connection</li>
|
||||
<li>%d = title of the console</li>
|
||||
<li>%i = project UUID</li>
|
||||
<li>%c = server URL</li>
|
||||
</ul>
|
||||
</body></html></string>
|
||||
<string><html><head/><body><p>Command line replacements:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%h = console IP or hostname</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%p = console port</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%d = title of the console</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%i = project UUID</li><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%n = node UUID</li></ul><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%c = server URL</li></ul></body></html></string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
@@ -508,17 +501,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Command line replacements:</p>
|
||||
<ul>
|
||||
<li>%h = console IP or hostname</li>
|
||||
<li>%p = console port</li>
|
||||
<li>%P = VNC display</li>
|
||||
<li>%s = path of the serial connection</li>
|
||||
<li>%d = title of the console</li>
|
||||
<li>%i = project UUID</li>
|
||||
<li>%c = server URL</li>
|
||||
</ul>
|
||||
</body></html></string>
|
||||
<string><html><head/><body><p>Command line replacements:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%h = console IP or hostname</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%p = console port</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%P = VNC display</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%d = title of the console</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%i = project UUID</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%n = node UUID</li></ul></body></html></string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
@@ -587,17 +570,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Command line replacements:</p>
|
||||
<ul>
|
||||
<li>%h = console IP or hostname</li>
|
||||
<li>%p = console port</li>
|
||||
<li>%P = VNC display</li>
|
||||
<li>%s = path of the serial connection</li>
|
||||
<li>%d = title of the console</li>
|
||||
<li>%i = project UUID</li>
|
||||
<li>%c = server URL</li>
|
||||
</ul>
|
||||
</body></html></string>
|
||||
<string><html><head/><body><p>Command line replacements:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%h = console IP or hostname</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%p = console port</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%d = title of the console</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%i = project UUID</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%n = node UUID</li></ul><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
@@ -684,6 +657,9 @@
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -730,16 +706,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="uiDrawLinkStatusPointsCheckBox">
|
||||
<property name="text">
|
||||
<string>Draw link status points</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
@@ -785,6 +751,9 @@
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -801,6 +770,9 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> pixels</string>
|
||||
</property>
|
||||
@@ -826,6 +798,9 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> pixels</string>
|
||||
</property>
|
||||
@@ -879,6 +854,9 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
@@ -901,6 +879,9 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
@@ -945,6 +926,16 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="uiDrawLinkStatusPointsCheckBox">
|
||||
<property name="text">
|
||||
<string>Draw link status points</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="uiMiscTab">
|
||||
@@ -1066,6 +1057,56 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>uiProjectsPathLineEdit</tabstop>
|
||||
<tabstop>uiProjectsPathToolButton</tabstop>
|
||||
<tabstop>uiSymbolsPathLineEdit</tabstop>
|
||||
<tabstop>uiSymbolsPathToolButton</tabstop>
|
||||
<tabstop>uiConfigsPathLineEdit</tabstop>
|
||||
<tabstop>uiConfigsPathToolButton</tabstop>
|
||||
<tabstop>uiAppliancesPathLineEdit</tabstop>
|
||||
<tabstop>uiAppliancesPathToolButton</tabstop>
|
||||
<tabstop>uiStyleComboBox</tabstop>
|
||||
<tabstop>uiSymbolThemeComboBox</tabstop>
|
||||
<tabstop>uiImportConfigurationFilePushButton</tabstop>
|
||||
<tabstop>uiExportConfigurationFilePushButton</tabstop>
|
||||
<tabstop>uiBrowseConfigurationPushButton</tabstop>
|
||||
<tabstop>uiImagesPathLineEdit</tabstop>
|
||||
<tabstop>uiImagesPathToolButton</tabstop>
|
||||
<tabstop>uiImageDirectoriesAddPushButton</tabstop>
|
||||
<tabstop>uiImageDirectoriesDeletePushButton</tabstop>
|
||||
<tabstop>uiTelnetConsoleCommandLineEdit</tabstop>
|
||||
<tabstop>uiTelnetConsolePreconfiguredCommandPushButton</tabstop>
|
||||
<tabstop>uiDelayConsoleAllSpinBox</tabstop>
|
||||
<tabstop>uiVNCConsoleCommandLineEdit</tabstop>
|
||||
<tabstop>uiVNCConsolePreconfiguredCommandPushButton</tabstop>
|
||||
<tabstop>uiSPICEConsoleCommandLineEdit</tabstop>
|
||||
<tabstop>uiSPICEConsolePreconfiguredCommandPushButton</tabstop>
|
||||
<tabstop>uiSceneWidthSpinBox</tabstop>
|
||||
<tabstop>uiSceneHeightSpinBox</tabstop>
|
||||
<tabstop>uiNodeGridSizeSpinBox</tabstop>
|
||||
<tabstop>uiDrawingGridSizeSpinBox</tabstop>
|
||||
<tabstop>uiRectangleSelectedItemCheckBox</tabstop>
|
||||
<tabstop>uiDrawLinkStatusPointsCheckBox</tabstop>
|
||||
<tabstop>uiShowInterfaceLabelsOnNewProject</tabstop>
|
||||
<tabstop>uiShowGridOnNewProject</tabstop>
|
||||
<tabstop>uiSnapToGridOnNewProject</tabstop>
|
||||
<tabstop>uiLimitSizeNodeSymbolCheckBox</tabstop>
|
||||
<tabstop>uiDefaultLabelFontPushButton</tabstop>
|
||||
<tabstop>uiDefaultLabelColorPushButton</tabstop>
|
||||
<tabstop>uiDefaultNoteFontPushButton</tabstop>
|
||||
<tabstop>uiDefaultNoteColorPushButton</tabstop>
|
||||
<tabstop>uiCheckForUpdateCheckBox</tabstop>
|
||||
<tabstop>uiCrashReportCheckBox</tabstop>
|
||||
<tabstop>uiStatsCheckBox</tabstop>
|
||||
<tabstop>uiOverlayNotificationsCheckBox</tabstop>
|
||||
<tabstop>uiExperimentalFeaturesCheckBox</tabstop>
|
||||
<tabstop>uiHdpiCheckBox</tabstop>
|
||||
<tabstop>uiMultiProfilesCheckBox</tabstop>
|
||||
<tabstop>uiDirectFileUpload</tabstop>
|
||||
<tabstop>uiRestoreDefaultsPushButton</tabstop>
|
||||
<tabstop>uiMiscTabWidget</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/general_preferences_page.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
# Created by: PyQt5 UI code generator 5.13.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_GeneralPreferencesPageWidget(object):
|
||||
def setupUi(self, GeneralPreferencesPageWidget):
|
||||
GeneralPreferencesPageWidget.setObjectName("GeneralPreferencesPageWidget")
|
||||
GeneralPreferencesPageWidget.resize(931, 878)
|
||||
GeneralPreferencesPageWidget.resize(941, 910)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(GeneralPreferencesPageWidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.uiMiscTabWidget = QtWidgets.QTabWidget(GeneralPreferencesPageWidget)
|
||||
@@ -140,6 +142,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiImageDirectoriesLabel.setObjectName("uiImageDirectoriesLabel")
|
||||
self.verticalLayout_10.addWidget(self.uiImageDirectoriesLabel)
|
||||
self.uiImageDirectoriesListWidget = QtWidgets.QListWidget(self.uiLocalBinaryImagePathsGroupBox)
|
||||
self.uiImageDirectoriesListWidget.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
self.uiImageDirectoriesListWidget.setLineWidth(0)
|
||||
self.uiImageDirectoriesListWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.uiImageDirectoriesListWidget.setAlternatingRowColors(False)
|
||||
@@ -312,6 +315,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
sizePolicy.setHeightForWidth(self.uiDefaultNoteStylePlainTextEdit.sizePolicy().hasHeightForWidth())
|
||||
self.uiDefaultNoteStylePlainTextEdit.setSizePolicy(sizePolicy)
|
||||
self.uiDefaultNoteStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
|
||||
self.uiDefaultNoteStylePlainTextEdit.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.uiDefaultNoteStylePlainTextEdit.setReadOnly(True)
|
||||
self.uiDefaultNoteStylePlainTextEdit.setObjectName("uiDefaultNoteStylePlainTextEdit")
|
||||
self.gridLayout_3.addWidget(self.uiDefaultNoteStylePlainTextEdit, 14, 0, 1, 3)
|
||||
@@ -329,10 +333,6 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiLimitSizeNodeSymbolCheckBox = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiLimitSizeNodeSymbolCheckBox.setObjectName("uiLimitSizeNodeSymbolCheckBox")
|
||||
self.gridLayout_3.addWidget(self.uiLimitSizeNodeSymbolCheckBox, 9, 0, 1, 2)
|
||||
self.uiDrawLinkStatusPointsCheckBox = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiDrawLinkStatusPointsCheckBox.setChecked(True)
|
||||
self.uiDrawLinkStatusPointsCheckBox.setObjectName("uiDrawLinkStatusPointsCheckBox")
|
||||
self.gridLayout_3.addWidget(self.uiDrawLinkStatusPointsCheckBox, 5, 0, 1, 1)
|
||||
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
|
||||
self.uiDefaultLabelFontPushButton = QtWidgets.QPushButton(self.uiSceneTab)
|
||||
@@ -351,6 +351,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
sizePolicy.setHeightForWidth(self.uiDefaultLabelStylePlainTextEdit.sizePolicy().hasHeightForWidth())
|
||||
self.uiDefaultLabelStylePlainTextEdit.setSizePolicy(sizePolicy)
|
||||
self.uiDefaultLabelStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
|
||||
self.uiDefaultLabelStylePlainTextEdit.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.uiDefaultLabelStylePlainTextEdit.setReadOnly(True)
|
||||
self.uiDefaultLabelStylePlainTextEdit.setObjectName("uiDefaultLabelStylePlainTextEdit")
|
||||
self.gridLayout_3.addWidget(self.uiDefaultLabelStylePlainTextEdit, 11, 0, 1, 3)
|
||||
@@ -360,6 +361,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uiSceneHeightSpinBox.sizePolicy().hasHeightForWidth())
|
||||
self.uiSceneHeightSpinBox.setSizePolicy(sizePolicy)
|
||||
self.uiSceneHeightSpinBox.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
self.uiSceneHeightSpinBox.setMinimum(500)
|
||||
self.uiSceneHeightSpinBox.setMaximum(1000000)
|
||||
self.uiSceneHeightSpinBox.setSingleStep(100)
|
||||
@@ -372,6 +374,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uiSceneWidthSpinBox.sizePolicy().hasHeightForWidth())
|
||||
self.uiSceneWidthSpinBox.setSizePolicy(sizePolicy)
|
||||
self.uiSceneWidthSpinBox.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
self.uiSceneWidthSpinBox.setMinimum(500)
|
||||
self.uiSceneWidthSpinBox.setMaximum(1000000)
|
||||
self.uiSceneWidthSpinBox.setSingleStep(100)
|
||||
@@ -396,6 +399,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uiDrawingGridSizeSpinBox.sizePolicy().hasHeightForWidth())
|
||||
self.uiDrawingGridSizeSpinBox.setSizePolicy(sizePolicy)
|
||||
self.uiDrawingGridSizeSpinBox.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
self.uiDrawingGridSizeSpinBox.setMinimum(5)
|
||||
self.uiDrawingGridSizeSpinBox.setMaximum(100)
|
||||
self.uiDrawingGridSizeSpinBox.setSingleStep(5)
|
||||
@@ -408,6 +412,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uiNodeGridSizeSpinBox.sizePolicy().hasHeightForWidth())
|
||||
self.uiNodeGridSizeSpinBox.setSizePolicy(sizePolicy)
|
||||
self.uiNodeGridSizeSpinBox.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
self.uiNodeGridSizeSpinBox.setMinimum(5)
|
||||
self.uiNodeGridSizeSpinBox.setMaximum(150)
|
||||
self.uiNodeGridSizeSpinBox.setSingleStep(5)
|
||||
@@ -423,6 +428,10 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.gridLayout_3.addWidget(self.uiRectangleSelectedItemCheckBox, 4, 0, 1, 3)
|
||||
spacerItem9 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_3.addItem(spacerItem9, 16, 0, 1, 1)
|
||||
self.uiDrawLinkStatusPointsCheckBox = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiDrawLinkStatusPointsCheckBox.setChecked(True)
|
||||
self.uiDrawLinkStatusPointsCheckBox.setObjectName("uiDrawLinkStatusPointsCheckBox")
|
||||
self.gridLayout_3.addWidget(self.uiDrawLinkStatusPointsCheckBox, 5, 0, 1, 2)
|
||||
self.uiMiscTabWidget.addTab(self.uiSceneTab, "")
|
||||
self.uiMiscTab = QtWidgets.QWidget()
|
||||
self.uiMiscTab.setObjectName("uiMiscTab")
|
||||
@@ -472,6 +481,53 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.retranslateUi(GeneralPreferencesPageWidget)
|
||||
self.uiMiscTabWidget.setCurrentIndex(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(GeneralPreferencesPageWidget)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiProjectsPathLineEdit, self.uiProjectsPathToolButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiProjectsPathToolButton, self.uiSymbolsPathLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSymbolsPathLineEdit, self.uiSymbolsPathToolButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSymbolsPathToolButton, self.uiConfigsPathLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiConfigsPathLineEdit, self.uiConfigsPathToolButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiConfigsPathToolButton, self.uiAppliancesPathLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiAppliancesPathLineEdit, self.uiAppliancesPathToolButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiAppliancesPathToolButton, self.uiStyleComboBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiStyleComboBox, self.uiSymbolThemeComboBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSymbolThemeComboBox, self.uiImportConfigurationFilePushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiImportConfigurationFilePushButton, self.uiExportConfigurationFilePushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiExportConfigurationFilePushButton, self.uiBrowseConfigurationPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiBrowseConfigurationPushButton, self.uiImagesPathLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiImagesPathLineEdit, self.uiImagesPathToolButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiImagesPathToolButton, self.uiImageDirectoriesAddPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiImageDirectoriesAddPushButton, self.uiImageDirectoriesDeletePushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiImageDirectoriesDeletePushButton, self.uiTelnetConsoleCommandLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiTelnetConsoleCommandLineEdit, self.uiTelnetConsolePreconfiguredCommandPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiTelnetConsolePreconfiguredCommandPushButton, self.uiDelayConsoleAllSpinBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDelayConsoleAllSpinBox, self.uiVNCConsoleCommandLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiVNCConsoleCommandLineEdit, self.uiVNCConsolePreconfiguredCommandPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiVNCConsolePreconfiguredCommandPushButton, self.uiSPICEConsoleCommandLineEdit)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSPICEConsoleCommandLineEdit, self.uiSPICEConsolePreconfiguredCommandPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSPICEConsolePreconfiguredCommandPushButton, self.uiSceneWidthSpinBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSceneWidthSpinBox, self.uiSceneHeightSpinBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSceneHeightSpinBox, self.uiNodeGridSizeSpinBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiNodeGridSizeSpinBox, self.uiDrawingGridSizeSpinBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDrawingGridSizeSpinBox, self.uiRectangleSelectedItemCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiRectangleSelectedItemCheckBox, self.uiDrawLinkStatusPointsCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDrawLinkStatusPointsCheckBox, self.uiShowInterfaceLabelsOnNewProject)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiShowInterfaceLabelsOnNewProject, self.uiShowGridOnNewProject)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiShowGridOnNewProject, self.uiSnapToGridOnNewProject)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiSnapToGridOnNewProject, self.uiLimitSizeNodeSymbolCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiLimitSizeNodeSymbolCheckBox, self.uiDefaultLabelFontPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDefaultLabelFontPushButton, self.uiDefaultLabelColorPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDefaultLabelColorPushButton, self.uiDefaultNoteFontPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDefaultNoteFontPushButton, self.uiDefaultNoteColorPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDefaultNoteColorPushButton, self.uiCheckForUpdateCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiCheckForUpdateCheckBox, self.uiCrashReportCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiCrashReportCheckBox, self.uiStatsCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiStatsCheckBox, self.uiOverlayNotificationsCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiOverlayNotificationsCheckBox, self.uiExperimentalFeaturesCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiExperimentalFeaturesCheckBox, self.uiHdpiCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiHdpiCheckBox, self.uiMultiProfilesCheckBox)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiMultiProfilesCheckBox, self.uiDirectFileUpload)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiDirectFileUpload, self.uiRestoreDefaultsPushButton)
|
||||
GeneralPreferencesPageWidget.setTabOrder(self.uiRestoreDefaultsPushButton, self.uiMiscTabWidget)
|
||||
|
||||
def retranslateUi(self, GeneralPreferencesPageWidget):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
@@ -506,17 +562,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiImagesTab), _translate("GeneralPreferencesPageWidget", "Binary images"))
|
||||
self.uiTelnetConsoleSettingsGroupBox.setTitle(_translate("GeneralPreferencesPageWidget", "Console settings"))
|
||||
self.uiTelnetConsoleCommandLabel.setText(_translate("GeneralPreferencesPageWidget", "Console application command for Telnet:"))
|
||||
self.uiTelnetConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p>\n"
|
||||
"<ul>\n"
|
||||
"<li>%h = console IP or hostname</li>\n"
|
||||
"<li>%p = console port</li>\n"
|
||||
"<li>%P = VNC display</li>\n"
|
||||
"<li>%s = path of the serial connection</li>\n"
|
||||
"<li>%d = title of the console</li>\n"
|
||||
"<li>%i = project UUID</li>\n"
|
||||
"<li>%c = server URL</li>\n"
|
||||
"</ul>\n"
|
||||
"</body></html>"))
|
||||
self.uiTelnetConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%h = console IP or hostname</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%p = console port</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%d = title of the console</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%i = project UUID</li><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%n = node UUID</li></ul><li style=\" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%c = server URL</li></ul></body></html>"))
|
||||
self.uiTelnetConsolePreconfiguredCommandPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Edit"))
|
||||
self.uiConsoleMiscGroupBox.setTitle(_translate("GeneralPreferencesPageWidget", "Miscellaneous"))
|
||||
self.uiDelayConsoleAllSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " ms"))
|
||||
@@ -524,32 +570,12 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiConsoleTab), _translate("GeneralPreferencesPageWidget", "Console applications"))
|
||||
self.uiVNCConsoleSettingsGroupBox.setTitle(_translate("GeneralPreferencesPageWidget", "Settings for VNC connections"))
|
||||
self.uiVNCConsoleCommandLabel.setText(_translate("GeneralPreferencesPageWidget", "Console application command for VNC:"))
|
||||
self.uiVNCConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p>\n"
|
||||
"<ul>\n"
|
||||
"<li>%h = console IP or hostname</li>\n"
|
||||
"<li>%p = console port</li>\n"
|
||||
"<li>%P = VNC display</li>\n"
|
||||
"<li>%s = path of the serial connection</li>\n"
|
||||
"<li>%d = title of the console</li>\n"
|
||||
"<li>%i = project UUID</li>\n"
|
||||
"<li>%c = server URL</li>\n"
|
||||
"</ul>\n"
|
||||
"</body></html>"))
|
||||
self.uiVNCConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%h = console IP or hostname</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%p = console port</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%P = VNC display</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%d = title of the console</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%i = project UUID</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%n = node UUID</li></ul></body></html>"))
|
||||
self.uiVNCConsolePreconfiguredCommandPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Edit"))
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiVNCTab), _translate("GeneralPreferencesPageWidget", "VNC"))
|
||||
self.uiSPICEConsoleSettingsGroupBox.setTitle(_translate("GeneralPreferencesPageWidget", "Settings for SPICE connections"))
|
||||
self.uiSPICEConsoleCommandLabel.setText(_translate("GeneralPreferencesPageWidget", "Console application command for SPICE:"))
|
||||
self.uiSPICEConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p>\n"
|
||||
"<ul>\n"
|
||||
"<li>%h = console IP or hostname</li>\n"
|
||||
"<li>%p = console port</li>\n"
|
||||
"<li>%P = VNC display</li>\n"
|
||||
"<li>%s = path of the serial connection</li>\n"
|
||||
"<li>%d = title of the console</li>\n"
|
||||
"<li>%i = project UUID</li>\n"
|
||||
"<li>%c = server URL</li>\n"
|
||||
"</ul>\n"
|
||||
"</body></html>"))
|
||||
self.uiSPICEConsoleCommandLineEdit.setToolTip(_translate("GeneralPreferencesPageWidget", "<html><head/><body><p>Command line replacements:</p><ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li style=\" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%h = console IP or hostname</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%p = console port</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%d = title of the console</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%i = project UUID</li><li style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%n = node UUID</li></ul><p><br/></p></body></html>"))
|
||||
self.uiSPICEConsolePreconfiguredCommandPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Edit"))
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiSPICETab), _translate("GeneralPreferencesPageWidget", "SPICE"))
|
||||
self.uiSceneWidthLabel.setText(_translate("GeneralPreferencesPageWidget", "Default width:"))
|
||||
@@ -560,7 +586,6 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiDefaultNoteFontPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default font"))
|
||||
self.uiDefaultNoteColorPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default color"))
|
||||
self.uiLimitSizeNodeSymbolCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Limit the size of node symbols"))
|
||||
self.uiDrawLinkStatusPointsCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw link status points"))
|
||||
self.uiDefaultLabelFontPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default font"))
|
||||
self.uiDefaultLabelColorPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default color"))
|
||||
self.uiDefaultLabelStylePlainTextEdit.setPlainText(_translate("GeneralPreferencesPageWidget", "AaBbYyZz"))
|
||||
@@ -572,6 +597,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiNodeGridSizeLabel.setText(_translate("GeneralPreferencesPageWidget", "Default node grid size:"))
|
||||
self.uiShowGridOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Show grid on new project"))
|
||||
self.uiRectangleSelectedItemCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw a rectangle when an item is selected"))
|
||||
self.uiDrawLinkStatusPointsCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw link status points"))
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiSceneTab), _translate("GeneralPreferencesPageWidget", "Topology view"))
|
||||
self.uiCheckForUpdateCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Automatically check for update"))
|
||||
self.uiCrashReportCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Send anonymous crash reports"))
|
||||
@@ -584,4 +610,3 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiDirectFileUpload.setText(_translate("GeneralPreferencesPageWidget", "Upload files directly to computes (experimental)"))
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiMiscTab), _translate("GeneralPreferencesPageWidget", "Miscellaneous"))
|
||||
self.uiRestoreDefaultsPushButton.setText(_translate("GeneralPreferencesPageWidget", "Restore defaults"))
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>494</width>
|
||||
<height>585</height>
|
||||
<width>467</width>
|
||||
<height>657</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -191,6 +191,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="uiPortLabel">
|
||||
<property name="text">
|
||||
<string>Port:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="uiPortSpinBox">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65635</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>80</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -209,6 +229,18 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>uiEnableVMCheckBox</tabstop>
|
||||
<tabstop>uiGNS3VMEngineComboBox</tabstop>
|
||||
<tabstop>uiVMListComboBox</tabstop>
|
||||
<tabstop>uiRefreshPushButton</tabstop>
|
||||
<tabstop>uiHeadlessCheckBox</tabstop>
|
||||
<tabstop>uiRamSpinBox</tabstop>
|
||||
<tabstop>uiCpuSpinBox</tabstop>
|
||||
<tabstop>uiWhenExitKeepRadioButton</tabstop>
|
||||
<tabstop>uiWhenExitSuspendRadioButton</tabstop>
|
||||
<tabstop>uiWhenExitStopRadioButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<designerdata>
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/gns3_vm_preferences_page.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
# Created by: PyQt5 UI code generator 5.13.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_GNS3VMPreferencesPageWidget(object):
|
||||
def setupUi(self, GNS3VMPreferencesPageWidget):
|
||||
GNS3VMPreferencesPageWidget.setObjectName("GNS3VMPreferencesPageWidget")
|
||||
GNS3VMPreferencesPageWidget.resize(494, 585)
|
||||
GNS3VMPreferencesPageWidget.resize(467, 657)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
@@ -102,12 +104,30 @@ class Ui_GNS3VMPreferencesPageWidget(object):
|
||||
self.uiCpuLabel = QtWidgets.QLabel(self.uiGNS3VMSettingsGroupBox)
|
||||
self.uiCpuLabel.setObjectName("uiCpuLabel")
|
||||
self.gridLayout.addWidget(self.uiCpuLabel, 5, 0, 1, 1)
|
||||
self.uiPortLabel = QtWidgets.QLabel(self.uiGNS3VMSettingsGroupBox)
|
||||
self.uiPortLabel.setObjectName("uiPortLabel")
|
||||
self.gridLayout.addWidget(self.uiPortLabel, 2, 0, 1, 1)
|
||||
self.uiPortSpinBox = QtWidgets.QSpinBox(self.uiGNS3VMSettingsGroupBox)
|
||||
self.uiPortSpinBox.setMinimum(1)
|
||||
self.uiPortSpinBox.setMaximum(65635)
|
||||
self.uiPortSpinBox.setProperty("value", 80)
|
||||
self.uiPortSpinBox.setObjectName("uiPortSpinBox")
|
||||
self.gridLayout.addWidget(self.uiPortSpinBox, 2, 1, 1, 1)
|
||||
self.verticalLayout.addWidget(self.uiGNS3VMSettingsGroupBox)
|
||||
spacerItem = QtWidgets.QSpacerItem(10, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem)
|
||||
|
||||
self.retranslateUi(GNS3VMPreferencesPageWidget)
|
||||
QtCore.QMetaObject.connectSlotsByName(GNS3VMPreferencesPageWidget)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiEnableVMCheckBox, self.uiGNS3VMEngineComboBox)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiGNS3VMEngineComboBox, self.uiVMListComboBox)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiVMListComboBox, self.uiRefreshPushButton)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiRefreshPushButton, self.uiHeadlessCheckBox)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiHeadlessCheckBox, self.uiRamSpinBox)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiRamSpinBox, self.uiCpuSpinBox)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiCpuSpinBox, self.uiWhenExitKeepRadioButton)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiWhenExitKeepRadioButton, self.uiWhenExitSuspendRadioButton)
|
||||
GNS3VMPreferencesPageWidget.setTabOrder(self.uiWhenExitSuspendRadioButton, self.uiWhenExitStopRadioButton)
|
||||
|
||||
def retranslateUi(self, GNS3VMPreferencesPageWidget):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
@@ -126,4 +146,4 @@ class Ui_GNS3VMPreferencesPageWidget(object):
|
||||
self.uiRamLabel.setText(_translate("GNS3VMPreferencesPageWidget", "RAM:"))
|
||||
self.uiRamSpinBox.setSuffix(_translate("GNS3VMPreferencesPageWidget", " MB"))
|
||||
self.uiCpuLabel.setText(_translate("GNS3VMPreferencesPageWidget", "vCPUs:"))
|
||||
|
||||
self.uiPortLabel.setText(_translate("GNS3VMPreferencesPageWidget", "Port:"))
|
||||
|
||||
@@ -62,7 +62,7 @@ background-none;
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>986</width>
|
||||
<height>40</height>
|
||||
<height>42</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="uiEditMenu">
|
||||
@@ -164,7 +164,6 @@ background-none;
|
||||
</property>
|
||||
<addaction name="uiScreenshotAction"/>
|
||||
<addaction name="uiImportExportConfigsAction"/>
|
||||
<addaction name="uiWebInterfaceAction"/>
|
||||
<addaction name="uiWebUIAction"/>
|
||||
</widget>
|
||||
<addaction name="uiFileMenu"/>
|
||||
@@ -1197,11 +1196,6 @@ background-none;
|
||||
<string>Edit project</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="uiWebInterfaceAction">
|
||||
<property name="text">
|
||||
<string>Light Web interface</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="uiDrawLineAction">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
@@ -1237,7 +1231,7 @@ background-none;
|
||||
</action>
|
||||
<action name="uiWebUIAction">
|
||||
<property name="text">
|
||||
<string>WebUI - topology preview</string>
|
||||
<string>Web UI - beta</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="uiNewTemplateAction">
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/main_window.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
# Created by: PyQt5 UI code generator 5.13.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
@@ -46,7 +48,7 @@ class Ui_MainWindow(object):
|
||||
self.gridlayout.addWidget(self.uiGraphicsView, 0, 0, 1, 1)
|
||||
MainWindow.setCentralWidget(self.uiCentralWidget)
|
||||
self.uiMenuBar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.uiMenuBar.setGeometry(QtCore.QRect(0, 0, 986, 40))
|
||||
self.uiMenuBar.setGeometry(QtCore.QRect(0, 0, 986, 42))
|
||||
self.uiMenuBar.setObjectName("uiMenuBar")
|
||||
self.uiEditMenu = QtWidgets.QMenu(self.uiMenuBar)
|
||||
self.uiEditMenu.setObjectName("uiEditMenu")
|
||||
@@ -378,9 +380,9 @@ class Ui_MainWindow(object):
|
||||
self.uiAddLinkAction.setCheckable(True)
|
||||
icon28 = QtGui.QIcon()
|
||||
icon28.addPixmap(QtGui.QPixmap(":/icons/connection-new.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon28.addPixmap(QtGui.QPixmap(":/icons/cancel-connection.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
|
||||
icon28.addPixmap(QtGui.QPixmap(":/icons/cancel-connection.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On)
|
||||
icon28.addPixmap(QtGui.QPixmap(":/icons/connection-new-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
|
||||
icon28.addPixmap(QtGui.QPixmap(":/icons/cancel-connection.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
|
||||
self.uiAddLinkAction.setIcon(icon28)
|
||||
self.uiAddLinkAction.setObjectName("uiAddLinkAction")
|
||||
self.uiFitInViewAction = QtWidgets.QAction(MainWindow)
|
||||
@@ -425,8 +427,6 @@ class Ui_MainWindow(object):
|
||||
self.uiEditProjectAction = QtWidgets.QAction(MainWindow)
|
||||
self.uiEditProjectAction.setIcon(icon31)
|
||||
self.uiEditProjectAction.setObjectName("uiEditProjectAction")
|
||||
self.uiWebInterfaceAction = QtWidgets.QAction(MainWindow)
|
||||
self.uiWebInterfaceAction.setObjectName("uiWebInterfaceAction")
|
||||
self.uiDrawLineAction = QtWidgets.QAction(MainWindow)
|
||||
self.uiDrawLineAction.setCheckable(True)
|
||||
icon33 = QtGui.QIcon()
|
||||
@@ -438,9 +438,9 @@ class Ui_MainWindow(object):
|
||||
self.uiLockAllAction.setChecked(False)
|
||||
icon34 = QtGui.QIcon()
|
||||
icon34.addPixmap(QtGui.QPixmap(":/icons/unlock.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon34.addPixmap(QtGui.QPixmap(":/icons/lock.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
|
||||
icon34.addPixmap(QtGui.QPixmap(":/icons/lock.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On)
|
||||
icon34.addPixmap(QtGui.QPixmap(":/icons/unlock.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
|
||||
icon34.addPixmap(QtGui.QPixmap(":/icons/lock.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
|
||||
self.uiLockAllAction.setIcon(icon34)
|
||||
self.uiLockAllAction.setObjectName("uiLockAllAction")
|
||||
self.uiWebUIAction = QtWidgets.QAction(MainWindow)
|
||||
@@ -503,7 +503,6 @@ class Ui_MainWindow(object):
|
||||
self.uiAnnotateMenu.addAction(self.uiEditReadmeAction)
|
||||
self.uiToolsMenu.addAction(self.uiScreenshotAction)
|
||||
self.uiToolsMenu.addAction(self.uiImportExportConfigsAction)
|
||||
self.uiToolsMenu.addAction(self.uiWebInterfaceAction)
|
||||
self.uiToolsMenu.addAction(self.uiWebUIAction)
|
||||
self.uiMenuBar.addAction(self.uiFileMenu.menuAction())
|
||||
self.uiMenuBar.addAction(self.uiEditMenu.menuAction())
|
||||
@@ -707,13 +706,11 @@ class Ui_MainWindow(object):
|
||||
self.uiDeleteProjectAction.setText(_translate("MainWindow", "Delete project"))
|
||||
self.uiShowGridAction.setText(_translate("MainWindow", "Show the grid"))
|
||||
self.uiEditProjectAction.setText(_translate("MainWindow", "Edit project"))
|
||||
self.uiWebInterfaceAction.setText(_translate("MainWindow", "Light Web interface"))
|
||||
self.uiDrawLineAction.setText(_translate("MainWindow", "Drawn line"))
|
||||
self.uiLockAllAction.setText(_translate("MainWindow", "Lock or unlock all items"))
|
||||
self.uiLockAllAction.setToolTip(_translate("MainWindow", "Lock or unlock all items"))
|
||||
self.uiWebUIAction.setText(_translate("MainWindow", "WebUI - topology preview"))
|
||||
self.uiWebUIAction.setText(_translate("MainWindow", "Web UI - beta"))
|
||||
self.uiNewTemplateAction.setText(_translate("MainWindow", "New template"))
|
||||
|
||||
from ..compute_summary_view import ComputeSummaryView
|
||||
from ..console_view import ConsoleView
|
||||
from ..graphics_view import GraphicsView
|
||||
|
||||
@@ -125,6 +125,14 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>uiPreconfiguredCaptureReaderCommandComboBox</tabstop>
|
||||
<tabstop>uiPreconfiguredCaptureReaderCommandPushButton</tabstop>
|
||||
<tabstop>uiCaptureReaderCommandLineEdit</tabstop>
|
||||
<tabstop>uiAutoStartCheckBox</tabstop>
|
||||
<tabstop>uiCaptureAnalyzerCommandLineEdit</tabstop>
|
||||
<tabstop>uiRestoreDefaultsPushButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>980</width>
|
||||
<height>680</height>
|
||||
<width>680</width>
|
||||
<height>517</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -36,6 +36,9 @@
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>10</number>
|
||||
</property>
|
||||
@@ -73,6 +76,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -81,8 +87,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>542</width>
|
||||
<height>559</height>
|
||||
<width>269</width>
|
||||
<height>457</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -103,6 +109,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="uiButtonBox">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
@@ -131,8 +140,8 @@
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>540</x>
|
||||
<y>571</y>
|
||||
<x>672</x>
|
||||
<y>509</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>449</x>
|
||||
@@ -147,8 +156,8 @@
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>540</x>
|
||||
<y>571</y>
|
||||
<x>672</x>
|
||||
<y>509</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>449</x>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>301</height>
|
||||
<width>823</width>
|
||||
<height>367</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -36,16 +36,7 @@
|
||||
<string>New project</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<property name="margin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -163,16 +154,7 @@
|
||||
<string>Projects library</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<property name="margin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -222,6 +204,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="uiResetMacAddressesCheckBox">
|
||||
<property name="text">
|
||||
<string>&Reset MAC addresses</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -284,6 +276,19 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>uiProjectTabWidget</tabstop>
|
||||
<tabstop>uiNameLineEdit</tabstop>
|
||||
<tabstop>uiLocationLineEdit</tabstop>
|
||||
<tabstop>uiLocationBrowserToolButton</tabstop>
|
||||
<tabstop>uiOpenProjectPushButton</tabstop>
|
||||
<tabstop>uiRecentProjectsPushButton</tabstop>
|
||||
<tabstop>uiSettingsPushButton</tabstop>
|
||||
<tabstop>uiProjectsTreeWidget</tabstop>
|
||||
<tabstop>uiDeleteProjectButton</tabstop>
|
||||
<tabstop>uiDuplicateProjectPushButton</tabstop>
|
||||
<tabstop>uiRefreshProjectsPushButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/Users/noplay/code/gns3/gns3-gui/gns3/ui/project_dialog.ui'
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/project_dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.6
|
||||
# Created by: PyQt5 UI code generator 5.13.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_ProjectDialog(object):
|
||||
|
||||
def setupUi(self, ProjectDialog):
|
||||
ProjectDialog.setObjectName("ProjectDialog")
|
||||
ProjectDialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
ProjectDialog.resize(520, 301)
|
||||
ProjectDialog.resize(823, 367)
|
||||
ProjectDialog.setModal(True)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(ProjectDialog)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
@@ -99,6 +99,10 @@ class Ui_ProjectDialog(object):
|
||||
self.uiDuplicateProjectPushButton = QtWidgets.QPushButton(self.uiProjectsLibraryTab)
|
||||
self.uiDuplicateProjectPushButton.setObjectName("uiDuplicateProjectPushButton")
|
||||
self.horizontalLayout_4.addWidget(self.uiDuplicateProjectPushButton)
|
||||
self.uiResetMacAddressesCheckBox = QtWidgets.QCheckBox(self.uiProjectsLibraryTab)
|
||||
self.uiResetMacAddressesCheckBox.setChecked(True)
|
||||
self.uiResetMacAddressesCheckBox.setObjectName("uiResetMacAddressesCheckBox")
|
||||
self.horizontalLayout_4.addWidget(self.uiResetMacAddressesCheckBox)
|
||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_4.addItem(spacerItem2)
|
||||
self.uiRefreshProjectsPushButton = QtWidgets.QPushButton(self.uiProjectsLibraryTab)
|
||||
@@ -116,7 +120,7 @@ class Ui_ProjectDialog(object):
|
||||
self.horizontalLayout.addItem(spacerItem3)
|
||||
self.uiButtonBox = QtWidgets.QDialogButtonBox(ProjectDialog)
|
||||
self.uiButtonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.uiButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
|
||||
self.uiButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||
self.uiButtonBox.setObjectName("uiButtonBox")
|
||||
self.horizontalLayout.addWidget(self.uiButtonBox)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
@@ -128,6 +132,16 @@ class Ui_ProjectDialog(object):
|
||||
self.uiNameLineEdit.returnPressed.connect(ProjectDialog.accept)
|
||||
self.uiLocationLineEdit.returnPressed.connect(ProjectDialog.accept)
|
||||
QtCore.QMetaObject.connectSlotsByName(ProjectDialog)
|
||||
ProjectDialog.setTabOrder(self.uiProjectTabWidget, self.uiNameLineEdit)
|
||||
ProjectDialog.setTabOrder(self.uiNameLineEdit, self.uiLocationLineEdit)
|
||||
ProjectDialog.setTabOrder(self.uiLocationLineEdit, self.uiLocationBrowserToolButton)
|
||||
ProjectDialog.setTabOrder(self.uiLocationBrowserToolButton, self.uiOpenProjectPushButton)
|
||||
ProjectDialog.setTabOrder(self.uiOpenProjectPushButton, self.uiRecentProjectsPushButton)
|
||||
ProjectDialog.setTabOrder(self.uiRecentProjectsPushButton, self.uiSettingsPushButton)
|
||||
ProjectDialog.setTabOrder(self.uiSettingsPushButton, self.uiProjectsTreeWidget)
|
||||
ProjectDialog.setTabOrder(self.uiProjectsTreeWidget, self.uiDeleteProjectButton)
|
||||
ProjectDialog.setTabOrder(self.uiDeleteProjectButton, self.uiDuplicateProjectPushButton)
|
||||
ProjectDialog.setTabOrder(self.uiDuplicateProjectPushButton, self.uiRefreshProjectsPushButton)
|
||||
|
||||
def retranslateUi(self, ProjectDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
@@ -146,6 +160,7 @@ class Ui_ProjectDialog(object):
|
||||
self.uiProjectsTreeWidget.headerItem().setText(2, _translate("ProjectDialog", "Path"))
|
||||
self.uiDeleteProjectButton.setText(_translate("ProjectDialog", "Delete"))
|
||||
self.uiDuplicateProjectPushButton.setText(_translate("ProjectDialog", "Duplicate"))
|
||||
self.uiResetMacAddressesCheckBox.setText(_translate("ProjectDialog", "&Reset MAC addresses"))
|
||||
self.uiRefreshProjectsPushButton.setText(_translate("ProjectDialog", "Refresh list"))
|
||||
self.uiProjectTabWidget.setTabText(self.uiProjectTabWidget.indexOf(self.uiProjectsLibraryTab), _translate("ProjectDialog", "Projects library"))
|
||||
self.uiSettingsPushButton.setText(_translate("ProjectDialog", "Settings"))
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>607</width>
|
||||
<height>308</height>
|
||||
<width>647</width>
|
||||
<height>214</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Welcome</string>
|
||||
<string>Project variables</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
@@ -32,7 +32,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Loading.. Please wait.</p></body></html></string>
|
||||
<string><html><head/><body><p>Please provide the missing values for the project variables:</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/home/dominik/projects/gns3-gui/gns3/ui/project_welcome_dialog.ui'
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/project_welcome_dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.8.2
|
||||
# Created by: PyQt5 UI code generator 5.13.0
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_ProjectWelcomeDialog(object):
|
||||
def setupUi(self, ProjectWelcomeDialog):
|
||||
ProjectWelcomeDialog.setObjectName("ProjectWelcomeDialog")
|
||||
ProjectWelcomeDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
ProjectWelcomeDialog.resize(607, 308)
|
||||
ProjectWelcomeDialog.resize(659, 220)
|
||||
ProjectWelcomeDialog.setModal(True)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(ProjectWelcomeDialog)
|
||||
self.verticalLayout.setContentsMargins(-1, -1, 12, -1)
|
||||
@@ -48,13 +50,14 @@ class Ui_ProjectWelcomeDialog(object):
|
||||
self.uiOkButton.setObjectName("uiOkButton")
|
||||
self.horizontalLayout.addWidget(self.uiOkButton)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem1)
|
||||
|
||||
self.retranslateUi(ProjectWelcomeDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(ProjectWelcomeDialog)
|
||||
|
||||
def retranslateUi(self, ProjectWelcomeDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
ProjectWelcomeDialog.setWindowTitle(_translate("ProjectWelcomeDialog", "Welcome"))
|
||||
self.label.setText(_translate("ProjectWelcomeDialog", "<html><head/><body><p>Loading.. Please wait.</p></body></html>"))
|
||||
|
||||
ProjectWelcomeDialog.setWindowTitle(_translate("ProjectWelcomeDialog", "Project variables"))
|
||||
self.label.setText(_translate("ProjectWelcomeDialog", "<html><head/><body><p>Please provide the missing values for the project variables:</p></body></html>"))
|
||||
from . import resources_rc
|
||||
|
||||
@@ -6,16 +6,10 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1069</width>
|
||||
<height>615</height>
|
||||
<width>1081</width>
|
||||
<height>534</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Setup Wizard</string>
|
||||
</property>
|
||||
@@ -83,7 +77,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Eveything that is supported by your system will run on your computer.</string>
|
||||
<string>Everything that is supported by your system will run on your computer.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run appliances on my local computer</string>
|
||||
|
||||
@@ -2,21 +2,18 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/setup_wizard.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
# Created by: PyQt5 UI code generator 5.13.0
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_SetupWizard(object):
|
||||
def setupUi(self, SetupWizard):
|
||||
SetupWizard.setObjectName("SetupWizard")
|
||||
SetupWizard.resize(1069, 615)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(SetupWizard.sizePolicy().hasHeightForWidth())
|
||||
SetupWizard.setSizePolicy(sizePolicy)
|
||||
SetupWizard.resize(1081, 534)
|
||||
SetupWizard.setModal(True)
|
||||
SetupWizard.setWizardStyle(QtWidgets.QWizard.ModernStyle)
|
||||
SetupWizard.setOptions(QtWidgets.QWizard.NoBackButtonOnStartPage)
|
||||
@@ -247,7 +244,7 @@ class Ui_SetupWizard(object):
|
||||
self.uiVMRadioButton.setToolTip(_translate("SetupWizard", "Dynamips, IOU, VPCS and Qemu will use this virtual machine."))
|
||||
self.uiVMRadioButton.setText(_translate("SetupWizard", "Run appliances in a virtual machine"))
|
||||
self.label.setText(_translate("SetupWizard", "Requires to download and install the GNS3 VM (available for free) "))
|
||||
self.uiLocalRadioButton.setToolTip(_translate("SetupWizard", "Eveything that is supported by your system will run on your computer."))
|
||||
self.uiLocalRadioButton.setToolTip(_translate("SetupWizard", "Everything that is supported by your system will run on your computer."))
|
||||
self.uiLocalRadioButton.setText(_translate("SetupWizard", "Run appliances on my local computer"))
|
||||
self.uiLocalLabel.setText(_translate("SetupWizard", "A limited number of appliances like the Cisco IOS routers <= C7200 can be run"))
|
||||
self.uiRemoteControllerRadioButton.setText(_translate("SetupWizard", "Run appliances on a remote server (advanced usage)"))
|
||||
@@ -286,5 +283,4 @@ class Ui_SetupWizard(object):
|
||||
self.uiSummaryWizardPage.setSubTitle(_translate("SetupWizard", "The server type has been configured, please see the summary of the settings below"))
|
||||
self.uiSummaryTreeWidget.headerItem().setText(0, _translate("SetupWizard", "1"))
|
||||
self.uiSummaryTreeWidget.headerItem().setText(1, _translate("SetupWizard", "2"))
|
||||
|
||||
from . import resources_rc
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import shlex
|
||||
import importlib
|
||||
import hashlib
|
||||
import re
|
||||
@@ -108,3 +110,14 @@ def natural_sort_key(s):
|
||||
* pc10
|
||||
"""
|
||||
return [int(text) if text.isdecimal() else text.lower() for text in re.split('([0-9]+)', s)]
|
||||
|
||||
|
||||
def shlex_quote(s):
|
||||
"""
|
||||
Compatible shlex_quote to handle case where Windows needs double quotes around file names, not single quotes.
|
||||
"""
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
|
||||
else:
|
||||
return shlex.quote(s)
|
||||
|
||||
@@ -66,7 +66,7 @@ def bring_window_to_front_from_process_name(process_name, title=None):
|
||||
elif title in win32gui.GetWindowText(hwnd):
|
||||
set_foreground_window(hwnd)
|
||||
return True
|
||||
except psutil.Error:
|
||||
except (OSError, psutil.Error):
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
@@ -28,17 +28,18 @@ class ExportProjectWorker(QtCore.QObject):
|
||||
finished = QtCore.pyqtSignal()
|
||||
updated = QtCore.pyqtSignal(int)
|
||||
|
||||
def __init__(self, project, path, include_images, include_snapshots, compression):
|
||||
def __init__(self, project, path, include_images, include_snapshots, reset_mac_addresses, compression):
|
||||
super().__init__()
|
||||
self._project = project
|
||||
self._include_images = include_images
|
||||
self._include_snapshots = include_snapshots
|
||||
self._reset_mac_addresses = reset_mac_addresses
|
||||
self._path = path
|
||||
self._compression = compression
|
||||
|
||||
def run(self):
|
||||
if self._project:
|
||||
self._project.get("/export?include_images={}&include_snapshots={}&compression={}".format(self._include_images, self._include_snapshots, self._compression),
|
||||
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)
|
||||
|
||||
@@ -80,11 +80,10 @@ def get_windows_interfaces():
|
||||
:returns: list of windows interfaces
|
||||
"""
|
||||
|
||||
import win32com.client
|
||||
import pywintypes
|
||||
|
||||
interfaces = []
|
||||
try:
|
||||
import win32com.client
|
||||
locator = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
||||
service = locator.ConnectServer(".", "root\cimv2")
|
||||
network_configs = service.InstancesOf("Win32_NetworkAdapterConfiguration")
|
||||
@@ -109,7 +108,7 @@ def get_windows_interfaces():
|
||||
"netcard": adapter.name,
|
||||
"netmask": netmask,
|
||||
"type": "ethernet"})
|
||||
except (AttributeError, pywintypes.com_error):
|
||||
except (ImportError, AttributeError, pywintypes.com_error):
|
||||
log.warning("Could not use the COM service to retrieve interface info, trying using the registry...")
|
||||
return _get_windows_interfaces_from_registry()
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ def sudo(*commands, parent=None, shell=False):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
@@ -21,7 +21,7 @@ Thread to run a command and wait for its completion.
|
||||
|
||||
import tempfile
|
||||
import subprocess
|
||||
import shlex
|
||||
from ..utils import shlex_quote
|
||||
from ..qt import QtCore
|
||||
|
||||
import logging
|
||||
@@ -63,7 +63,7 @@ class WaitForCommandWorker(QtCore.QObject):
|
||||
timeout=self._timeout,
|
||||
shell=self._shell)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
command_string = " ".join(shlex.quote(s) for s in self._command)
|
||||
command_string = " ".join(shlex_quote(s) for s in self._command)
|
||||
self.error.emit('Could not execute command "{}": {}'.format(command_string, e), True)
|
||||
return
|
||||
self.finished.emit()
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Thread to run a command with administrator rights on Windows and wait for its completion.
|
||||
"""
|
||||
|
||||
import shlex
|
||||
from ..utils import shlex_quote
|
||||
from ..qt import QtCore
|
||||
|
||||
import logging
|
||||
@@ -69,7 +69,7 @@ class WaitForRunAsWorker(QtCore.QObject):
|
||||
lpFile=program,
|
||||
lpParameters=params)
|
||||
except pywintypes.error as e:
|
||||
command_string = " ".join(shlex.quote(s) for s in self._command)
|
||||
command_string = " ".join(shlex_quote(s) for s in self._command)
|
||||
self.error.emit('Could not execute command "{}": {}'.format(command_string, e), True)
|
||||
return
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "2.2.0b2"
|
||||
__version_info__ = (2, 2, 0, -99)
|
||||
__version__ = "2.2.11"
|
||||
__version_info__ = (2, 2, 11, 0)
|
||||
|
||||
# If it's a git checkout try to add the commit
|
||||
if "dev" in __version__:
|
||||
|
||||
@@ -24,26 +24,34 @@ import os
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
from .controller import Controller
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def vncConsole(host, port, command):
|
||||
def vncConsole(node, port, command):
|
||||
"""
|
||||
Start a VNC console program.
|
||||
|
||||
:param host: host or IP address
|
||||
:param node: Node instance
|
||||
:param port: port number
|
||||
"""
|
||||
|
||||
if len(command.strip(' ')) == 0:
|
||||
log.warning('VNC client is not configured')
|
||||
log.error("VNC client is not configured")
|
||||
return
|
||||
|
||||
name = node.name()
|
||||
host = node.consoleHost()
|
||||
|
||||
# replace the place-holders by the actual values
|
||||
command = command.replace("%h", host)
|
||||
command = command.replace("%p", str(port))
|
||||
command = command.replace("%P", str(port - 5900))
|
||||
command = command.replace("%d", name.replace('"', '\\"'))
|
||||
command = command.replace("%i", node.project().id())
|
||||
command = command.replace("%n", str(node.id()))
|
||||
|
||||
try:
|
||||
log.debug('starting VNC program "{}"'.format(command))
|
||||
@@ -56,4 +64,3 @@ def vncConsole(host, port, command):
|
||||
subprocess.Popen(args, env=os.environ)
|
||||
except (OSError, ValueError, subprocess.SubprocessError) as e:
|
||||
log.error("Could not start VNC program with command '{}': {}".format(command, e))
|
||||
raise
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
-rrequirements.txt
|
||||
|
||||
#PyQt5==5.7
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
jsonschema==2.6.0 # pyup: ignore
|
||||
raven>=5.23.0
|
||||
psutil>=2.2.1
|
||||
distro>=1.3.0
|
||||
jsonschema==3.2.0
|
||||
sentry-sdk>=0.14.4
|
||||
psutil==5.7.0
|
||||
distro>=1.3.0
|
||||
|
||||
11
resources/linux/applications/gns3.desktop
Normal file
@@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Exec=gns3 %f
|
||||
Name=GNS3
|
||||
Comment=GNS3 Graphical Network Simulator
|
||||
Icon=gns3
|
||||
Categories=Education;Network;
|
||||
MimeType=application/x-gns3;application/x-gns3appliance;application/x-gns3project;
|
||||
Keywords=simulator;network;netsim;
|
||||
28
resources/linux/gns3-gui.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-gns3">
|
||||
<comment>GNS3 Project File</comment>
|
||||
<comment xml:lang="en">GNS3 Project File</comment>
|
||||
<glob pattern="*.gns3"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-gns3project">
|
||||
<comment>GNS3 Project File</comment>
|
||||
<comment xml:lang="en">GNS3 Portable Project File</comment>
|
||||
<glob pattern="*.gns3project"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-gns3project">
|
||||
<comment>GNS3 Project File</comment>
|
||||
<comment xml:lang="en">GNS3 Portable Project File</comment>
|
||||
<glob pattern="*.gns3p"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-gns3appliance">
|
||||
<comment>GNS3 Appliance File</comment>
|
||||
<comment xml:lang="en">GNS3 Appliance File</comment>
|
||||
<glob pattern="*.gns3appliance"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-gns3appliance">
|
||||
<comment>GNS3 Appliance File</comment>
|
||||
<comment xml:lang="en">GNS3 Appliance File</comment>
|
||||
<glob pattern="*.gns3a"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
BIN
resources/linux/icons/hicolor/16x16/apps/gns3.png
Normal file
|
After Width: | Height: | Size: 832 B |
BIN
resources/linux/icons/hicolor/32x32/apps/gns3.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
resources/linux/icons/hicolor/48x48/apps/gns3.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
85
resources/linux/icons/hicolor/scalable/apps/gns3.svg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 256 256"
|
||||
enable-background="new 0 0 792 612"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
width="100%"
|
||||
height="100%"
|
||||
sodipodi:docname="gns3_project_base_icon.svg"
|
||||
inkscape:export-filename="/home/grossmj/Downloads/Logos/gns3_project.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"><metadata
|
||||
id="metadata30"><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:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs28" /><sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="688"
|
||||
inkscape:window-height="508"
|
||||
id="namedview26"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="99.209744"
|
||||
inkscape:cy="115.40526"
|
||||
inkscape:window-x="692"
|
||||
inkscape:window-y="203"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g3"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" /><g
|
||||
id="g3"
|
||||
transform="translate(-19.09309,-355.40651)"><linearGradient
|
||||
id="SVGID_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="404.2251"
|
||||
y1="365.32321"
|
||||
x2="384.2139"
|
||||
y2="136.595"
|
||||
gradientTransform="matrix(0.9154681,0,0,0.8343604,-205.93817,243.15241)"><stop
|
||||
offset="0"
|
||||
style="stop-color:#00A5CE"
|
||||
id="stop6" /><stop
|
||||
offset="0.1989"
|
||||
style="stop-color:#1295CB"
|
||||
id="stop8" /><stop
|
||||
offset="0.5994"
|
||||
style="stop-color:#426BC5"
|
||||
id="stop10" /><stop
|
||||
offset="1"
|
||||
style="stop-color:#783CBD"
|
||||
id="stop12" /></linearGradient><path
|
||||
d="m 171.60071,405.01864 c 0,4.83934 -4.30236,8.76063 -9.61217,8.76063 -5.30966,0 -9.61254,-3.92129 -9.61254,-8.76063 0,-4.83945 4.30288,-8.76118 9.61254,-8.76118 5.30981,0 9.61217,3.92173 9.61217,8.76118 z M 66.413524,449.07279 c -5.767437,28.78523 2.105621,49.56075 14.372861,64.41269 21.879695,28.11798 54.012515,33.04057 54.012515,33.04057 37.53432,6.50778 66.09685,-5.33982 83.21603,-20.02463 21.51357,-18.60601 20.7815,-37.04552 20.87311,-37.29575 0.18299,-8.59424 -2.92957,-14.7682 -8.42235,-19.02378 -9.24633,-7.09206 -20.86017,2.69341 -20.86017,2.69341 -19.49927,4.17192 -15.94169,13.32637 -15.94169,13.32637 1.09845,4.83918 3.84457,8.00978 6.95741,9.84533 5.9504,3.33747 12.26728,2.67002 12.26728,2.67002 9.3378,-1.0009 12.08427,-9.0109 11.53485,-13.01608 -0.91555,-6.34088 -7.23234,-7.50924 -7.23234,-7.50924 -2.38005,-0.41709 -4.21098,-0.0833 -5.58438,0.58407 -2.01358,0.75135 -2.83796,2.00243 -2.83796,2.00243 -1.46454,2.25284 -0.54914,3.50469 0.36639,4.00532 0.54947,0.33369 1.19036,0.33369 1.19036,0.33369 0.73207,0 1.18967,-0.16678 1.73912,-0.41707 1.28185,-0.6675 2.2885,-1.7525 4.21141,-1.085 2.83793,1.00135 2.10546,3.7549 2.10546,3.7549 -0.641,3.00358 -2.5634,4.42215 -4.66897,4.9226 -2.83795,0.66751 -5.76742,-0.16678 -5.76742,-0.16678 -10.07028,-3.75491 -5.58438,-12.93261 -5.58438,-12.93261 3.11245,-5.75734 10.07038,-6.42485 10.07038,-6.42485 10.89377,-1.58499 18.12615,5.08962 16.93565,16.35342 -1.37299,14.0176 -13.18224,23.27863 -25.81601,29.11962 -47.32956,22.19367 -82.66676,-5.92441 -82.66676,-5.92441 -13.18276,-11.43056 -10.07016,-23.52883 -8.60541,-27.3666 0.54929,-1.41865 2.01406,-1.6691 2.74639,-1.6691 10.80253,-0.83407 19.774,17.35498 20.68947,19.85809 1.92249,4.17184 4.94343,6.50765 7.78138,8.00972 6.40841,3.25405 13.18313,1.66883 13.18313,1.66883 9.33745,-2.9202 6.1335,-9.84533 6.1335,-9.84533 -1.09842,-2.75341 -2.74644,-3.50477 -4.02786,-3.42093 -1.19054,0 -2.10564,0.83442 -2.10564,0.83442 -0.45776,0.50034 -1.00731,0.83393 -1.46489,1.25125 -5.95071,4.58892 -8.8803,-0.25044 -8.8803,-0.25044 -2.10546,-3.08697 1.09901,-4.75583 1.09901,-4.75583 0.64083,-0.33355 1.28141,-0.50061 1.73895,-0.66748 2.38042,-0.66714 2.01441,-2.50311 2.01441,-2.50311 -0.36609,-3.42046 -4.57745,-2.00244 -4.57745,-2.00244 -4.1195,1.3353 -5.40139,4.42214 -5.76771,5.75738 -0.0915,0.41727 -0.1831,0.75095 -0.8236,0.83435 -1.18996,0.16676 -1.18996,-1.75225 -1.18996,-1.75225 -0.36654,-13.68349 -4.4859,-19.77428 -6.40854,-21.94348 -0.54936,-0.66796 -1.46475,-1.08497 -1.73923,-1.25176 -4.30289,-1.7521 -9.06329,-5.17302 -9.06329,-5.17302 -9.06314,-5.92391 -5.4928,-11.26375 -5.4928,-11.26375 5.40125,-12.34883 9.79555,-16.60411 9.79555,-16.60411 3.66173,-3.17061 7.50696,-0.33364 7.50696,-0.33364 -2.83792,3.17075 -5.03524,6.25803 -5.03524,6.25803 -6.49982,9.42787 -7.59841,13.26624 -7.59841,13.26624 -0.0915,0.33355 -0.0915,0.91765 0.45776,1.58531 6.68291,10.42945 24.7176,12.01453 24.80912,12.01453 4.94374,0.66741 8.97144,0.0834 8.97144,0.0834 2.38032,-0.41707 2.74642,1.00145 2.74642,1.00145 0.27482,0.83443 1.28199,3.17072 1.28199,3.17072 1.73928,3.33708 4.3939,1.5849 4.3939,1.5849 1.00743,-0.66745 1.465,-1.5849 1.465,-2.33588 0,-0.75128 -0.45757,-1.25175 -0.45757,-1.25175 -1.19053,-1.58542 -1.55653,-3.0869 -1.55653,-3.0869 -0.54909,-1.83594 1.73957,-2.58648 1.73957,-2.58648 4.48544,-1.83599 6.95727,1.66826 6.95727,1.66826 2.38047,2.5865 7.14092,-0.66745 7.14092,-0.66745 3.8449,-2.66992 -4.0976,0.19062 -4.0976,0.19062 -9.88688,-8.26038 -16.56361,-1.34696 -16.56361,-1.34696 -0.64104,0.7511 -3.04976,-2.76495 -3.04976,-2.76495 -9.61199,0.25048 -18.76672,-6.17438 -18.76672,-6.50818 3.66187,-5.5901 10.71075,-15.35234 10.71075,-15.35234 l 37.25985,13.09964 c 1.46442,0.41703 3.02095,0.0833 3.02095,0.0833 l 58.04043,-10.67989 c 1.28195,-0.16687 1.09896,-1.58503 1.09896,-1.58503 0,-1.16837 -0.0919,-1.33515 -1.19046,-1.50194 -1.18994,-0.16678 -56.48449,-4.08849 -56.48449,-4.08849 -3.02097,-0.16681 -3.02097,-3.92122 0.0915,-4.17141 l 40.64728,-3.08733 c 0.91552,-0.0834 1.09848,-0.91783 1.09848,-0.91783 1.09844,-4.92279 0.73198,-11.34711 0.73198,-11.34711 11.80979,-12.01521 6.1339,-23.86288 6.1339,-23.86288 -7.23198,-16.35348 -23.1616,-16.60396 -23.1616,-16.60396 -26.18203,-26.36565 -60.60355,-23.27867 -60.60355,-23.27867 -1.55672,0 -1.6483,1.1683 -1.6483,1.1683 l -3.93608,22.69445 c -0.18325,1.25177 -0.91581,1.58533 -0.91581,1.58533 l -5.21788,3.75447 c -0.6411,0.58421 -0.82414,0.58421 -1.73956,0.58421 -0.82389,0.0835 -3.38706,0.41742 -3.38706,0.41742 -61.519582,11.93091 -70.399667,62.15974 -70.399667,62.15974 z M 232.66269,395.33968 c 2.47189,7.17584 -4.30298,9.42866 -4.30298,9.42866 -1.28144,-5.59034 -5.76742,-13.4333 -5.76742,-13.4333 7.59878,-2.16932 10.0704,4.00464 10.0704,4.00464 z m -38.08373,12.7658 c 0,13.18286 -11.71793,23.94621 -26.27368,23.94621 -14.46437,0 -26.27391,-10.67996 -26.27391,-23.94621 0,-13.26611 11.80954,-23.94585 26.27391,-23.94585 14.55575,0 26.27368,10.76313 26.27368,23.94585 z"
|
||||
id="path14"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#SVGID_1_);stroke:#000000;stroke-width:1.59908056;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ssssscccccccccccccccccccccccccccccccccccccccccccccccccccccccccscccccccccccccccccccccccccccccccccccsssss" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.53344321;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 158.75467,430.66421 c -20.07367,-7.21051 -22.89938,-32.31848 -4.80964,-42.73635 5.48181,-3.15698 10.67085,-4.37809 16.54241,-3.89283 9.69836,0.8015 17.57306,5.9338 21.90388,14.2758 2.15749,4.15572 2.35397,5.15328 2.09049,10.61352 -0.34596,7.1686 -2.32163,11.43951 -7.45042,16.1059 -5.29156,4.81452 -10.10597,6.6958 -17.7209,6.92464 -4.93337,0.14827 -7.384,-0.1514 -10.55582,-1.29068 z m 8.60473,-19.04379 c 4.68093,-3.03776 4.59848,-10.37728 -0.15058,-13.40894 -4.87998,-3.11524 -12.93893,-0.19374 -14.0254,5.08442 -1.51639,7.36707 7.49622,12.65953 14.17598,8.32452 z"
|
||||
id="path3756"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.53344321;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 227.52716,403.78857 c -0.005,-0.70451 -1.22789,-3.82375 -2.7167,-6.93163 -2.55419,-5.33189 -2.6206,-5.67568 -1.17683,-6.09329 5.73392,-1.65864 11.08127,3.87024 9.37974,9.69827 -0.98678,3.37997 -5.465,6.09542 -5.48621,3.32665 z"
|
||||
id="path3758"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
sodipodi:nodetypes="sccccccccccscssccsscccccccsss"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3810"
|
||||
d="m 239.93926,461.59913 0.0895,0.12221 -1.17469,-1.46733 -32.98343,9.00545 -37.23862,-8.13752 -55.68743,19.71173 -26.020537,-23.43049 -22.596889,16.06861 -11.889522,-13.06537 -0.909404,1.46024 -0.899284,1.4798 c -10.753621,17.54929 -16.66218,36.40349 -16.66218,54.40796 0,27.619 11.056869,51.53032 32.647508,68.48515 20.408405,16.0284 49.491298,25.55892 80.597778,25.55892 31.10647,0 59.83583,-9.88212 80.24671,-25.91052 21.59065,-16.95484 32.29143,-40.51455 32.29143,-68.13355 -0.002,-19.76102 -7.4557,-37.40288 -19.8109,-56.15529 z m -93.0808,146.64361 c -64.382901,0 -109.352655,-37.21035 -109.352655,-90.48655 0,-17.02801 5.398302,-34.83435 15.234948,-51.5232 l 9.904862,13.47926 23.843928,-16.75492 25.086777,22.57192 56.84998,-20.39383 37.73928,8.09108 30.77268,-8.56604 c 12.01921,18.34365 19.27279,33.79352 19.27279,53.09573 0,53.2762 -44.96719,90.48655 -109.35259,90.48655 z" /><path
|
||||
style="fill:#fefefa;fill-opacity:1;stroke:#ffffff;stroke-width:1.04580986000000009;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 138.44788,607.85596 C 113.73945,606.22444 93.962212,600.20959 76.995281,589.1664 55.352378,575.07977 42.490317,555.32103 38.520707,530.06164 c -0.759205,-4.83094 -0.874973,-17.42518 -0.205365,-22.34039 1.791597,-13.15109 5.868285,-25.3134 12.758033,-38.06201 0.84206,-1.55812 1.659698,-2.75565 1.816969,-2.66115 0.157281,0.0945 2.381859,3.03194 4.9435,6.5277 2.561642,3.49575 4.723755,6.35591 4.80468,6.35591 0.0809,0 5.326847,-3.64298 11.657596,-8.0955 6.330738,-4.45253 11.699908,-8.16513 11.931484,-8.25022 0.231577,-0.085 4.398802,3.42528 9.260505,7.80084 4.861701,4.37556 10.457641,9.41045 12.435431,11.18865 l 3.59596,3.23309 28.31322,-10.1561 c 15.57226,-5.58585 28.50887,-10.1561 28.74803,-10.1561 0.23916,0 8.7836,1.79241 18.98766,3.9831 l 18.55285,3.98308 14.95072,-4.15493 c 8.2229,-2.28522 15.17245,-4.15493 15.44345,-4.15493 0.27101,0 1.79664,2.10211 3.39032,4.67137 11.40625,18.38862 15.96668,32.0966 15.97665,48.02357 0.006,9.17148 -1.01587,16.56703 -3.40156,24.6239 -9.62567,32.50771 -38.51348,55.82167 -78.32153,63.20957 -10.50247,1.94913 -25.62193,2.8911 -35.71143,2.22487 z"
|
||||
id="path3821"
|
||||
inkscape:connector-curvature="0" /></g></svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo '
|
||||
_______ ________ _______ ______
|
||||
| \ | \| \ / \
|
||||
| $$$$$$$\| $$$$$$$$| $$$$$$$\| $$$$$$\
|
||||
| $$__/ $$| $$__ | $$__/ $$| $$__/ $$
|
||||
| $$ $$| $$ \ | $$ $$ >$$ $$
|
||||
| $$$$$$$ | $$$$$ | $$$$$$$ | $$$$$$
|
||||
| $$ | $$_____ | $$ | $$__/ $$
|
||||
| $$ | $$ \| $$ \$$ $$
|
||||
\$$ \$$$$$$$$ \$$ \$$$$$$
|
||||
|
||||
'
|
||||
|
||||
|
||||
find . -name '*.py' -exec autopep8 --in-place -v --aggressive --aggressive \{\} \;
|
||||
|
||||
echo "It's clean"
|
||||
32
setup.py
@@ -24,7 +24,7 @@ if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 4)
|
||||
raise SystemExit("Python 3.4 or higher is required")
|
||||
|
||||
|
||||
class Tox(TestCommand):
|
||||
class PyTest(TestCommand):
|
||||
|
||||
def finalize_options(self):
|
||||
TestCommand.finalize_options(self)
|
||||
@@ -33,17 +33,36 @@ class Tox(TestCommand):
|
||||
|
||||
def run_tests(self):
|
||||
# import here, cause outside the eggs aren't loaded
|
||||
import tox
|
||||
errcode = tox.cmdline(self.test_args)
|
||||
import pytest
|
||||
|
||||
errcode = pytest.main(self.test_args)
|
||||
sys.exit(errcode)
|
||||
|
||||
if sys.platform.startswith('linux'):
|
||||
data_files = [
|
||||
("share/applications/", ["resources/linux/applications/gns3.desktop"]),
|
||||
("share/mime/packages", ["resources/linux/gns3-gui.xml"]),
|
||||
("share/icons/hicolor/16x16/apps", ["resources/linux/icons/hicolor/16x16/apps/gns3.png"]),
|
||||
("share/icons/hicolor/32x32/apps", ["resources/linux/icons/hicolor/32x32/apps/gns3.png"]),
|
||||
("share/icons/hicolor/48x48/apps", ["resources/linux/icons/hicolor/48x48/apps/gns3.png"]),
|
||||
("share/icons/hicolor/48x48/mimetypes", ["resources/linux/icons/hicolor/48x48/mimetypes/application-x-gns3.png",
|
||||
"resources/linux/icons/hicolor/48x48/mimetypes/application-x-gns3appliance.png",
|
||||
"resources/linux/icons/hicolor/48x48/mimetypes/application-x-gns3project.png"]),
|
||||
("share/icons/hicolor/scalable/apps", ["resources/linux/icons/hicolor/scalable/apps/gns3.svg"]),
|
||||
("share/icons/hicolor/scalable/mimetypes", ["resources/linux/icons/hicolor/scalable/mimetypes/application-x-gns3.svg",
|
||||
"resources/linux/icons/hicolor/scalable/mimetypes/application-x-gns3appliance.svg",
|
||||
"resources/linux/icons/hicolor/scalable/mimetypes/application-x-gns3project.svg"]),
|
||||
]
|
||||
else:
|
||||
data_files = []
|
||||
|
||||
setup(
|
||||
name="gns3-gui",
|
||||
version=__import__("gns3").__version__,
|
||||
url="http://github.com/GNS3/gns3-gui",
|
||||
license="GNU General Public License v3 (GPLv3)",
|
||||
tests_require=["tox"],
|
||||
cmdclass={"test": Tox},
|
||||
tests_require=["pytest"],
|
||||
cmdclass={"test": PyTest},
|
||||
author="Jeremy Grossmann",
|
||||
author_email="package-maintainer@gns3.net",
|
||||
description="GNS3 graphical interface for the GNS3 server.",
|
||||
@@ -54,10 +73,12 @@ setup(
|
||||
"gns3 = gns3.main:main"
|
||||
]
|
||||
},
|
||||
data_files=data_files,
|
||||
packages=find_packages(".", exclude=["docs", "tests"]),
|
||||
include_package_data=True,
|
||||
package_data={"gns3": ["configs/*.txt", "schemas/*.json"]},
|
||||
platforms="any",
|
||||
setup_requires=["setuptools>=17.1"],
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
@@ -74,6 +95,7 @@ setup(
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ import json
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from gns3.local_config import LocalConfig
|
||||
from gns3.version import __version_info__
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -119,16 +120,17 @@ def test_migrateOldConfigOSX(tmpdir):
|
||||
|
||||
local_config = LocalConfig()
|
||||
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'GNS3'))
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'GNS3' / 'hello'))
|
||||
version = "{}.{}".format(__version_info__[0], __version_info__[1])
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'GNS3' / version))
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'GNS3' / version / 'hello'))
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'gns3.net'))
|
||||
|
||||
# It should migrate only one time
|
||||
open(str(tmpdir / '.config' / 'gns3.net' / 'world'), 'w+').close()
|
||||
|
||||
local_config._migrateOldConfigPath()
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'GNS3' / 'hello'))
|
||||
assert not os.path.exists(str(tmpdir / '.config' / 'GNS3' / 'world'))
|
||||
assert os.path.exists(str(tmpdir / '.config' / 'GNS3' / version / 'hello'))
|
||||
assert not os.path.exists(str(tmpdir / '.config' / 'GNS3' / version / 'world'))
|
||||
|
||||
|
||||
def test_migrate13Config(tmpdir):
|
||||
|
||||
@@ -23,33 +23,35 @@ from unittest.mock import patch
|
||||
from gns3.spice_console import spiceConsole
|
||||
|
||||
|
||||
def test_spice_console_on_linux_and_mac():
|
||||
def test_spice_console_on_linux_and_mac(vpcs_device):
|
||||
with patch('subprocess.Popen') as popen, \
|
||||
patch('sys.platform', new="linux"):
|
||||
|
||||
spiceConsole('localhost', '2525', 'command %h %p')
|
||||
vpcs_device.settings()["console_host"] = "localhost"
|
||||
spiceConsole(vpcs_device, '2525', 'command %h %p')
|
||||
popen.assert_called_once_with(shlex.split('command localhost 2525'), env=os.environ)
|
||||
|
||||
|
||||
def test_spice_console_on_windows():
|
||||
def test_spice_console_on_windows(vpcs_device):
|
||||
with patch('subprocess.Popen') as popen, \
|
||||
patch('sys.platform', new="win"):
|
||||
|
||||
spiceConsole('localhost', '2525', 'command %h %p')
|
||||
vpcs_device.settings()["console_host"] = "localhost"
|
||||
spiceConsole(vpcs_device, '2525', 'command %h %p')
|
||||
popen.assert_called_once_with('command localhost 2525')
|
||||
|
||||
|
||||
def test_spice_console_on_linux_with_popen_issues():
|
||||
with patch('subprocess.Popen', side_effect=OSError("Dummy")), \
|
||||
patch('sys.platform', new="linux"):
|
||||
|
||||
with pytest.raises(OSError):
|
||||
spiceConsole('localhost', '2525', 'command %h %p')
|
||||
# def test_spice_console_on_linux_with_popen_issues():
|
||||
# with patch('subprocess.Popen', side_effect=OSError("Dummy")), \
|
||||
# patch('sys.platform', new="linux"):
|
||||
#
|
||||
# with pytest.raises(OSError):
|
||||
# spiceConsole('localhost', '2525', 'command %h %p')
|
||||
|
||||
|
||||
def test_spice_console_with_ipv6_support():
|
||||
def test_spice_console_with_ipv6_support(vpcs_device):
|
||||
with patch('subprocess.Popen') as popen, \
|
||||
patch('sys.platform', new="linux"):
|
||||
|
||||
spiceConsole('::1', '2525', 'command %h %p')
|
||||
vpcs_device.settings()["console_host"] = "::1"
|
||||
spiceConsole(vpcs_device, '2525', 'command %h %p')
|
||||
popen.assert_called_once_with(shlex.split('command [::1] 2525'), env=os.environ)
|
||||
|
||||