Migration to View/Model design (#195)

Squashed commit of the following:

commit 7929b8f0f6ec21ae0fb90203205e4224ce5bc1b7
Author: donoban <donoban@riseup.net>
Date:   Tue Jul 28 17:21:11 2020 +0200

    Try to fix sort

commit 5e4598e1d6aba0e2208e7bca761be83931457a26
Author: donoban <donoban@riseup.net>
Date:   Mon Jul 27 04:06:37 2020 +0200

    Fix import

commit 60f53e7ef0e35fde9143835b18015db570739544
Author: donoban <donoban@riseup.net>
Date:   Mon Jul 27 04:03:17 2020 +0200

    Fix 218 test

commit e430e394774bba4ca306f2fb0b8d55e10f9e2bc2
Author: donoban <donoban@riseup.net>
Date:   Mon Jul 27 04:01:56 2020 +0200

    Avoid error if dvm is None

commit 679880ff4f3d7117784e90d6cb53538b7fba4f0d
Author: donoban <donoban@riseup.net>
Date:   Mon Jul 27 03:58:59 2020 +0200

    Fix sorting again

commit f84edcdc02bf311de9d60274ae7fba90566a460e
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 26 03:30:14 2020 +0200

    Yes, it's needed

commit 5d00c91db45f99c78d7f15f77dd65a51b286fc77
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 25 23:08:38 2020 +0200

    Fix pylint error

commit 88a54dc3d2927a04c44e8d4c9548e123852b3e39
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 25 18:56:40 2020 +0200

    Style change

commit 42ae96c45b37e03823e782c67b0995f588c7e0bd
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 25 18:56:00 2020 +0200

    Fix sorting errores

commit daa872297b2082237860a66bb01e2c71fda3e55d
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 25 18:54:21 2020 +0200

    Fixed sort test errors

commit 73ad25ed9e77d25bfc6a4159b8ef9a6f24e3294a
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 25 00:25:37 2020 +0200

    Var rename

commit 825d8ad6f7d3a9a7bb11252ebe5a5402851d56a9
Author: donoban <donoban@riseup.net>
Date:   Fri Jul 24 23:37:04 2020 +0200

    Restored Cleanup

commit 09f183946d23fb4a92e428395834ef3ad4473ffb
Author: donoban <donoban@riseup.net>
Date:   Fri Jul 24 23:35:20 2020 +0200

    Removed workaround, now works properly without clear reason

commit 2f5bde0484e3eb0a3128e8b98f61a5311e0f529e
Author: donoban <donoban@riseup.net>
Date:   Fri Jul 24 23:29:20 2020 +0200

    Multiple tests fixes

commit e21f9ab7416e728d1ef0409fb9ca880e23d9240c
Author: donoban <donoban@riseup.net>
Date:   Fri Jul 24 23:28:32 2020 +0200

    Save dvm name instead VM object

commit 46e2fe1cf68708fa41df59661d11691ddb331984
Author: donoban <donoban@riseup.net>
Date:   Fri Jul 24 01:11:15 2020 +0200

    Deleted wrong mapToSource()

commit b155e051beb4ffcf0c1d48c5c9e24576c7db2e94
Author: donoban <donoban@riseup.net>
Date:   Fri Jul 24 01:10:43 2020 +0200

    Fix get 'Is DVM Template' widget

commit 61d7a6dc05f39055cbfd473c2a5cd638194aa132
Author: donoban <donoban@riseup.net>
Date:   Wed Jul 22 12:17:10 2020 +0200

    fix set_keyboar_layout test fail

commit 1dba52eb14b1d9c190d3a8c7bedf026ba242ac7d
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 19 00:05:53 2020 +0200

    More test fixes

commit 665a1453eca121a3c60975fe4c9e08b05e4831fe
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 12 23:39:07 2020 +0200

    Fixed power state checking

commit 6733fb1cd80cbc0917a5d1e42680d5424364649d
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 12 17:44:01 2020 +0200

    Return vm object instead name on select_vm functions

commit 80f3b3f7498c8c98517ae77053861861aaecfba7
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 12 17:43:27 2020 +0200

    Removed wrong calls to text()

commit 32bbb864bf96ad3e37b6aaddc25817a5ad38a0cc
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 12 17:42:45 2020 +0200

    Removed implicity calls to sortItems()

commit bc288b616b80a5fabe179954eb18953ee59c18bc
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 12 17:03:22 2020 +0200

    setCurrentItem() -> setCurrentIndex()

commit 10bac8d300aa5d428080a0dc6a689e88230d93d2
Author: donoban <donoban@riseup.net>
Date:   Sun Jul 12 16:43:41 2020 +0200

    get_table_vminfo renamed to get_table_vm

commit cee7b0af871183e4917fdb535bbcc83facd64446
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 11 23:46:41 2020 +0200

    First version fixing tests

commit 42d566f032941679608669d1ccc28a4523715b8a
Author: donoban <donoban@riseup.net>
Date:   Sat Jul 11 23:38:33 2020 +0200

    Fixing tests

commit ccd7c162ef2a3f882c7d683d5b9c97db11829ac6
Merge: 24e5d58 8a74e43
Author: donoban <donoban@riseup.net>
Date:   Mon Jun 8 22:16:34 2020 +0200

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

    # Conflicts:
    #	qubesmanager/qube_manager.py

commit 24e5d58c98981b3635b3c6dfa9202cac3e3455d8
Author: donoban <donoban@riseup.net>
Date:   Sun Jun 7 19:03:09 2020 +0200

    Added workaround for dom0 sorting

commit db2781a6392ff32c2d26053999819e08cb0e0ca0
Author: donoban <donoban@riseup.net>
Date:   Sun Jun 7 18:57:28 2020 +0200

    Fixed Sorting Case Insensivity

commit 93330ea6a45598a212811251843d32682a20016a
Author: donoban <donoban@riseup.net>
Date:   Sun Jun 7 18:51:39 2020 +0200

    Added "default" to netvm and default dispvm

commit a40156c4f4b08a201fb877fc92f547c5138a7e32
Author: donoban <donoban@riseup.net>
Date:   Sun Jun 7 18:18:03 2020 +0200

    Fixed QSettings saving

commit a1d96e78778c84fe077b62196c8ed561978de9bc
Author: donoban <donoban@riseup.net>
Date:   Wed Jun 3 00:23:50 2020 +0200

    Added 'defaultValue' on settings load

commit a0a7ee812298e6361a2ee585049c96303d1bbda7
Author: donoban <donoban@riseup.net>
Date:   Wed Jun 3 00:10:31 2020 +0200

    Init view menu out of load_manager_settings

commit 6f9a60004282e85c32727baa4b49c0a4d080f74e
Author: donoban <donoban@riseup.net>
Date:   Tue Jun 2 23:19:09 2020 +0200

    "Size" renamed to "Disk Usage"

commit 5fbda06b370de790e31a9a983891a69ab8d031de
Author: donoban <donoban@riseup.net>
Date:   Tue Jun 2 01:34:56 2020 +0200

    Replaced unneded elif's with if's

commit 5516bca8616d2e1ab99d4c40b11ff7b69cdbea48
Author: donoban <donoban@riseup.net>
Date:   Tue Jun 2 01:30:46 2020 +0200

    Use "Yes"/"" for bool properties

commit 1e5429e7ef9240570a5f31eae3a49a8380f97ee4
Author: donoban <donoban@riseup.net>
Date:   Tue Jun 2 01:12:46 2020 +0200

    Restored exactly old icon size

commit 270c82547365fbecddab1a21afef637da3eb2aa6
Author: donoban <donoban@riseup.net>
Date:   Sun May 31 12:52:03 2020 +0200

    AdminVM and DispVM icon workaround

commit cfb8a87b6dbdfda6f1652a6a4c82299e60b8c158
Author: donoban <donoban@riseup.net>
Date:   Sun May 31 12:51:44 2020 +0200

    Icon size adjusted to 128/4

commit 173dc9413c6ae81e851026538beb101e385e5974
Author: donoban <donoban@riseup.net>
Date:   Sat May 30 00:56:40 2020 +0200

    Add italic and gray color for differentiate templates and standalone/dom0

commit 2062f9308833241994b3ba87964f4f871b115e2a
Author: donoban <donoban@riseup.net>
Date:   Thu May 28 00:21:58 2020 +0200

    Fixig Marek comments

commit 348485e960d18c5d7fd1746448251374480d270b
Author: donoban <donoban@riseup.net>
Date:   Thu May 28 00:03:44 2020 +0200

    More readable

commit dc823a3923ab6c110fa8c51d4d66e501bc3e9f97
Author: donoban <donoban@riseup.net>
Date:   Thu May 28 00:01:02 2020 +0200

    Needed for pylint proplerly import PyQt5 modules on fedora 32

commit 4478b284ce6f4521d1bddd5f6dc3d564c6c02408
Author: donoban <donoban@riseup.net>
Date:   Tue May 19 01:11:05 2020 +0200

    Removed unused unued vars

commit 450f0e32525792d48f121edfb890f0f24e6f6c36
Author: donoban <donoban@riseup.net>
Date:   Fri May 8 00:26:59 2020 +0200

    Fix wrong var names

commit c1bd9577e21e79a708870bbd22ff557ec0f48547
Author: donoban <donoban@riseup.net>
Date:   Fri May 8 00:24:31 2020 +0200

    Fixed params order to VmSettingsWindow()

commit 6d50d033d5866aa9cd0913822189bf235c8c7bdd
Author: donoban <donoban@riseup.net>
Date:   Fri May 8 00:20:06 2020 +0200

    Modeless settings windows

commit ef3ac6a962b09e34602a624b3e8fdbdaf4cf8a42
Author: donoban <donoban@riseup.net>
Date:   Thu May 7 23:51:30 2020 +0200

    Fix some vm/vm_info confusion

commit 09392f99dc1ecd2e96e756884dd75c22090aa127
Author: donoban <donoban@riseup.net>
Date:   Wed Apr 29 10:26:58 2020 +0200

    removed trailing whitespace

commit 9e35ddf882053b25e2ab1d6cce6393cb77b95e79
Author: donoban <donoban@riseup.net>
Date:   Wed Apr 29 00:50:27 2020 +0200

    columns_indices redudancy fixed and menu_view auto generation

commit 8d96ef46d7f1eb0f26cf8d92203a22890c6165c0
Author: donoban <donoban@riseup.net>
Date:   Sat Apr 25 00:29:53 2020 +0200

    Use col_name instead col number, improves readiblity

commit 1cae3cab93d31592819941eee16ed239805d9cc8
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 24 00:52:12 2020 +0200

    Add QubesNoSuchProperyError

commit aed771d4eb3b6b16652ec1ae27abb0761ebe2fa9
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 24 00:45:59 2020 +0200

    Added missing virt_mode

commit 580749b83376204880da7be93d6325c6cdc0c239
Merge: 70878dc b058db4
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 24 00:16:48 2020 +0200

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

commit 70878dc647cf34f716cfe0f4753f41cd1487a45e
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 24 00:16:31 2020 +0200

    Let's try travis

commit 5f65477abdb304413c3d3800d6e109c51275e13a
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 24 00:11:37 2020 +0200

    Fix ProgressDialog not being properly drawn

commit b577cb91d908e065ba43e68c613ff0eca449bbd7
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 23:44:27 2020 +0200

    pylint fixes and wrong 'outdated'

commit 2a55c5d65b0cb3f7bb9d4adb10f5e41f662a85fe
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 23:35:47 2020 +0200

    Restored menubar and toolbar context menu

commit ac7086011328f1ef8f94a838425f8fe872b4fd20
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 23:28:02 2020 +0200

    restored logs

commit a0b2b7be3cb6bf2693644289a9ae0452ce330cb5
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 23:16:03 2020 +0200

    Removed unused attributes

commit cb514949f55e50925e1eabb19c8303e914c20d17
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 23:08:00 2020 +0200

    Part of last commit...

commit 7f0c42fb9a9622d33f5281f8134c7f669a1ae7a4
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 23:07:20 2020 +0200

    Save sort settings on closeEvent

commit 8dcfc3c9a9467e512b6c58e8b0a53c727bce7e89
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 12 13:02:37 2020 +0200

    Pylint fixes

commit 8e5f9ff1d4e33d1a8d97842a696ba45ec40c7103
Author: donoban <donoban@riseup.net>
Date:   Mon Apr 6 23:35:15 2020 +0200

    State converted to dict making pylint happier

commit 233ec124736d09f0a64f65ce2d7e19383942e73a
Author: donoban <donoban@riseup.net>
Date:   Mon Apr 6 00:25:34 2020 +0200

    Pylint fixes

commit 37790f01e3755dccbb6da24b3170320fcf2b2fe9
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 5 23:47:17 2020 +0200

    pylint

commit 7dbe393047a00e4d5914368f8dad3c23d5a69586
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 5 23:41:12 2020 +0200

    pylint fixes

commit f79f096ce3307167256308ce44ef8d3cf5f9a824
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 5 23:37:03 2020 +0200

    fixed wrong info_by_id refrences

commit dbf17bde761a6efc03ff29b87e65623a214a44d2
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 5 17:46:31 2020 +0200

    Added QubesCache

    QubesTableModel and main app should operate directly to the cache

commit 42d124520f7910f2ba0e77531fa6f469dd1932e5
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 5 13:50:00 2020 +0200

    Fixing multiple pylint warnings

commit c708b4293035d8d296680b8a7513a066eb475f2b
Author: donoban <donoban@riseup.net>
Date:   Sun Apr 5 12:59:43 2020 +0200

    Added action_open_console setEnabled

commit de1499464d47f145d10250619f83f8dfec2861b5
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 3 00:25:07 2020 +0200

    Forgot context_menu.actions()

commit d24903b2462e416c5148036ed1184b90b02bd8b0
Author: donoban <donoban@riseup.net>
Date:   Fri Apr 3 00:03:16 2020 +0200

    Elegant alternative for _enable_all()

commit a0603870a3bbb78128d4cb23e259d3d00449b94c
Author: donoban <donoban@riseup.net>
Date:   Thu Apr 2 00:22:46 2020 +0200

    Fixed outdate

commit 36e4b310080738bd9d8f9c92a16ad012735eb01a
Author: donoban <donoban@riseup.net>
Date:   Wed Apr 1 11:36:28 2020 +0200

    Removed table_widgets.py dependency

commit 72e679e2d17e663b64213c23530e14cd6f6f843c
Author: donoban <donoban@riseup.net>
Date:   Wed Apr 1 00:55:10 2020 +0200

    Fixed pylint warnings

commit 8e118be165d0ec77d9415cb90ef32c8b73c57612
Author: donoban <donoban@riseup.net>
Date:   Wed Apr 1 00:26:14 2020 +0200

    Added get_selected_vms() and UserRole + 1

commit fd12a95280c5296a92a04bf3dbdb8487c8190729
Author: donoban <donoban@riseup.net>
Date:   Tue Mar 31 01:10:51 2020 +0200

    fix some pylint warnings

commit 09dfe83d89a14ba4a3745ec86ee59ad89ac153ae
Author: donoban <donoban@riseup.net>
Date:   Tue Mar 31 00:34:51 2020 +0200

    Removed unneded margins

commit f0c81bf5a93f51c95b6afb01744f14a387dd4610
Merge: 00876bc f1ad829
Author: donoban <donoban@riseup.net>
Date:   Tue Mar 31 00:33:53 2020 +0200

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

commit 00876bcbfc7b70cd51848938ceb9f8f969848698
Author: donoban <donoban@riseup.net>
Date:   Mon Mar 30 23:31:18 2020 +0200

    Alternative pyqt imports

    After reading official pyqt doc this seems the standard way
    (Continue previous commit)

commit 6cf09d319021ab7b6491347c579f2911fbcb4e05
Author: donoban <donoban@riseup.net>
Date:   Mon Mar 30 23:29:21 2020 +0200

    Alternative pyqt imports

    After reading official pyqt doc this seems the standard way

commit 410dbaefca27fe3be85fc306db6afc04292f8f6f
Author: donoban <donoban@riseup.net>
Date:   Mon Mar 30 00:12:09 2020 +0200

    Restored sorting and filtering using QSortFilterProxyModel()

commit 0b7fd6e7301009ebab702933b1114254ee5b6a93
Author: donoban <donoban@riseup.net>
Date:   Tue Mar 24 12:46:18 2020 +0100

    Added QSortFilterProyModel

    indexes need proxy.mapToSource(index)

    model.layoutChanged.emit(), replaced by proxy.invalidate()

commit 97440e8a616b84e49e446dc11576a987dae33da2
Author: donoban <donoban@riseup.net>
Date:   Tue Mar 24 12:34:35 2020 +0100

    Removed unneded calls to setContentsMargins

commit 1ad2aaac2cdfa4c7fc4323a3cf82220340de462f
Author: donoban <donoban@riseup.net>
Date:   Sun Mar 22 22:56:33 2020 +0100

    fix removevm with multiselection

commit 19be1da69f3f43b1a9a4d9c5c561d4a6c9004d0c
Author: donoban <donoban@riseup.net>
Date:   Sun Mar 22 22:34:52 2020 +0100

    Restored context menu

commit f43394a446ecb23b36fda029f62f2a0633ee01b6
Author: donoban <donoban@riseup.net>
Date:   Sun Mar 22 00:08:43 2020 +0100

    Deleted unedeed updates after change of settings

commit c98ba627579871b302563f42678ac412e9ccdd48
Merge: 103c572 cf3f102
Author: donoban <donoban@riseup.net>
Date:   Sat Mar 21 23:45:46 2020 +0100

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

commit 103c5721d3f9f9c1ef6e922aeba053a2eb69b332
Merge: 2756864 da2826d
Author: donoban <donoban@riseup.net>
Date:   Sat Feb 29 16:40:22 2020 +0100

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

commit 2756864bd04b1b16cf819fb4e726fff40189c8f3
Merge: 2e2a14b 8902727
Author: donoban <donoban@riseup.net>
Date:   Thu Jan 23 23:43:32 2020 +0100

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

commit 2e2a14bdcaf8f6e7ce2f8fcec944109f18aad27f
Author: donoban <donoban@riseup.net>
Date:   Wed Jan 8 16:41:30 2020 +0100

    Removed fill_table :)

