Compare commits

...

195 Commits

Author SHA1 Message Date
grossmj
4e50c2a4b1 Release v2.2.11 2020-07-09 20:37:10 +09:30
grossmj
94c636ae61 Merge branch 'master' into 2.2 2020-07-09 20:23:31 +09:30
grossmj
f53b9a266e Try to fix "Recent project" selection not working. Ref #3007 2020-07-09 15:37:47 +09:30
grossmj
2476448032 Fix debug entries shown twice in console window and double error messages with remote GNS3VM. Fixes #3010 #3011
Note that the debug level is broken (has been for a long time apparently).
2020-07-07 21:06:56 +09:30
grossmj
a34cd742e3 Merge branch '2.2' 2020-07-06 22:20:05 +09:30
grossmj
39698196ac Fix deprecation warning. Ref #3009 2020-07-06 22:08:10 +09:30
grossmj
61432ced4f Fix tests on macOS. Ref #3009 2020-07-06 22:03:29 +09:30
grossmj
2ddd13c445 Add Snyk badges. 2020-06-27 18:27:04 +09:30
grossmj
af6c4c5b3e Merge branch 'master' into 2.2 2020-06-26 21:30:37 +09:30
grossmj
d4012294bf Run tests inside container 2020-06-26 20:50:48 +09:30
grossmj
4b04b0e855 Use xvfb to run tests 2020-06-26 20:41:08 +09:30
grossmj
1bec5019bf Install PyQt5 using pip 2020-06-26 19:42:42 +09:30
grossmj
ec6b876baa Explicitly install sip 2020-06-26 19:38:10 +09:30
grossmj
7cee0d01ab Install PyQt5 for tests and add GitHub Actions badge 2020-06-26 19:34:02 +09:30
Jeremy Grossmann
dd3314d06b Set up GitHub Actions for running tests 2020-06-26 19:27:22 +09:30
grossmj
9c58b26265 Remove tox, Travis CI and pep8.sh script
Update dependencies
2020-06-26 19:21:51 +09:30
Jeremy Grossmann
83c26f47da Merge pull request #3005 from GNS3/whitesource/configure
Configure WhiteSource for GitHub.com
2020-06-26 09:04:39 +08:00
whitesource-for-github-com[bot]
8ed2f55600 Add .whitesource configuration file 2020-06-26 01:01:54 +00:00
Jeremy Grossmann
b435317904 Merge pull request #3003 from GNS3/snyk-fix-8bf3e4840df4587cc42afb85b857d470
[Snyk] Security upgrade psutil from 5.6.6 to 5.6.7
2020-06-25 11:41:01 +08:00
snyk-bot
acb8aa8ca2 fix: requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-PSUTIL-483082
2020-06-24 13:10:33 +00:00
grossmj
c55d6b8a6f Fix sentry SDK is configured twice. 2020-06-24 12:40:06 +09:30
grossmj
a4039a254e Development on 2.2.11dev1 2020-06-18 19:06:00 +09:30
grossmj
85ed4b3026 Release v2.2.10 2020-06-18 12:29:33 +09:30
grossmj
5207a99692 New fix for multi-device selection/deselection not working as expected with right click. Fixes #2986 2020-06-09 13:50:31 +09:30
grossmj
d69527995d Revert "Fix Multi-device selection/deselection not working as expected with right click. Fixes #2986"
This reverts commit ddeb95cb0a.
2020-06-09 13:19:20 +09:30
grossmj
4b000ba2f7 Optimize snap-to-grid code for drawing items. Fixes #2997 2020-06-09 11:47:05 +09:30
grossmj
1b302b77a0 Move jsonschema 2.6.0 requirement in build repository.
https://github.com/GNS3/gns3-server/issues/1751
https://github.com/GNS3/gns3-gui/issues/2849

This is to avoid the following error:

```
ERROR: Double requirement given: jsonschema==2.6.0 (from -r gns3-gui\win-requirements.txt (line 4)) (already in jsonschema==3.2.0 (from -r gns3-gui\requirements.txt (line 1)), name='jsonschema')
```
2020-06-07 13:07:16 +09:30
grossmj
a5b5c404ec Only use jsonschema 2.6.0 on Windows and macOS.
https://github.com/GNS3/gns3-server/issues/1751
https://github.com/GNS3/gns3-gui/issues/2849
2020-06-07 12:55:22 +09:30
grossmj
6b97b0c6cd Disable default integrations for sentry sdk. 2020-06-06 15:37:17 +09:30
grossmj
115dd43eee Development on 2.2.10dev1 2020-06-04 21:06:30 +09:30
grossmj
2530bf97a8 Release v2.2.9 2020-06-04 18:39:27 +09:30
grossmj
9892fd0654 Fix issue editing README.txt on Windows. 2020-06-04 18:12:50 +09:30
grossmj
c71ee73da8 Merge branch 'master' into 2.2 2020-06-04 12:22:11 +09:30
Jeremy Grossmann
0643fd516d Merge pull request #2993 from GNS3/replicate-network-connection-state
Support to activate/deactive network connection state replication in Qemu
2020-06-04 10:49:41 +08:00
grossmj
a25680f2ce Fix GUI doesn't detect another GUI on macOS. Fixes #2994 2020-06-03 20:38:55 +09:30
grossmj
58bd5be920 Support to activate/deactive network connection state replication in Qemu. 2020-06-02 18:45:22 +09:30
Jeremy Grossmann
d95633ba2c Merge pull request #2989 from GNS3/reset-mac-addresses
Option to reset all MAC addresses when exporting or duplicating a project.
2020-05-27 10:53:22 +08:00
grossmj
dfea6d1723 Option to reset or not all MAC addresses when exporting or duplicating a project. 2020-05-27 12:14:47 +09:30
grossmj
ddeb95cb0a Fix Multi-device selection/deselection not working as expected with right click. Fixes #2986 2020-05-26 16:11:53 +09:30
grossmj
5f7ff0d70d Generate MainWindow Ui file. 2020-05-26 13:01:04 +09:30
Jeremy Grossmann
a00e039cec Merge pull request #2875 from fatoms/master
Proposed fix for "Edit readme" is missing in GNS3 GUI. #2854
2020-05-26 11:30:19 +08:00
Dominic
a24e9adef1 Merge branch 'Edit_Readme' 2020-05-21 20:03:03 +02:00
Dominic Harford
ee5f8e8edd Resolve conflict with GNS3 repo 2020-05-21 19:08:29 +02:00
grossmj
f5470130f5 Fix issues with crash reporting & bump version to 2.2.9dev2. Ref https://github.com/GNS3/gns3-server/issues/1758 2020-05-21 18:19:19 +09:30
grossmj
1ff405885e Merge branch 'master' into 2.2 2020-05-20 17:25:37 +09:30
Dominic
9fb42ead9f Revert "Updated GUI pyqt files from Tab Order 'fixes' in "Tab Order in Preferences and Project Dialog #2872""
This reverts commit 0d2f91709c.
2020-05-19 18:31:02 +02:00
grossmj
2ea1946c0f Replace Raven by Sentry SDK. Fixes https://github.com/GNS3/gns3-server/issues/1758 2020-05-19 15:48:53 +09:30
grossmj
963e054918 Fix online help menu URL. Fixes #2984 2020-05-08 12:42:06 +09:30
grossmj
0f5f6ab645 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
2020-05-08 12:34:58 +09:30
grossmj
8a905b5c39 Development on 2.2.9dev1 2020-05-07 23:10:20 +09:30
grossmj
e917193f06 Merge branch '2.2' 2020-05-07 23:09:04 +09:30
grossmj
16846ce49c Release v2.2.8 2020-05-07 18:10:57 +09:30
grossmj
624a670ae7 Make sure "port" is defined. 2020-05-07 17:51:32 +09:30
grossmj
406326ccd8 Merge remote-tracking branch 'origin/master' 2020-05-06 11:57:25 +09:30
grossmj
24bc15fb73 Default port set to 80 for server running in the GNS3 VM. Fixes #1737 2020-05-05 12:40:50 +09:30
grossmj
348d8b9438 Make the Web UI the default page. Ref https://github.com/GNS3/gns3-server/issues/1737 2020-04-30 17:27:06 +09:30
grossmj
6787982408 Fix "export portable project forgets contents of README". Fixes #1724 2020-04-30 16:43:00 +09:30
Jeremy Grossmann
c2384917fa Update README. Ref https://github.com/GNS3/gns3-server/issues/1719 2020-04-29 15:01:45 +09:30
grossmj
b80178d0cf Activate unified title and toolbar on MacOS. Fixes #2968 2020-04-29 13:08:51 +09:30
grossmj
e6084ed834 Confirmation dialog for "console connect to all nodes". Fixes #2971 2020-04-28 15:04:39 +09:30
grossmj
ba924cd0d9 Add "Resume all suspended links". Fixes #2858 2020-04-28 14:00:26 +09:30
grossmj
0c3d43346f Revert "Change default path for SecureCRT. Fixes #2896"
This reverts commit 0c4367d77e.
2020-04-28 13:19:38 +09:30
grossmj
fcf6ef3027 Remove @property from ConfigurationDialog(). Fixes #2819 #2965 2020-04-28 11:57:54 +09:30
grossmj
e0f87e573d 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
2020-04-27 12:54:17 +09:30
grossmj
3ec068f0cb Use Environmental Markers to force jsonschema version 2.6.0 on Windows/macOS. Ref https://github.com/GNS3/gns3-gui/issues/2849 2020-04-27 12:43:07 +09:30
grossmj
37f1fcf6f7 Remove preferences dialog geometry restoration. Fixes #2807 2020-04-27 11:55:01 +09:30
grossmj
c51dd1605d Merge branch '2.3'
# Conflicts:
#	gns3/version.py
2020-04-13 11:57:04 +09:30
grossmj
4ebf3b4e1c Development on 2.2.8dev1 2020-04-08 01:26:42 +09:30
grossmj
b1ec9d535c Release v2.2.7 2020-04-08 00:03:13 +09:30
grossmj
7fc9087cf0 Fix unable to configure custom adapters for Qemu VMs. Fixes #2961 2020-04-07 15:47:53 +09:30
grossmj
4972d460d2 Fix tests. 2020-04-06 21:09:47 +09:30
grossmj
c388836be7 Fix VNC console template doesn't extract %i (Project UUID). Fixes #2960 2020-04-06 18:34:37 +09:30
grossmj
18ae4a6ce9 Fix contextual menu issues. Ref #2955 2020-03-30 21:37:52 -07:00
grossmj
3020e1fc9f Downgrade to PyQt 5.12.3 Ref #2955 #2952 2020-03-29 18:18:53 +10:30
grossmj
fe2f8424db Downgrade to PyQt 5.13.2 Ref #2955 #2952 2020-03-29 14:46:34 +10:30
grossmj
a744f65199 Merge branch 'master' into 2.3
# Conflicts:
#	gns3/version.py
2020-03-28 13:44:08 +10:30
grossmj
d27578f0fc Release v2.2.6 2020-03-26 12:37:59 +10:30
grossmj
b01c11f19b Prevent locked drawings to be deleted. Fixes https://github.com/GNS3/gns3-gui/issues/2948 2020-03-16 16:30:09 +10:30
grossmj
fb269da4d3 Fix issues with empty project variables. Fixes https://github.com/GNS3/gns3-gui/issues/2941 2020-03-14 17:22:44 +10:30
grossmj
ab15f96bb5 Upgrade psutil to version 5.6.6 due to CVE-2019-18874
https://github.com/advisories/GHSA-qfc5-mcwq-26q8
2020-03-14 15:47:12 +10:30
grossmj
5bb8b8e8bd Use existing README.txt if existing when exporting portable project. Fixes https://github.com/GNS3/gns3-server/issues/1724 2020-03-10 17:32:13 +10:30
grossmj
3f9632fae0 Allow creation of a diskless Qemu VMs. Fixes #2939 2020-03-10 17:04:07 +10:30
grossmj
b5f8195abb Re-enable "create new version" in appliance wizard. Fixes #2837 2020-03-03 13:11:01 +08:00
grossmj
73a293bd17 Fix unable to load project from project library. Fixes #2932 2020-03-03 09:34:45 +08:00
grossmj
0a1dfb99e9 Merge remote-tracking branch 'origin/2.2' into 2.2 2020-02-19 14:13:16 +08:00
grossmj
d352919264 Fix some permission denied errors when loading remote project. Ref #2871 Fixes #2901 2020-02-19 14:13:03 +08:00
Jeremy Grossmann
65f2a1e461 Merge pull request #2931 from inthought/2.2
Add 'Royal TS V5' to predefined console list
2020-02-18 13:29:46 +10:30
Travis Abram
71f289721b Add 'Royal TS V5' to predefined console list 2020-02-16 20:46:40 -08:00
grossmj
c28089d400 Disallow invalid grid sized. Fixes #2908 2020-02-10 16:59:17 +08:00
grossmj
64f009bf71 Check if hostname is blank. Fixes #2924 2020-01-25 18:21:02 +08:00
Jeremy Grossmann
edb2fd7fd9 Merge pull request #2925 from GNS3/qemu-changes
GUI changes to support recent versions of Qemu
2020-01-25 16:04:54 +07:00
grossmj
62e7ad8c8a Add nvme disk interface and fix scsi disk interface for Qemu VMs. 2020-01-25 16:22:34 +08:00
grossmj
caeb5d71c3 Add latest Qemu nic models. 2020-01-24 19:05:46 +08:00
grossmj
cfe96b2311 Upgrade Qt version to 5.14.1. Ref #2778 #2903 2020-01-24 17:47:01 +08:00
grossmj
8955b9ee29 Upgrade to PyQt 5.14.1. Ref #2778 2020-01-22 17:53:04 +08:00
grossmj
e727abf27a Change version to 2.3.0dev1 on 2.3 branch 2020-01-16 18:06:51 +08:00
grossmj
f209bf7644 Development on 2.2.6dev1 2020-01-10 00:32:10 +08:00
grossmj
5860dedc32 Release v2.2.5 2020-01-09 23:52:40 +08:00
grossmj
9e2df17a4e Add gns3-gui.xml and update Linux icons paths & permissions. Ref #2919 2020-01-09 23:49:44 +08:00
grossmj
a95761437a Development on 2.2.5dev1 2020-01-09 05:17:01 +08:00
grossmj
626510865f Update paths to icons for Linux 2020-01-09 04:19:21 +08:00
grossmj
2e248aa340 Release v2.2.4 2020-01-09 00:45:09 +08:00
grossmj
e306f73f01 Fix "Console to all nodes" doesn't open cloud objects with console configured. Fixes #2902 2020-01-08 00:35:57 +08:00
grossmj
0c4367d77e Change default path for SecureCRT. Fixes #2896 2019-12-26 06:13:23 +08:00
grossmj
6dda0ff787 Add icons in setup.py Ref #2898 2019-12-26 06:04:40 +08:00
grossmj
d7d4b84309 Add remote viewer as a VNC console for Linux. Fixes #2913 2019-12-26 04:13:02 +08:00
grossmj
7b57983699 Development on 2.2.4dev1 2019-11-12 16:43:21 +08:00
grossmj
bb89fe2275 Release v2.2.3 2019-11-12 15:29:54 +08:00
grossmj
7eb2a923b2 Fix issue when binding on 0.0.0.0. Fixes #2892 2019-11-11 17:49:09 +08:00
grossmj
58052e3cce Allow double click on cloud with configured console to open session. Fixes #2894 2019-11-11 14:31:35 +08:00
grossmj
e431104f6b Officially support Python 3.8. Ref https://github.com/GNS3/gns3-gui/issues/2895 2019-11-11 12:56:11 +08:00
grossmj
a4e9d6b8ce Set psutil to version 5.6.3 in requirements.txt 2019-11-08 10:44:17 +08:00
grossmj
f58f5c7b95 Developement version on 2.2.3dev1 2019-11-04 19:45:18 +08:00
grossmj
37faa39309 Release v2.2.2 2019-11-04 18:33:29 +08:00
grossmj
4d64598ed2 Fix KeyError: 'spice+agent'. Fixes #2890 2019-11-03 15:19:18 +08:00
grossmj
5132c4e172 Fix wrong log.error() call when exporting file. 2019-11-02 23:19:04 +08:00
grossmj
3c3fdd9ffd Revert "Explicitly cleanup the cache directory."
This reverts commit 8095fef228.
2019-11-02 15:47:24 +08:00
grossmj
efa50571c6 Fix "UnboundLocalError: local variable 'pywintypes' referenced before assignment" 2019-11-02 15:45:16 +08:00
grossmj
ca9b10fcca Fix version string. 2019-11-02 15:27:28 +08:00
grossmj
8660161b10 Fix GUI uses only telnet console. Fixes #2885 2019-11-02 02:23:13 +08:00
grossmj
50ebfb9c06 Fix missing sys module in sudo.py Fixes #2886 2019-11-02 02:06:33 +08:00
grossmj
26df59d6b6 Development on 2.2.2dev1 2019-11-01 18:43:36 +08:00
grossmj
b903e2ad73 Release v2.2.1 2019-11-01 17:53:20 +08:00
grossmj
b2fe7eb643 Check if console_type is None. 2019-11-01 17:47:26 +08:00
grossmj
8095fef228 Explicitly cleanup the cache directory. 2019-11-01 17:46:53 +08:00
grossmj
b8da5440f5 Get Windows interface from registry if cannot load win32com module. 2019-11-01 17:44:47 +08:00
grossmj
6cea094e4e Ignore OSError returned by psutil when bringing console to front. 2019-11-01 17:44:06 +08:00
grossmj
9ac46c9d50 Catch error if NPF or NPCAP service cannot be detected. Ref https://github.com/GNS3/gns3-server/issues/1670 2019-10-30 17:59:19 +08:00
Dominic
c8a8663ff0 Restore editReadme attribute which was removed in Change 'New export project wizard' ( ID c2472bcb22 ) 2019-10-18 17:11:28 +02:00
Dominic
d27e5c1795 Revert "Remove unused edit readme action. Fixes #2816"
This reverts commit 7cd0187f33.
2019-10-18 16:46:48 +02:00
Dominic
0d2f91709c Updated GUI pyqt files from Tab Order 'fixes' in "Tab Order in Preferences and Project Dialog #2872" 2019-10-18 16:45:39 +02:00
grossmj
6dc44d5108 Better handling for reading synchronous JSON response from server. Ref #2874 2019-10-17 12:45:40 +08:00
grossmj
9c6be0341b Fix tests. 2019-10-17 12:34:34 +08:00
grossmj
011a49e998 Fix JSONDecodeError when getting server version. Fixes #2874 2019-10-17 12:31:15 +08:00
grossmj
e18c2df5f5 Fix FileNotFoundError exceptions when launching SPICE or VNC clients. 2019-10-17 12:19:55 +08:00
grossmj
1794b8389f Fix UnboundLocalError local variable 'win32serviceutil' referenced before assignment 2019-10-17 12:12:46 +08:00
grossmj
0379c370eb Merge remote-tracking branch 'remotes/origin/master' into 2.2 2019-10-15 23:36:49 +08:00
Jeremy Grossmann
e03550a89b Merge pull request #2872 from fatoms/Tab_Order
Tab Order in Preferences and Project Dialog
2019-10-15 22:35:53 +07:00
Dominic
c6ea775e81 'Fix' tab order in preferences dialog so it follows the layout 2019-10-12 22:06:30 +02:00
Dominic
38233ba5e9 'Fix' tab order in edit project dialog so it follows the layout 2019-10-12 22:05:52 +02:00
grossmj
89c1272bc1 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 2019-10-09 17:02:30 +08:00
grossmj
8bbb46c599 Use 0.0.0.0 by default for server host. Fixes https://github.com/GNS3/gns3-server/issues/1663 2019-10-09 16:35:42 +08:00
grossmj
74fca3d736 Set default host to "localhost". Fixes https://github.com/GNS3/gns3-server/issues/1663 2019-10-08 18:28:11 +08:00
grossmj
7aeed7aa59 Catch IndexError when configuring port names. Fixes #2865 2019-10-08 16:15:36 +08:00
grossmj
aa15ace887 Bump version to 2.2.1dev1 2019-10-08 16:05:37 +08:00
grossmj
2d0a7b5f58 Release v2.2.0 2019-09-30 16:24:26 +08:00
grossmj
20a09b56c1 Merge branch 'master' into 2.2
# Conflicts:
#	gns3/version.py
2019-09-24 14:07:38 +08:00
grossmj
1938cdabae Bump version to 2.2.0dev18 2019-09-24 14:02:36 +08:00
grossmj
8d1bff782c Release v2.2.0rc5 2019-09-09 15:06:14 +07:00
grossmj
4e3eee2383 Adjust size for setup dialog and remove question about running the wizard again. Ref #2846 2019-09-07 23:46:02 +07:00
grossmj
da8aa0d2fd Release v2.2.0rc4 2019-08-30 15:23:32 +07:00
grossmj
5b4481c43a Fix issue when asking to run the setup wizard again. Ref #2846 2019-08-29 15:59:40 +07:00
grossmj
593cb8c1fd Remove warning about VirtualBox not supporting nested virtualization. Ref https://github.com/GNS3/gns3-server/issues/1610 2019-08-27 17:28:44 +07:00
grossmj
210cf63fe2 Ask user if they want to see the wizard again. Ref #2846 2019-08-27 16:15:50 +07:00
grossmj
3b178013c0 Bump version to 2.2.0dev17 2019-08-23 18:20:44 +07:00
grossmj
6e44d6b919 Merge branch 'master' into 2.2
# Conflicts:
#	gns3/version.py
2019-08-20 17:35:13 +07:00
grossmj
6b520b8036 Bump version to 2.2.0dev16 2019-08-20 17:33:48 +07:00
grossmj
803782b9d8 Release v2.2.0rc3 2019-08-11 19:14:56 -07:00
grossmj
d3d6ca3f2e Revert to jsonschema 2.6.0 due to packaging problem. 2019-08-11 19:11:41 -07:00
grossmj
f545c793f8 Release v2.2.0rc2 2019-08-10 12:04:19 -05:00
grossmj
47d6a4fef6 Release v2.2.0rc1 2019-08-10 11:42:13 -05:00
grossmj
8862b608cf Bump jsonschema to version 3.0.2 2019-08-09 13:47:58 -05:00
grossmj
76832ab83f Fix "Unable to change Remote Main Server IP". Fixes #2823 2019-08-02 17:23:31 -02:30
grossmj
fed245fd34 Fix "AttributeError: 'QGraphicsTextItem' object has no attribute 'locked'". Fixes #2814 2019-07-30 15:50:40 -02:30
grossmj
3e0f1affd0 Merge branch '2.2' 2019-07-12 12:07:33 +02:00
grossmj
2110c2805e Development on 2.2.0dev15 2019-07-11 17:34:56 +02:00
grossmj
46cfdd8314 Release v2.2.0b4 2019-07-11 16:58:35 +02:00
grossmj
f8f648c2b6 Fix issue preventing to open the QFileDialog in the correct directory. 2019-07-11 16:50:59 +02:00
grossmj
7cd0187f33 Remove unused edit readme action. Fixes #2816 2019-07-10 12:10:17 +02:00
grossmj
4d8f362f11 Remove deprecated Qemu parameter to run legacy ASA VMs. Fixes #2827 2019-07-10 11:33:04 +02:00
grossmj
469eaa4737 Upload images on remote controller. Fixes #2828 2019-07-10 11:23:29 +02:00
grossmj
c921224b30 Preferences dialog: send API request only if connected to controller 2019-07-05 18:07:48 +02:00
grossmj
61487b2e2f Fix AttributeError: 'QGraphicsTextItem' object has no attribute 'locked'. Fixes #2814 2019-06-24 15:29:07 +02:00
grossmj
9affca495e Fix KeyError: 'chassis' when converting old IOS templates. Fixes #2813 2019-06-24 15:14:20 +02:00
grossmj
9d8886a640 Development on 2.2.0dev14 2019-06-15 16:38:06 +02:00
grossmj
98cfec1b77 Release v2.2.0b3 2019-06-15 15:39:32 +02:00
grossmj
aed174953e Merge 2.1 into 2.2 branch. 2019-06-15 15:26:20 +02:00
grossmj
f0feea8262 Fix template migration issues from GUI to controller. Fixes https://github.com/GNS3/gns3-gui/issues/2803 2019-06-15 12:52:50 +02:00
grossmj
e2aeaf0a78 Development on 2.1.22dev1 2019-06-14 19:42:18 +02:00
grossmj
b92bb94875 Release v2.1.21 2019-06-14 10:47:08 +02:00
grossmj
c56db59353 Increase timeout from 2 to 5 seconds for synchronous check. Ref #2805 2019-06-10 22:20:40 +02:00
grossmj
a87c4e21d7 %guest-cid% variable implementation for Qemu VMs. Fixes https://github.com/GNS3/gns3-gui/issues/2804 2019-06-04 18:00:44 +02:00
grossmj
ed99a989d7 Fix "General Preferences dialog displays misleading information". Fixes #2801 2019-06-04 17:05:01 +02:00
grossmj
f9a4c9399a Increase timeout from 2 to 5 seconds for synchronous check. Ref #2805 2019-05-31 09:17:34 +02:00
grossmj
efb5c8ca9a Development on 2.2.0dev13 2019-05-29 17:52:38 +07:00
grossmj
7f08675121 Merging 2.2 into master 2019-05-24 15:27:07 +07:00
Jeremy Grossmann
a9a2a541c0 Update gns3-feature-request.md 2019-02-27 15:46:57 +07:00
Jeremy Grossmann
8998c07e0e Update gns3-bug-report.md 2019-02-27 15:46:28 +07:00
Jeremy Grossmann
ba01a89af1 Update gns3-feature-request.md 2019-02-27 15:43:07 +07:00
Jeremy Grossmann
eae07d62ad Update gns3-bug-report.md 2019-02-27 15:42:21 +07:00
Jeremy Grossmann
23903cf0c9 Delete feature_request.md 2019-02-27 15:36:33 +07:00
Jeremy Grossmann
4d908fd855 Delete bug_report.md 2019-02-27 15:36:18 +07:00
Jeremy Grossmann
bb0e67be4f Update issue templates 2019-02-27 15:32:18 +07:00
Jeremy Grossmann
0410c446fc Merge pull request #2466 from dhalperi/fix-typo
Fix a minor typo in the setup wizard
2018-04-05 13:19:38 +07:00
Daniel Halperin
18486e4772 Fix a minor typo in the setup wizard
Eveything -> Everything
2018-04-04 22:53:09 -07:00
103 changed files with 1850 additions and 612 deletions

