123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- #!/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 = "n/a"
- elif val is True:
- text = "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 = 'default (' + 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("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
- if 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 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\">Check updates</font>"
- icon_path = ":/update-recommended.png"
- tooltip_text = self.tr("Updates pending!")
- elif state == "outdated":
- label_text = "<font color=\"red\">Qube outdated</font>"
- icon_path = ":/outdated.png"
- tooltip_text = self.tr(
- "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\">Template running</font>"
- icon_path = ":/to-be-outdated.png"
- tooltip_text = self.tr(
- "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("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("")
|