commit 9f3f61a5d0c6c11e9eca81bbedcfe7affa187148
Author: donoban <donoban@riseup.net>
Date:   Tue Dec 31 17:29:39 2019 +0100

    When Template changes status, all AppVMs should update too

commit b970a703ab5bb559b6627c637466558651403f74
Author: donoban <donoban@riseup.net>
Date:   Fri Dec 27 17:59:05 2019 +0100

    Improved multi row system

commit 2f3fc988707252c2079998343de3c508ac4d9a74
Merge: 1f21da6 cca5d7d
Author: donoban <donoban@riseup.net>
Date:   Fri Dec 27 17:25:15 2019 +0100

    Merge remote-tracking branch 'upstream/master'

commit 1f21da6d48d1bdddfc75c3ab47d28e92ed221a6d
Author: donoban <donoban@riseup.net>
Date:   Mon Sep 23 21:41:39 2019 +0200

    Restored 'selection changed' with multiple row support

    It reacts to selection changes but it is missing real functionally yet.

commit bdf16015cd2a4ba894ae1d5c2c495403bb78be0d
Author: donoban <donoban@riseup.net>
Date:   Wed Sep 18 07:27:47 2019 +0200

    Restored add/remove/change events handling

commit 2f9b21f07241b43655fb579dd4130a9c72d3db42
Author: donoban <donoban@riseup.net>
Date:   Wed Sep 18 07:00:49 2019 +0200

    Added StateIconDelegate and StateInfo

    Used for paint different icons on same cell with custom tooltips.