View 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.

View File

@@ -0,0 +1,10 @@
---
name: GNS3 development
about: Any question or discussion regarding GNS3 development
title: ''
labels: ''
assignees: ''
---

View 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
View 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

View File

@@ -1,2 +0,0 @@
branch:
2.2

View File

@@ -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
View File

@@ -0,0 +1,13 @@
{
"scanSettings": {
"configMode": "AUTO",
"configExternalURL": "",
"projectToken" : ""
},
"checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure"
},
"issueSettings": {
"minSeverityLevel": "LOW"
}
}

151
CHANGELOG
View File

@@ -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

View File

@@ -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

View File

@@ -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 *

View File

@@ -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
-------------

View File

@@ -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

View File

@@ -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:

View File

@@ -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):

View File

@@ -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"])

View File

@@ -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

View File

@@ -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__':

View File

@@ -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)

View File

@@ -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",

View File

@@ -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):

View File

@@ -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_()

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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):

View File

@@ -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:

View File

@@ -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):
"""

View File

@@ -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:

View File

@@ -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:

View File

@@ -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"))

View File

@@ -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)

View File

@@ -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):
"""

View File

@@ -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)

View File

@@ -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"

View File

@@ -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()

View File

@@ -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"],

View File

@@ -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,

View File

@@ -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 @@
&lt;li&gt;%vm-id% =VM ID&lt;/li&gt;
&lt;li&gt;%project-id% = project ID&lt;/li&gt;
&lt;li&gt;%project-path% = project path&lt;/li&gt;
&lt;li&gt;%console-port% = console port number&lt;/li&gt;
&lt;li&gt;%guest-cid% = unique ID from 3 to 65535&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;</string>
</property>