commit ccfa5453b2fe1ca5948406126a0f32a2caf4bde0
Author: donoban <donoban@riseup.net>
Date:   Wed Sep 18 06:35:12 2019 +0200

    Removed Default and Minium horizonal header section size

    It affects resizeColumnsToContents()

commit 628073e9522af5bff83224491645addbdcfb7418
Author: donoban <donoban@riseup.net>
Date:   Sun Sep 15 10:45:36 2019 +0200

    Uncompatible with TableView

commit 52ddd56bf293ca704205824f9c3b0f25f2c8d4c4
Merge: 0a87cf9 1ced452
Author: donoban <donoban@riseup.net>
Date:   Sun Sep 15 10:43:13 2019 +0200

    Merge branch 'master' of https://github.com/QubesOS/qubes-manager

commit 0a87cf963388bc9a33d241146e642a8ce9518ddb
Author: donoban <donoban@riseup.net>
Date:   Mon Sep 2 21:55:21 2019 +0200

    Restored precises updates
    https://github.com/QubesOS/qubes-manager/pull/195#issuecomment-525795486

commit 030bf13fab31cd57c5891d6ff692faf57c500f0a
Author: donoban <donoban@riseup.net>
Date:   Sun Aug 25 18:33:11 2019 +0200

    New and dirty first Model/View version

commit 981ee9c1c3ccd6af4fe8b2745b7b5ddb29ecc0c4
Author: donoban <donoban@riseup.net>
Date:   Sun Aug 25 18:32:28 2019 +0200

    QtableWidget > QTableView

commit 41beaed24b69e7e9dc9223fa100605b0fd5bb40e
Author: donoban <donoban@riseup.net>
Date:   Sun Aug 25 18:31:59 2019 +0200

    Removed table_widgets
This commit is contained in:
donoban 2020-07-30 02:40:40 +02:00 committed by Marek Marczykowski-Górecki
parent e4be77b75b
commit bb1995af2e
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
8 changed files with 980 additions and 1445 deletions

View File

@ -14,6 +14,7 @@ ignore=tests,
ui_restoredlg.py,
ui_settingsdlg.py,
resources_rc.py
extension-pkg-whitelist=PyQt5
[MESSAGES CONTROL]
# abstract-class-little-used: see http://www.logilab.org/ticket/111138

View File

@ -33,7 +33,6 @@ SOURCES = \
qubesmanager/resources_rc.py \
qubesmanager/restore.py \
qubesmanager/settings.py \
qubesmanager/table_widgets.py \
qubesmanager/template_manager.py \
qubesmanager/ui_about.py \
qubesmanager/ui_backupdlg.py \

File diff suppressed because it is too large Load Diff

View File

@ -1,510 +0,0 @@
#!/usr/bin/python3
# -*- coding: utf8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014 Marek Marczykowski-Górecki
# <marmarek@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program; if not, see <http://www.gnu.org/licenses/>.
import datetime
from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error
# pylint: disable=too-few-public-methods
power_order = QtCore.Qt.DescendingOrder
update_order = QtCore.Qt.AscendingOrder
row_height = 30
class VmIconWidget(QtWidgets.QWidget):
def __init__(self, icon_path, enabled=True, size_multiplier=0.7,
tooltip=None, parent=None,
icon_sz=(32, 32)): # pylint: disable=unused-argument
super(VmIconWidget, self).__init__(parent)
self.enabled = enabled
self.size_multiplier = size_multiplier
self.label_icon = QtWidgets.QLabel()
self.set_icon(icon_path)
if tooltip is not None:
self.label_icon.setToolTip(tooltip)
layout = QtWidgets.QHBoxLayout()
layout.addWidget(self.label_icon)
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
def setToolTip(self, tooltip): # pylint: disable=invalid-name
if tooltip is not None:
self.label_icon.setToolTip(tooltip)
else:
self.label_icon.setToolTip('')
def set_icon(self, icon_path):
if icon_path[0] in ':/':
icon = QtGui.QIcon(icon_path)
else:
icon = QtGui.QIcon.fromTheme(icon_path)
icon_sz = QtCore.QSize(row_height * self.size_multiplier,
row_height * self.size_multiplier)
icon_pixmap = icon.pixmap(
icon_sz,
QtGui.QIcon.Disabled if not self.enabled else QtGui.QIcon.Normal)
self.label_icon.setPixmap(icon_pixmap)
self.label_icon.setFixedSize(icon_sz)
class VmTypeWidget(VmIconWidget):
class VmTypeItem(QtWidgets.QTableWidgetItem):
def __init__(self, value, vm):
super(VmTypeWidget.VmTypeItem, self).__init__()
self.value = value
self.qid = vm.qid
self.name = vm.name
def set_value(self, value):
self.value = value
# pylint: disable=too-many-return-statements
def __lt__(self, other):
if self.qid == 0:
return True
if other.qid == 0:
return False
if self.value == other.value:
return self.name < other.name
return self.value < other.value
def __init__(self, vm, parent=None):
(icon_path, tooltip) = self.get_vm_icon(vm)
super(VmTypeWidget, self).__init__(
icon_path, True, 0.8, tooltip, parent)
self.vm = vm
self.table_item = self.VmTypeItem(self.value, vm)
self.value = None
# TODO: add "provides network" column
def get_vm_icon(self, vm):
if vm.klass == 'AdminVM':
self.value = 0
icon_name = "dom0"
elif vm.klass == 'TemplateVM':
self.value = 3
icon_name = "templatevm"
elif vm.klass == 'StandaloneVM':
self.value = 4
icon_name = "standalonevm"
else:
self.value = 5 + vm.label.index
icon_name = "appvm"
return ":/" + icon_name + ".png", vm.klass
class VmLabelWidget(VmIconWidget):
class VmLabelItem(QtWidgets.QTableWidgetItem):
def __init__(self, value, vm):
super(VmLabelWidget.VmLabelItem, self).__init__()
self.value = value
self.qid = vm.qid
self.name = vm.name
def set_value(self, value):
self.value = value
# pylint: disable=too-many-return-statements
def __lt__(self, other):
if self.qid == 0:
return True
if other.qid == 0:
return False
if self.value == other.value:
return self.name < other.name
return self.value < other.value
def __init__(self, vm, parent=None):
self.icon_path = self.get_vm_icon_path(vm)
super(VmLabelWidget, self).__init__(self.icon_path,
True, 0.8, None, parent)
self.vm = vm
self.table_item = self.VmLabelItem(self.value, vm)
self.value = None
def get_vm_icon_path(self, vm):
self.value = vm.label.index
return vm.label.icon
def update(self):
icon_path = self.get_vm_icon_path(self.vm)
if icon_path != self.icon_path:
self.icon_path = icon_path
self.set_icon(icon_path)
class VmStatusIcon(QtWidgets.QLabel):
def __init__(self, vm, parent=None):
super(VmStatusIcon, self).__init__(parent)
self.vm = vm
self.status = None
self.set_on_icon()
self.previous_power_state = self.vm.get_power_state()
def update(self):
if self.previous_power_state != self.vm.get_power_state():
self.set_on_icon()
self.previous_power_state = self.vm.get_power_state()
def set_on_icon(self):
if self.vm.get_power_state() == "Running":
icon = QtGui.QIcon(":/on.png")
self.status = 3
elif self.vm.get_power_state() in ["Paused", "Suspended"]:
icon = QtGui.QIcon(":/paused.png")
self.status = 2
elif self.vm.get_power_state() in ["Transient", "Halting", "Dying"]:
icon = QtGui.QIcon(":/transient.png")
self.status = 1
else:
icon = QtGui.QIcon(":/off.png")
self.status = 0
icon_sz = QtCore.QSize(row_height * 0.5, row_height * 0.5)
icon_pixmap = icon.pixmap(icon_sz)
self.setPixmap(icon_pixmap)
self.setFixedSize(icon_sz)
class VmInfoWidget(QtWidgets.QWidget):
class VmInfoItem(QtWidgets.QTableWidgetItem):
def __init__(self, on_icon, upd_info_item, vm):
super(VmInfoWidget.VmInfoItem, self).__init__()
self.on_icon = on_icon
self.upd_info_item = upd_info_item
self.vm = vm
self.qid = vm.qid
self.name = vm.name
def __lt__(self, other):
# pylint: disable=too-many-return-statements
if self.qid == 0:
return True
if other.qid == 0:
return False
self_val = self.upd_info_item.value
other_val = other.upd_info_item.value
if self.tableWidget().\
horizontalHeader().sortIndicatorOrder() == update_order:
# the result will be sorted by upd, sorting order: Ascending
self_val += 1 if self.on_icon.status > 0 else 0
other_val += 1 if other.on_icon.status > 0 else 0
if self_val == other_val:
return self.name < other.name
return self_val > other_val
if self.tableWidget().\
horizontalHeader().sortIndicatorOrder() == power_order:
# the result will be sorted by power state,
# sorting order: Descending
if self.on_icon.status == other.on_icon.status:
return self.name < other.name
return self_val > other_val
# it would be strange if this happened
return
def __init__(self, vm, parent=None):
super(VmInfoWidget, self).__init__(parent)
self.vm = vm
layout = QtWidgets.QHBoxLayout()
self.on_icon = VmStatusIcon(vm)
self.upd_info = VmUpdateInfoWidget(vm, show_text=False)
self.error_icon = VmIconWidget(":/warning.png")
self.blk_icon = VmIconWidget(":/mount.png")
self.rec_icon = VmIconWidget(":/mic.png")
layout.addWidget(self.on_icon)
layout.addWidget(self.upd_info)
layout.addWidget(self.error_icon)
layout.addItem(QtWidgets.QSpacerItem(0, 10,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding))
layout.addWidget(self.blk_icon)
layout.addWidget(self.rec_icon)
layout.setContentsMargins(5, 0, 5, 0)
self.setLayout(layout)
self.rec_icon.setVisible(False)
self.blk_icon.setVisible(False)
self.error_icon.setVisible(False)
self.table_item = self.VmInfoItem(self.on_icon,
self.upd_info.table_item, vm)
def update_vm_state(self):
self.on_icon.update()
self.upd_info.update_outdated()
class VMPropertyItem(QtWidgets.QTableWidgetItem):
def __init__(self, vm, property_name, empty_function=(lambda x: False),
check_default=False):
"""
Class used to represent Qube Manager table widget.
:param vm: vm object
:param property_name: name of the property the widget represents
:param empty_function: a function that, when applied to values of
vm.property_name, returns True when the property value should be
represented as an empty string and False otherwise; by default this
function always returns false (vm.property_name is represented by an
empty string only when it actually is one)
:param check_default: if True, the widget will prepend its text with
"default" if the if the property is set to DEFAULT
"""
super(VMPropertyItem, self).__init__()
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.setTextAlignment(QtCore.Qt.AlignVCenter)
self.vm = vm
self.qid = vm.qid
self.property_name = property_name
self.name = vm.name
self.empty_function = empty_function
self.check_default = check_default
self.update()
def update(self):
val = getattr(self.vm, self.property_name, None)
if self.empty_function(val):
text = ""
elif val is None:
text = QtCore.QCoreApplication.translate("QubeManager", "n/a")
elif val is True:
text = QtCore.QCoreApplication.translate("QubeManager", "Yes")
else:
text = str(val)
if self.check_default and hasattr(self.vm, self.property_name) and \
self.vm.property_is_default(self.property_name):
text = QtCore.QCoreApplication.translate(
"QubeManager", 'default ({})').format(text)
self.setText(text)
def __lt__(self, other):
if self.qid == 0:
return True
if other.qid == 0:
return False
if self.text() == other.text():
return self.name < other.name
return super(VMPropertyItem, self).__lt__(other)
class VmTemplateItem(VMPropertyItem):
def __init__(self, vm):
super(VmTemplateItem, self).__init__(vm, "template")
def update(self):
if getattr(self.vm, 'template', None) is not None:
self.setText(self.vm.template.name)
else:
font = QtGui.QFont()
font.setStyle(QtGui.QFont.StyleItalic)
self.setFont(font)
self.setForeground(QtGui.QBrush(QtGui.QColor("gray")))
self.setText(self.vm.klass)
class VmInternalItem(VMPropertyItem):
def __init__(self, vm):
super(VmInternalItem, self).__init__(vm, None)
def update(self):
internal = self.vm.features.get('internal', False)
self.setText(QtCore.QCoreApplication.translate(
"QubeManager", "Yes") if internal else "")
# features man qvm-features
class VmUpdateInfoWidget(QtWidgets.QWidget):
class VmUpdateInfoItem(QtWidgets.QTableWidgetItem):
def __init__(self, value, vm):
super(VmUpdateInfoWidget.VmUpdateInfoItem, self).__init__()
self.value = 0
self.vm = vm
self.qid = vm.qid
self.name = vm.name
self.set_value(value)
def set_value(self, value):
if value in ("outdated", "to-be-outdated"):
self.value = 30
elif value == "update":
self.value = 20
else:
self.value = 0
def __lt__(self, other):
if self.qid == 0:
return True
if other.qid == 0:
return False
if self.value == other.value:
return self.name < other.name
return self.value < other.value
def __init__(self, vm, show_text=True, parent=None):
super(VmUpdateInfoWidget, self).__init__(parent)
layout = QtWidgets.QHBoxLayout()
self.show_text = show_text
if self.show_text:
self.label = QtWidgets.QLabel("")
layout.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
else:
self.icon = QtWidgets.QLabel("")
layout.addWidget(self.icon, alignment=QtCore.Qt.AlignCenter)
self.setLayout(layout)
self.vm = vm
self.previous_outdated_state = None
self.previous_update_recommended = None
self.value = None
self.table_item = VmUpdateInfoWidget.VmUpdateInfoItem(self.value, vm)
self.update_outdated()
def update_outdated(self):
outdated_state = False
is_disposable = getattr(self.vm, 'auto_cleanup', False)
if not is_disposable and self.vm.is_running():
if hasattr(self.vm, 'template') and self.vm.template.is_running():
outdated_state = "to-be-outdated"
if not outdated_state:
for vol in self.vm.volumes.values():
if vol.is_outdated():
outdated_state = "outdated"
break
if not is_disposable and \
self.vm.klass in {'TemplateVM', 'StandaloneVM'} and \
self.vm.features.get('updates-available', False):
outdated_state = 'update'
self.update_status_widget(outdated_state)
def update_status_widget(self, state):
if state == self.previous_outdated_state:
return
self.previous_outdated_state = state
self.value = state
self.table_item.set_value(state)
if state == "update":
label_text = "<font color=\"#CCCC00\">{}</font>".format(
QtCore.QCoreApplication.translate("QubeManager",
"Check updates"))
icon_path = ":/update-recommended.png"
tooltip_text = QtCore.QCoreApplication.translate("QubeManager",
"Updates pending!")
elif state == "outdated":
label_text = "<font color=\"red\">{}</font>".format(
QtCore.QCoreApplication.translate("QubeManager",
"Qube outdated"))
icon_path = ":/outdated.png"
tooltip_text = QtCore.QCoreApplication.translate(
"QubeManager",
"The qube must be restarted for its filesystem to reflect the "
"template's recent committed changes.")
elif state == "to-be-outdated":
label_text = "<font color=\"#800000\">{}</font>".format(
QtCore.QCoreApplication.translate("QubeManager",
"Template running"))
icon_path = ":/to-be-outdated.png"
tooltip_text = QtCore.QCoreApplication.translate(
"QubeManager",
"The Template must be stopped before changes from its "
"current session can be picked up by this qube.")
else:
label_text = None
tooltip_text = None
icon_path = None
if hasattr(self, 'icon'):
self.icon.setVisible(False)
self.layout().removeWidget(self.icon)
del self.icon
if self.show_text:
self.label.setText(label_text)
else:
if icon_path is not None:
self.icon = VmIconWidget(icon_path, True, 0.7)
self.icon.setToolTip(tooltip_text)
self.layout().addWidget(self.icon,
alignment=QtCore.Qt.AlignCenter)
self.icon.setVisible(True)
class VmSizeOnDiskItem(QtWidgets.QTableWidgetItem):
def __init__(self, vm):
super(VmSizeOnDiskItem, self).__init__()
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.vm = vm
self.qid = vm.qid
self.name = vm.name
self.value = 0
self.update()
self.setTextAlignment(QtCore.Qt.AlignVCenter)
def update(self):
if self.vm.qid == 0:
self.setText(QtCore.QCoreApplication.translate("QubeManager",
"n/a"))
else:
self.value = 10
self.value = round(self.vm.get_disk_utilization()/(1024*1024), 2)
self.setText(str(self.value) + " MiB")
def __lt__(self, other):
if self.qid == 0:
return True
if other.qid == 0:
return False
if self.value == other.value:
return self.name < other.name
return self.value < other.value
class VmLastBackupItem(VMPropertyItem):
def __init__(self, vm, property_name):
super(VmLastBackupItem, self).__init__(vm, property_name)
def update(self):
backup_timestamp = getattr(self.vm, 'backup_timestamp', None)
if backup_timestamp:
self.setText(
str(datetime.datetime.fromtimestamp(backup_timestamp)))
else:
self.setText("")