View File

@@ -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"))

View File

@@ -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

View File

@@ -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

View File

@@ -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):
"""

View File

@@ -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):

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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]

View File

@@ -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": {

View File

@@ -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,

View File

@@ -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))

View File

@@ -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.

View File

@@ -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)

View File

@@ -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()

View File

@@ -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>

View File

@@ -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>&amp;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>&amp;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>&amp;Reset MAC addresses</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="uiProjectReadmeWizardPage">

View File

@@ -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>"))

View File

@@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Command line replacements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;%h = console IP or hostname&lt;/li&gt;
&lt;li&gt;%p = console port&lt;/li&gt;
&lt;li&gt;%P = VNC display&lt;/li&gt;
&lt;li&gt;%s = path of the serial connection&lt;/li&gt;
&lt;li&gt;%d = title of the console&lt;/li&gt;
&lt;li&gt;%i = project UUID&lt;/li&gt;
&lt;li&gt;%c = server URL&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Command line replacements:&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%h = console IP or hostname&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%p = console port&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%d = title of the console&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%i = project UUID&lt;/li&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%n = node UUID&lt;/li&gt;&lt;/ul&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%c = server URL&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
@@ -508,17 +501,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Command line replacements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;%h = console IP or hostname&lt;/li&gt;
&lt;li&gt;%p = console port&lt;/li&gt;
&lt;li&gt;%P = VNC display&lt;/li&gt;
&lt;li&gt;%s = path of the serial connection&lt;/li&gt;
&lt;li&gt;%d = title of the console&lt;/li&gt;
&lt;li&gt;%i = project UUID&lt;/li&gt;
&lt;li&gt;%c = server URL&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Command line replacements:&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%h = console IP or hostname&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%p = console port&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%P = VNC display&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%d = title of the console&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%i = project UUID&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%n = node UUID&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
@@ -587,17 +570,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Command line replacements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;%h = console IP or hostname&lt;/li&gt;
&lt;li&gt;%p = console port&lt;/li&gt;
&lt;li&gt;%P = VNC display&lt;/li&gt;
&lt;li&gt;%s = path of the serial connection&lt;/li&gt;
&lt;li&gt;%d = title of the console&lt;/li&gt;
&lt;li&gt;%i = project UUID&lt;/li&gt;
&lt;li&gt;%c = server URL&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Command line replacements:&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%h = console IP or hostname&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%p = console port&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%d = title of the console&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%i = project UUID&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;%n = node UUID&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View File

@@ -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"))

View File

@@ -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>

View File

@@ -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:"))

View File

@@ -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">

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>&amp;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>

View File

@@ -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"))

View File

@@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Loading.. Please wait.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Please provide the missing values for the project variables:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "2.2.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__:

View File

@@ -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

View File

@@ -1,3 +1,2 @@
-rrequirements.txt
#PyQt5==5.7

View File

@@ -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

View 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;

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -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

View File

@@ -1,19 +0,0 @@
#!/bin/bash
echo '
_______ ________ _______ ______
| \ | \| \ / \
| $$$$$$$\| $$$$$$$$| $$$$$$$\| $$$$$$\
| $$__/ $$| $$__ | $$__/ $$| $$__/ $$
| $$ $$| $$ \ | $$ $$ >$$ $$
| $$$$$$$ | $$$$$ | $$$$$$$ | $$$$$$
| $$ | $$_____ | $$ | $$__/ $$
| $$ | $$ \| $$ \$$ $$
\$$ \$$$$$$$$ \$$ \$$$$$$
'
find . -name '*.py' -exec autopep8 --in-place -v --aggressive --aggressive \{\} \;
echo "It's clean"

View File

@@ -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",
],
)

View File

@@ -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):

View File

@@ -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)

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