View File

@ -30,11 +30,16 @@ import datetime
import time
from PyQt5 import QtTest, QtCore, QtWidgets
from PyQt5.QtCore import (Qt, QSize)
from PyQt5.QtGui import (QIcon)
from qubesadmin import Qubes, events, exc
import qubesmanager.qube_manager as qube_manager
from qubesmanager.tests import init_qtapp
icon_size = QSize(24, 24)
class QubeManagerTest(unittest.TestCase):
def setUp(self):
super(QubeManagerTest, self).setUp()
@ -58,14 +63,14 @@ class QubeManagerTest(unittest.TestCase):
def test_001_correct_vms_listed(self):
vms_in_table = []
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
self.assertIsNotNone(vm)
vms_in_table.append(vm.name)
# check that name is listed correctly
name_item = self._get_table_item(row, "Name")
self.assertEqual(name_item.text(), vm.name,
self.assertEqual(name_item, vm.name,
"Incorrect VM name for {}".format(vm.name))
actual_vms = [vm.name for vm in self.qapp.domains]
@ -76,39 +81,42 @@ class QubeManagerTest(unittest.TestCase):
"Incorrect VMs loaded")
def test_002_correct_template_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
# check that template is listed correctly
template_item = self._get_table_item(row, "Template")
if getattr(vm, "template", None):
self.assertEqual(vm.template,
template_item.text(),
template_item,
"Incorrect template for {}".format(vm.name))
else:
self.assertEqual(vm.klass, template_item.text(),
self.assertEqual(vm.klass, template_item,
"Incorrect class for {}".format(vm.name))
def test_003_correct_netvm_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
# check that netvm is listed correctly
netvm_item = self._get_table_item(row, "NetVM")
netvm_value = getattr(vm, "netvm", None)
netvm_value = "n/a" if not netvm_value else netvm_value
if not netvm_value:
netvm_value = "n/a"
if netvm_value and hasattr(vm, "netvm") \
and vm.property_is_default("netvm"):
netvm_value = "default ({})".format(netvm_value)
self.assertEqual(netvm_value,
netvm_item.text(),
netvm_item,
"Incorrect netvm for {}".format(vm.name))
def test_004_correct_disk_usage_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
size_item = self._get_table_item(row, "Size")
size_item = self._get_table_item(row, "Disk Usage")
if vm.klass == 'AdminVM':
size_value = "n/a"
else:
@ -116,22 +124,22 @@ class QubeManagerTest(unittest.TestCase):
size_value = str(size_value) + " MiB"
self.assertEqual(size_value,
size_item.text(),
size_item,
"Incorrect size for {}".format(vm.name))
def test_005_correct_internal_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
internal_item = self._get_table_item(row, "Internal")
internal_value = "Yes" if vm.features.get('internal', False) else ""
self.assertEqual(internal_item.text(), internal_value,
self.assertEqual(internal_item, internal_value,
"Incorrect internal value for {}".format(vm.name))
def test_006_correct_ip_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
ip_item = self._get_table_item(row, "IP")
if hasattr(vm, 'ip'):
@ -140,24 +148,24 @@ class QubeManagerTest(unittest.TestCase):
else:
ip_value = "n/a"
self.assertEqual(ip_value, ip_item.text(),
self.assertEqual(ip_value, ip_item,
"Incorrect ip value for {}".format(vm.name))
def test_007_incl_in_backups_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
incl_backups_item = self._get_table_item(row, "Include in backups")
incl_backups_value = getattr(vm, 'include_in_backups', False)
incl_backups_value = "Yes" if incl_backups_value else ""
self.assertEqual(
incl_backups_value, incl_backups_item.text(),
incl_backups_value, incl_backups_item,
"Incorrect include in backups value for {}".format(vm.name))
def test_008_last_backup_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
last_backup_item = self._get_table_item(row, "Last backup")
last_backup_value = getattr(vm, 'backup_timestamp', None)
@ -165,16 +173,14 @@ class QubeManagerTest(unittest.TestCase):
if last_backup_value:
last_backup_value = str(
datetime.datetime.fromtimestamp(last_backup_value))
else:
last_backup_value = ""
self.assertEqual(
last_backup_value, last_backup_item.text(),
last_backup_value, last_backup_item,
"Incorrect last backup value for {}".format(vm.name))
def test_009_def_dispvm_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
def_dispvm_item = self._get_table_item(row, "Default DispVM")
if vm.property_is_default("default_dispvm"):
@ -184,45 +190,40 @@ class QubeManagerTest(unittest.TestCase):
def_dispvm_value = getattr(vm, "default_dispvm", None)
self.assertEqual(
str(def_dispvm_value), def_dispvm_item.text(),
def_dispvm_value, def_dispvm_item,
"Incorrect default dispvm value for {}".format(vm.name))
def test_010_is_dvm_template_listed(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
is_dvm_template_item = self._get_table_item(row, "Is DVM Template")
is_dvm_template_value = "Yes" if \
getattr(vm, "template_for_dispvms", False) else ""
self.assertEqual(
is_dvm_template_value, is_dvm_template_item.text(),
is_dvm_template_value, is_dvm_template_item,
"Incorrect is DVM template value for {}".format(vm.name))
def test_011_is_label_correct(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
icon = QIcon.fromTheme(vm.label.icon)
icon = icon.pixmap(icon_size)
label_item = self._get_table_item(row, "Label")
self.assertEqual(label_item.icon_path, vm.label.icon)
label_pixmap = self._get_table_item(row, "Label", Qt.DecorationRole)
self.assertEqual(label_pixmap.toImage(), icon.toImage())
def test_012_is_state_correct(self):
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_vm(row)
state_item = self._get_table_item(row, "State")
# this should not be done like that in table_widgets
displayed_power_state = state_item.on_icon.status
if vm.is_running():
correct_power_state = 3
else:
correct_power_state = 0
displayed_power_state = self._get_table_item(row, "State")['power']
self.assertEqual(
displayed_power_state, correct_power_state,
displayed_power_state, vm.get_power_state(),
"Wrong power state displayed for {}".format(vm.name))
def test_013_incorrect_settings_file(self):
@ -245,25 +246,23 @@ class QubeManagerTest(unittest.TestCase):
self.assertEqual(mock_warning.call_count, 1)
def test_100_sorting(self):
self.dialog.table.sortByColumn(self.dialog.columns_indices["Template"],
QtCore.Qt.AscendingOrder)
col = self.dialog.qubes_model.columns_indices.index("Template")
self.dialog.table.sortByColumn(col, QtCore.Qt.AscendingOrder)
self.__check_sorting("Template")
self.dialog.table.sortByColumn(self.dialog.columns_indices["Name"],
QtCore.Qt.AscendingOrder)
col = self.dialog.qubes_model.columns_indices.index("Name")
self.dialog.table.sortByColumn(col, QtCore.Qt.AscendingOrder)
self.__check_sorting("Name")
@unittest.mock.patch('qubesmanager.qube_manager.QtCore.QSettings.setValue')
@unittest.mock.patch('qubesmanager.qube_manager.QtCore.QSettings.sync')
def test_101_hide_column(self, mock_sync, mock_settings):
self.dialog.action_is_dvm_template.trigger()
mock_settings.assert_called_with('columns/Is DVM Template', False)
self.assertEqual(mock_sync.call_count, 1, "Hidden column not synced")
self.dialog.action_is_dvm_template.trigger()
@unittest.mock.patch('qubesmanager.qube_manager.QSettings.setValue')
def test_101_hide_column(self, mock_settings):
model = self.dialog.qubes_model
action_no = model.columns_indices.index('Is DVM Template')
self.dialog.menu_view.actions()[action_no].trigger()
mock_settings.assert_called_with('columns/Is DVM Template', True)
self.assertEqual(mock_sync.call_count, 2, "Hidden column not synced")
self.dialog.menu_view.actions()[action_no].trigger()
mock_settings.assert_called_with('columns/Is DVM Template', False)
@unittest.mock.patch('qubesmanager.settings.VMSettingsWindow')
def test_200_vm_open_settings(self, mock_window):
@ -282,9 +281,9 @@ class QubeManagerTest(unittest.TestCase):
self.assertFalse(self.dialog.action_settings.isEnabled(),
"Settings not disabled for admin VM")
self.assertFalse(self.dialog.action_editfwrules.isEnabled(),
"Settings not disabled for admin VM")
"Editfw not disabled for admin VM")
self.assertFalse(self.dialog.action_appmenus.isEnabled(),
"Settings not disabled for admin VM")
"Appmenus not disabled for admin VM")
@unittest.mock.patch('qubesmanager.settings.VMSettingsWindow')
def test_202_vm_open_firewall(self, mock_window):
@ -461,16 +460,17 @@ class QubeManagerTest(unittest.TestCase):
self.assertFalse(self.dialog.action_removevm.isEnabled())
@unittest.mock.patch("PyQt5.QtWidgets.QMessageBox")
@unittest.mock.patch("qubesmanager.qube_manager.QMessageBox")
@unittest.mock.patch('qubesadmin.utils.vm_dependencies')
def test_218_remove_vm_dependencies(self, mock_dependencies, mock_msgbox):
action = self.dialog.action_removevm
mock_vm = unittest.mock.Mock(spec=['name'],
**{'name.return_value': 'test-vm'})
mock_dependencies.return_value = [(mock_vm, "test_prop")]
action = self.dialog.action_removevm
self._select_non_admin_vm()
action.trigger()
mock_msgbox().show.assert_called_with()
@unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
@ -486,7 +486,7 @@ class QubeManagerTest(unittest.TestCase):
with unittest.mock.patch('qubesmanager.common_threads.RemoveVMThread')\
as mock_thread:
mock_input.return_value = (selected_vm.name, False)
mock_input.return_value = (selected_vm, False)
action.trigger()
self.assertEqual(mock_thread.call_count, 0,
"VM removed despite user clicking 'cancel")
@ -766,7 +766,7 @@ class QubeManagerTest(unittest.TestCase):
self.assertEqual(len(self.dialog.threads_list), 3)
def test_400_event_domain_added(self):
number_of_vms = self.dialog.table.rowCount()
number_of_vms = self.dialog.table.model().rowCount()
self.addCleanup(subprocess.call, ["qvm-remove", "-f", "test-vm"])
@ -774,7 +774,7 @@ class QubeManagerTest(unittest.TestCase):
["qvm-create", "--label", "red", "test-vm"])
# a single row was added to the table
self.assertEqual(self.dialog.table.rowCount(), number_of_vms + 1)
self.assertEqual(self.dialog.table.model().rowCount(), number_of_vms+1)
# table contains the correct vms
vms_in_table = self._create_set_of_current_vms()
@ -785,15 +785,14 @@ class QubeManagerTest(unittest.TestCase):
"correctly after add")
# check if sorting works
self.dialog.table.sortItems(self.dialog.columns_indices["Name"],
QtCore.Qt.AscendingOrder)
self.__check_sorting("Name")
# try opening settings for the added vm
for row in range(self.dialog.table.rowCount()):
for row in range(self.dialog.table.model().rowCount()):
name = self._get_table_item(row, "Name")
if name.text() == "test-vm":
self.dialog.table.setCurrentItem(name)
if name == "test-vm":
index = self.dialog.table.model().index(row, 0)
self.dialog.table.setCurrentIndex(index)
break
with unittest.mock.patch('qubesmanager.settings.VMSettingsWindow')\
as mock_settings:
@ -816,8 +815,6 @@ class QubeManagerTest(unittest.TestCase):
self.assertEqual(initial_vms, current_vms)
# check if sorting works
self.dialog.table.sortItems(self.dialog.columns_indices["Name"],
QtCore.Qt.AscendingOrder)
self.__check_sorting("Name")
def test_403_event_dispvm_added(self):
@ -874,27 +871,28 @@ class QubeManagerTest(unittest.TestCase):
target_vm_name = "work"
vm_row = self._find_vm_row(target_vm_name)
current_label_path = self._get_table_item(vm_row, "Label").icon_path
current_label = self._get_table_item(vm_row, "Label", Qt.DecorationRole)
self.addCleanup(
subprocess.call, ["qvm-prefs", target_vm_name, "label", "blue"])
self._run_command_and_process_events(
["qvm-prefs", target_vm_name, "label", "red"])
new_label_path = self._get_table_item(vm_row, "Label").icon_path
new_label = self._get_table_item(vm_row, "Label", Qt.DecorationRole)
self.assertNotEqual(current_label_path, new_label_path,
"Label path did not change")
self.assertEqual(
new_label_path,
self.qapp.domains[target_vm_name].label.icon,
"Incorrect label")
self.assertNotEqual(current_label.toImage(), new_label.toImage(),
"Label icon did not change")
icon = QIcon.fromTheme(self.qapp.domains[target_vm_name].label.icon)
icon = icon.pixmap(icon_size)
self.assertEqual(new_label.toImage(), icon.toImage(), "Incorrect label")
def test_406_prop_change_template(self):
target_vm_name = "work"
vm_row = self._find_vm_row(target_vm_name)
old_template = self._get_table_item(vm_row, "Template").text()
old_template = self._get_table_item(vm_row, "Template")
new_template = None
for vm in self.qapp.domains:
if vm.klass == 'TemplateVM' and vm.name != old_template:
@ -908,10 +906,10 @@ class QubeManagerTest(unittest.TestCase):
["qvm-prefs", target_vm_name, "template", new_template])
self.assertNotEqual(old_template,
self._get_table_item(vm_row, "Template").text(),
self._get_table_item(vm_row, "Template"),
"Template did not change")
self.assertEqual(
self._get_table_item(vm_row, "Template").text(),
self._get_table_item(vm_row, "Template"),
self.qapp.domains[target_vm_name].template.name,
"Incorrect template")
@ -919,7 +917,7 @@ class QubeManagerTest(unittest.TestCase):
target_vm_name = "work"
vm_row = self._find_vm_row(target_vm_name)
old_netvm = self._get_table_item(vm_row, "NetVM").text()
old_netvm = self._get_table_item(vm_row, "NetVM")
new_netvm = None
for vm in self.qapp.domains:
if getattr(vm, "provides_network", False) and vm.name != old_netvm:
@ -932,10 +930,10 @@ class QubeManagerTest(unittest.TestCase):
["qvm-prefs", target_vm_name, "netvm", new_netvm])
self.assertNotEqual(old_netvm,
self._get_table_item(vm_row, "NetVM").text(),
self._get_table_item(vm_row, "NetVM"),
"NetVM did not change")
self.assertEqual(
self._get_table_item(vm_row, "NetVM").text(),
self._get_table_item(vm_row, "NetVM"),
self.qapp.domains[target_vm_name].netvm.name,
"Incorrect NetVM")
@ -950,7 +948,7 @@ class QubeManagerTest(unittest.TestCase):
["qvm-features", "work", "interal", "1"])
self.assertEqual(
self._get_table_item(vm_row, "Internal").text(),
self._get_table_item(vm_row, "Internal"),
"Yes",
"Incorrect value for internal VM")
@ -958,7 +956,7 @@ class QubeManagerTest(unittest.TestCase):
["qvm-features", "--unset", "work", "interal"])
self.assertEqual(
self._get_table_item(vm_row, "Internal").text(),
self._get_table_item(vm_row, "Internal"),
"",
"Incorrect value for non-internal VM")
@ -966,7 +964,7 @@ class QubeManagerTest(unittest.TestCase):
target_vm_name = "work"
vm_row = self._find_vm_row(target_vm_name)
old_ip = self._get_table_item(vm_row, "IP").text()
old_ip = self._get_table_item(vm_row, "IP")
new_ip = old_ip.replace(".0.", ".5.")
self.addCleanup(
@ -975,10 +973,10 @@ class QubeManagerTest(unittest.TestCase):
["qvm-prefs", target_vm_name, "ip", new_ip])
self.assertNotEqual(old_ip,
self._get_table_item(vm_row, "IP").text(),
self._get_table_item(vm_row, "IP"),
"IP did not change")
self.assertEqual(
self._get_table_item(vm_row, "IP").text(),
self._get_table_item(vm_row, "IP"),
self.qapp.domains[target_vm_name].ip,
"Incorrect IP")
@ -996,7 +994,7 @@ class QubeManagerTest(unittest.TestCase):
["qvm-prefs", target_vm_name, "include_in_backups", str(new_value)])
self.assertEqual(
self._get_table_item(vm_row, "Internal").text(),
self._get_table_item(vm_row, "Internal"),
"Yes" if new_value else "",
"Incorrect value for include_in_backups")
@ -1005,7 +1003,7 @@ class QubeManagerTest(unittest.TestCase):
target_timestamp = "2015-01-01 17:00:00"
vm_row = self._find_vm_row(target_vm_name)
old_value = self._get_table_item(vm_row, "Last backup").text()
old_value = self._get_table_item(vm_row, "Last backup")
new_value = datetime.datetime.strptime(
target_timestamp, "%Y-%m-%d %H:%M:%S")
@ -1017,10 +1015,10 @@ class QubeManagerTest(unittest.TestCase):
str(int(new_value.timestamp()))])
self.assertNotEqual(old_value,
self._get_table_item(vm_row, "Last backup").text(),
self._get_table_item(vm_row, "Last backup"),
"Last backup date did not change")
self.assertEqual(
self._get_table_item(vm_row, "Last backup").text(),
self._get_table_item(vm_row, "Last backup"),
target_timestamp,
"Incorrect Last backup date")
@ -1028,8 +1026,9 @@ class QubeManagerTest(unittest.TestCase):
target_vm_name = "work"
vm_row = self._find_vm_row(target_vm_name)
old_default_dispvm =\
self._get_table_item(vm_row, "Default DispVM").text()
self._get_table_item(vm_row, "Default DispVM")
new_default_dispvm = None
for vm in self.qapp.domains:
if getattr(vm, "template_for_dispvms", False) and vm.name !=\
@ -1038,18 +1037,18 @@ class QubeManagerTest(unittest.TestCase):
break
self.addCleanup(
subprocess.call,
["qvm-prefs", target_vm_name, "default_dispvm", old_default_dispvm])
subprocess.call,
["qvm-prefs", target_vm_name, "default_dispvm", old_default_dispvm])
self._run_command_and_process_events(
["qvm-prefs", target_vm_name, "default_dispvm", new_default_dispvm])
self.assertNotEqual(
old_default_dispvm,
self._get_table_item(vm_row, "Default DispVM").text(),
self._get_table_item(vm_row, "Default DispVM"),
"Default DispVM did not change")
self.assertEqual(
self._get_table_item(vm_row, "Default DispVM").text(),
self._get_table_item(vm_row, "Default DispVM"),
self.qapp.domains[target_vm_name].default_dispvm.name,
"Incorrect Default DispVM")
@ -1064,7 +1063,7 @@ class QubeManagerTest(unittest.TestCase):
["qvm-prefs", target_vm_name, "template_for_dispvms", "True"])
self.assertEqual(
self._get_table_item(vm_row, "Is DVM Template").text(),
self._get_table_item(vm_row, "Is DVM Template"),
"Yes",
"Incorrect value for DVM Template")
@ -1072,7 +1071,7 @@ class QubeManagerTest(unittest.TestCase):
["qvm-prefs", "--default", target_vm_name, "template_for_dispvms"])
self.assertEqual(
self._get_table_item(vm_row, "Is DVM Template").text(),
self._get_table_item(vm_row, "Is DVM Template"),
"",
"Incorrect value for not DVM Template")
@ -1088,19 +1087,17 @@ class QubeManagerTest(unittest.TestCase):
self._run_command_and_process_events(
["qvm-start", target_vm_name], timeout=60)
status_item = self._get_table_item(vm_row, "State")
displayed_state = self._get_table_item(vm_row, "State")
displayed_power_state = status_item.on_icon.status
self.assertEqual(displayed_power_state, 3,
self.assertEqual(displayed_state['power'], 'Running',
"Power state failed to update on start")
self._run_command_and_process_events(
["qvm-shutdown", "--wait", target_vm_name], timeout=30)
displayed_power_state = status_item.on_icon.status
displayed_state = self._get_table_item(vm_row, "State")
self.assertEqual(displayed_power_state, 0,
self.assertEqual(displayed_state['power'], 'Halted',
"Power state failed to update on shutdown")
def test_415_template_vm_started(self):
@ -1116,9 +1113,8 @@ class QubeManagerTest(unittest.TestCase):
if target_vm_name:
break
for i in range(self.dialog.table.rowCount()):
self._get_table_item(i, "State").update_vm_state =\
unittest.mock.Mock()
for i in range(self.dialog.table.model().rowCount()):
self._get_table_vminfo(i).update = unittest.mock.Mock()
self.addCleanup(
subprocess.call,
@ -1126,12 +1122,12 @@ class QubeManagerTest(unittest.TestCase):
self._run_command_and_process_events(
["qvm-start", target_vm_name], timeout=60)
for i in range(self.dialog.table.rowCount()):
call_count = self._get_table_item(
i, "State").update_vm_state.call_count
if self._get_table_item(i, "Template").text() == target_vm_name:
for i in range(self.dialog.table.model().rowCount()):
call_count = self._get_table_vminfo(
i).update.call_count
if self._get_table_item(i, "Template") == target_vm_name:
self.assertGreater(call_count, 0)
elif self._get_table_item(i, "Name").text() == target_vm_name:
elif self._get_table_item(i, "Name") == target_vm_name:
self.assertGreater(call_count, 0)
else:
self.assertEqual(call_count, 0)
@ -1168,15 +1164,15 @@ class QubeManagerTest(unittest.TestCase):
"Same logs found for dom0 and non-adminVM")
def _find_vm_row(self, vm_name):
for row in range(self.dialog.table.rowCount()):
for row in range(self.dialog.table.model().rowCount()):
name = self._get_table_item(row, "Name")
if name.text() == vm_name:
if name == vm_name:
return row
return None
def _count_visible_table_rows(self):
result = 0
for i in range(self.dialog.table.rowCount()):
for i in range(self.dialog.table.model().rowCount()):
if not self.dialog.table.isRowHidden(i):
result += 1
return result
@ -1210,54 +1206,51 @@ class QubeManagerTest(unittest.TestCase):
def _create_set_of_current_vms(self):
result = set()
for i in range(self.dialog.table.rowCount()):
result.add(self._get_table_item(i, "Name").vm.name)
for i in range(self.dialog.table.model().rowCount()):
result.add(self._get_table_item(i, "Name"))
return result
def _select_admin_vm(self):
for row in range(self.dialog.table.rowCount()):
template = self.dialog.table.item(
row, self.dialog.columns_indices["Template"])
if template.text() == 'AdminVM':
self.dialog.table.setCurrentItem(template)
return template.vm
for row in range(self.dialog.table.model().rowCount()):
template = self._get_table_item(row, "Template")
if template == 'AdminVM':
index = self.dialog.table.model().index(row, 0)
self.dialog.table.setCurrentIndex(index)
return index.data(Qt.UserRole).vm
return None
def _select_non_admin_vm(self, running=None):
for row in range(self.dialog.table.rowCount()):
template = self.dialog.table.item(
row, self.dialog.columns_indices["Template"])
status = self.dialog.table.item(
row, self.dialog.columns_indices["State"])
if template.text() != 'AdminVM' and \
for row in range(self.dialog.table.model().rowCount()):
template = self._get_table_item(row, "Template")
vm = self._get_table_vm(row)
if template != 'AdminVM' and \
(running is None
or (running and status.on_icon.status == 3)
or (not running and status.on_icon.status != 3)):
self.dialog.table.setCurrentItem(template)
return template.vm
or (running and vm.is_running())
or (not running and not vm.is_running())):
index = self.dialog.table.model().index(row, 0)
self.dialog.table.setCurrentIndex(index)
return vm
return None
def _select_templatevm(self, running=None):
for row in range(self.dialog.table.rowCount()):
template = self.dialog.table.item(
row, self.dialog.columns_indices["Template"])
status = self.dialog.table.item(
row, self.dialog.columns_indices["State"])
if template.text() == 'TemplateVM' and \
for row in range(self.dialog.table.model().rowCount()):
template = self._get_table_item(row, "Template")
vm = self._get_table_vm(row)
if template == 'TemplateVM' and \
(running is None
or (running and status.on_icon.status == 3)
or (not running and status.on_icon.status != 3)):
self.dialog.table.setCurrentItem(template)
return template.vm
or (running and vm.is_running())
or (not running and not vm.is_running())):
index = self.dialog.table.model().index(row, 0)
self.dialog.table.setCurrentIndex(index)
return vm
return None
def __check_sorting(self, column_name):
last_text = None
last_vm = None
for row in range(self.dialog.table.rowCount()):
vm = self._get_table_item(row, "Name").vm.name
text = self._get_table_item(row, column_name).text().lower()
for row in range(self.dialog.table.model().rowCount()):
vm = self._get_table_item(row, "Name")
text = self._get_table_item(row, column_name)
if row == 0:
self.assertEqual(vm, "dom0", "dom0 is not sorted first")
@ -1267,24 +1260,27 @@ class QubeManagerTest(unittest.TestCase):
else:
if last_text == text:
self.assertGreater(
vm, last_vm,
vm.lower(), last_vm.lower(),
"Incorrect sorting for {}".format(column_name))
else:
self.assertGreater(
text, last_text,
text.lower(), last_text.lower(),
"Incorrect sorting for {}".format(column_name))
last_text = text
last_vm = vm
def _get_table_item(self, row, column_name):
value = self.dialog.table.cellWidget(
row, self.dialog.columns_indices[column_name])
if not value:
value = self.dialog.table.item(
row, self.dialog.columns_indices[column_name])
def _get_table_vminfo(self, row):
model = self.dialog.table.model()
return model.index(row, 0).data(Qt.UserRole)
return value
def _get_table_vm(self, row):
model = self.dialog.table.model()
return model.index(row, 0).data(Qt.UserRole).vm
def _get_table_item(self, row, column_name, role = Qt.DisplayRole):
model = self.dialog.table.model()
column = self.dialog.qubes_model.columns_indices.index(column_name)
return model.index(row, column).data(role)
class QubeManagerThreadTest(unittest.TestCase):
def test_01_startvm_thread(self):

View File

@ -71,7 +71,6 @@ rm -rf $RPM_BUILD_ROOT
%{python3_sitelib}/qubesmanager/__pycache__
%{python3_sitelib}/qubesmanager/__init__.py
%{python3_sitelib}/qubesmanager/clipboard.py
%{python3_sitelib}/qubesmanager/table_widgets.py
%{python3_sitelib}/qubesmanager/appmenu_select.py
%{python3_sitelib}/qubesmanager/backup.py
%{python3_sitelib}/qubesmanager/backup_utils.py

View File

@ -11,6 +11,9 @@ class QubesVMNotStartedError(BaseException):
class QubesPropertyAccessError(BaseException):
pass
class QubesNoSuchPropertyError(BaseException):
pass
class QubesDaemonNoResponseError(BaseException):
pass

View File

@ -52,21 +52,6 @@
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="searchContainer">
<property name="spacing">
@ -85,7 +70,7 @@
</layout>
</item>
<item row="1" column="0">
<widget class="QTableWidget" name="table">
<widget class="QTableView" name="table">
<property name="minimumSize">
<size>
<width>0</width>
@ -140,21 +125,9 @@
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<property name="rowCount">
<number>10</number>
</property>
<property name="columnCount">
<number>14</number>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>150</number>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>150</number>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
@ -292,25 +265,6 @@ Template</string>
<property name="title">
<string>&amp;View</string>
</property>
<addaction name="action_vm_type"/>
<addaction name="action_label"/>
<addaction name="action_state"/>
<addaction name="action_template"/>
<addaction name="action_netvm"/>
<addaction name="action_size_on_disk"/>
<addaction name="action_internal"/>
<addaction name="action_ip"/>
<addaction name="action_backups"/>
<addaction name="action_last_backup"/>
<addaction name="action_dispvm_template"/>
<addaction name="action_is_dvm_template"/>
<addaction name="action_virt_mode"/>
<addaction name="separator"/>
<addaction name="action_toolbar"/>
<addaction name="action_menubar"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="action_search"/>
</widget>
<widget class="QMenu" name="menu_vm">
<property name="title">