123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- #!/usr/bin/python2
- # -*- 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 General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- import os
- from PyQt4 import QtGui
- from PyQt4.QtCore import QSize, Qt
- from PyQt4.QtGui import QTableWidgetItem, QHBoxLayout, QIcon, QLabel, QWidget, \
- QSizePolicy, QSpacerItem, QFont, QColor, QProgressBar, QPainter, QPen
- import time
- from qubes.qubes import vm_files
- import main
- qubes_dom0_updates_stat_file = '/var/lib/qubes/updates/dom0-updates-available'
- power_order = Qt.DescendingOrder
- update_order = Qt.AscendingOrder
- row_height = 30
- class VmIconWidget (QWidget):
- def __init__(self, icon_path, enabled=True, size_multiplier=0.7,
- tooltip = None, parent=None, icon_sz = (32, 32)):
- super(VmIconWidget, self).__init__(parent)
- self.label_icon = QLabel()
- icon = QIcon (icon_path)
- icon_sz = QSize (row_height * size_multiplier, row_height * size_multiplier)
- icon_pixmap = icon.pixmap(icon_sz, QIcon.Disabled if not enabled else QIcon.Normal)
- self.label_icon.setPixmap (icon_pixmap)
- self.label_icon.setFixedSize (icon_sz)
- if tooltip != None:
- self.label_icon.setToolTip(tooltip)
- layout = QHBoxLayout()
- layout.addWidget(self.label_icon)
- layout.setContentsMargins(0,0,0,0)
- self.setLayout(layout)
- def setToolTip(self, tooltip):
- if tooltip is not None:
- self.label_icon.setToolTip(tooltip)
- else:
- self.label_icon.setToolTip('')
- class VmTypeWidget(VmIconWidget):
- class VmTypeItem(QTableWidgetItem):
- def __init__(self, value, vm):
- super(VmTypeWidget.VmTypeItem, self).__init__()
- self.value = value
- self.vm = vm
- def set_value(self, value):
- self.value = value
- def __lt__(self, other):
- if self.value == other.value:
- return self.vm.qid < other.vm.qid
- else:
- 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.tableItem = self.VmTypeItem(self.value, vm)
- def get_vm_icon(self, vm):
- if vm.qid == 0:
- self.value = 0
- return (":/dom0.png", "Dom0")
- elif vm.is_netvm() and not vm.is_proxyvm():
- self.value = 1
- return (":/netvm.png", "NetVM")
- elif vm.is_proxyvm():
- self.value = 2
- return (":/proxyvm.png", "ProxyVM")
- elif vm.is_appvm() and vm.template is None:
- self.value = 4
- return (":/standalonevm.png", "StandaloneVM")
- elif vm.is_template():
- self.value = 3
- return (":/templatevm.png", "TemplateVM")
- elif vm.is_appvm() or vm.is_disposablevm():
- self.value = 5 + vm.label.index
- return (":/appvm.png", "AppVM")
- class VmLabelWidget(VmIconWidget):
- class VmLabelItem(QTableWidgetItem):
- def __init__(self, value, vm):
- super(VmLabelWidget.VmLabelItem, self).__init__()
- self.value = value
- self.vm = vm
- def set_value(self, value):
- self.value = value
- def __lt__(self, other):
- if self.value == other.value:
- return self.vm.qid < other.vm.qid
- else:
- return self.value < other.value
- def __init__(self, vm, parent=None):
- icon_path = self.get_vm_icon_path(vm)
- super (VmLabelWidget, self).__init__(icon_path, True, 0.8, None, parent)
- self.vm = vm
- self.tableItem = self.VmLabelItem(self.value, vm)
- def get_vm_icon_path(self, vm):
- if vm.qid == 0:
- self.value = 100
- return ":/off.png"
- else:
- self.value = vm.label.index
- return vm.label.icon_path
- class VmNameItem (QTableWidgetItem):
- def __init__(self, vm):
- super(VmNameItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.setText(vm.name)
- self.setTextAlignment(Qt.AlignVCenter)
- self.qid = vm.qid
- class VmStatusIcon(QLabel):
- def __init__(self, vm, parent=None):
- super (VmStatusIcon, self).__init__(parent)
- self.vm = vm
- self.set_on_icon()
- self.previous_power_state = vm.last_power_state
- def update(self):
- if self.previous_power_state != self.vm.last_power_state:
- self.set_on_icon()
- self.previous_power_state = self.vm.last_power_state
- def set_on_icon(self):
- if self.vm.last_power_state == "Running":
- icon = QIcon (":/on.png")
- elif self.vm.last_power_state in ["Paused"]:
- icon = QIcon (":/paused.png")
- elif self.vm.last_power_state in ["Transient", "Halting", "Dying"]:
- icon = QIcon (":/transient.png")
- else:
- icon = QIcon (":/off.png")
- icon_sz = 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 (QWidget):
- class VmInfoItem (QTableWidgetItem):
- def __init__(self, upd_info_item, vm):
- super(VmInfoWidget.VmInfoItem, self).__init__()
- self.upd_info_item = upd_info_item
- self.vm = vm
- def __lt__(self, other):
- 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.vm.is_running() else 0
- other_val += 1 if other.vm.is_running() else 0
- if self_val == other_val:
- return self.vm.qid < other.vm.qid
- else:
- return self_val > other_val
- elif self.tableWidget().horizontalHeader().sortIndicatorOrder() == power_order:
- #the result will be sorted by power state, sorting order: Descending
- self_val = -(self_val/10 + 10*(1 if self.vm.is_running() else 0))
- other_val = -(other_val/10 + 10*(1 if other.vm.is_running() else 0))
- if self_val == other_val:
- return self.vm.qid < other.vm.qid
- else:
- return self_val > other_val
- else:
- #it would be strange if this happened
- return
- def __init__(self, vm, parent = None):
- super (VmInfoWidget, self).__init__(parent)
- self.vm = vm
- layout = 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(QSpacerItem(0, 10, QSizePolicy.Expanding, QtGui.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.tableItem = self.VmInfoItem(self.upd_info.tableItem, vm)
- def update_vm_state(self, vm, blk_visible, rec_visible=None):
- self.on_icon.update()
- self.upd_info.update_outdated(vm)
- if blk_visible != None:
- self.blk_icon.setVisible(blk_visible)
- if rec_visible != None:
- self.rec_icon.setVisible(rec_visible)
- self.error_icon.setToolTip(vm.qubes_manager_state[main.QMVmState
- .ErrorMsg])
- self.error_icon.setVisible(vm.qubes_manager_state[main.QMVmState
- .ErrorMsg] is not None)
- class VmTemplateItem (QTableWidgetItem):
- def __init__(self, vm):
- super(VmTemplateItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.vm = vm
- if vm.template is not None:
- self.setText(vm.template.name)
- else:
- font = QFont()
- font.setStyle(QFont.StyleItalic)
- self.setFont(font)
- self.setTextColor(QColor("gray"))
- if vm.is_appvm(): # and vm.template is None
- self.setText("StandaloneVM")
- elif vm.is_template():
- self.setText("TemplateVM")
- elif vm.qid == 0:
- self.setText("AdminVM")
- elif vm.is_netvm():
- self.setText("NetVM")
- else:
- self.setText("---")
- self.setTextAlignment(Qt.AlignVCenter)
- def __lt__(self, other):
- if self.text() == other.text():
- return self.vm.qid < other.vm.qid
- else:
- return super(VmTemplateItem, self).__lt__(other)
- class VmNetvmItem (QTableWidgetItem):
- def __init__(self, vm):
- super(VmNetvmItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.vm = vm
- if vm.is_netvm() and not vm.is_proxyvm():
- self.setText("n/a")
- elif vm.netvm is not None:
- self.setText(vm.netvm.name)
- else:
- self.setText("---")
- self.setTextAlignment(Qt.AlignVCenter)
- def __lt__(self, other):
- if self.text() == other.text():
- return self.vm.qid < other.vm.qid
- else:
- return super(VmNetvmItem, self).__lt__(other)
- class VmInternalItem(QTableWidgetItem):
- def __init__(self, vm):
- super(VmInternalItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.internal = vm.internal
- if self.internal:
- self.setText("Yes")
- else:
- self.setText("")
- class VmUsageBarWidget (QWidget):
- class VmUsageBarItem (QTableWidgetItem):
- def __init__(self, value, vm):
- super(VmUsageBarWidget.VmUsageBarItem, self).__init__()
- self.value = value
- self.vm = vm
- def set_value(self, value):
- self.value = value
- def __lt__(self, other):
- if self.value == other.value:
- return self.vm.qid < other.vm.qid
- else:
- return int(self.value) < int(other.value)
- def __init__(self, min, max, format, update_func, vm, load, hue=210, parent = None):
- super (VmUsageBarWidget, self).__init__(parent)
- self.min = min
- self.max = max
- self.update_func = update_func
- self.value = min
- self.widget = QProgressBar()
- self.widget.setMinimum(min)
- self.widget.setMaximum(max)
- self.widget.setFormat(format)
- self.widget.setStyleSheet(
- "QProgressBar:horizontal{" +\
- "border: 1px solid hsv({0}, 100, 250);".format(hue) +\
- "border-radius: 4px;\
- background: white;\
- text-align: center;\
- }\
- QProgressBar::chunk:horizontal {\
- background: qlineargradient(x1: 0, y1: 0.5, x2: 1, y2: 0.5, " +\
- "stop: 0 hsv({0}, 170, 207),".format(hue) +
- " stop: 1 white); \
- }"
- )
- layout = QHBoxLayout()
- layout.addWidget(self.widget)
- self.setLayout(layout)
- self.tableItem = self.VmUsageBarItem(min, vm)
- self.update_load(vm, load)
- def update_load(self, vm, load):
- self.value = self.update_func(vm, load)
- self.widget.setValue(self.value)
- self.tableItem.set_value(self.value)
- class ChartWidget (QWidget):
- class ChartItem (QTableWidgetItem):
- def __init__(self, value, vm):
- super(ChartWidget.ChartItem, self).__init__()
- self.value = value
- self.vm = vm
- def set_value(self, value):
- self.value = value
- def __lt__(self, other):
- if self.value == other.value:
- return self.vm.qid < other.vm.qid
- else:
- return self.value < other.value
- def __init__(self, vm, update_func, hue, load = 0, parent = None):
- super (ChartWidget, self).__init__(parent)
- self.update_func = update_func
- self.hue = hue
- if hue < 0 or hue > 255:
- self.hue = 255
- self.load = load
- assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
- self.load_history = [self.load]
- self.tableItem = ChartWidget.ChartItem(self.load, vm)
- def update_load (self, vm, load):
- self.load = self.update_func(vm, load)
- assert self.load >= 0, "load = {0}".format(self.load)
- # assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
- if self.load > 100:
- # FIXME: This is an ugly workaround for cpu_load:/
- self.load = 100
- self.load_history.append (self.load)
- self.tableItem.set_value(self.load)
- self.repaint()
- def paintEvent (self, Event = None):
- p = QPainter (self)
- dx = 4
- W = self.width()
- H = self.height() - 5
- N = len(self.load_history)
- if N > W/dx:
- tail = N - W/dx
- N = W/dx
- self.load_history = self.load_history[tail:]
- assert len(self.load_history) == N
- for i in range (0, N-1):
- val = self.load_history[N- i - 1]
- sat = 70 + val*(255-70)/100
- color = QColor.fromHsv (self.hue, sat, 255)
- pen = QPen (color)
- pen.setWidth(dx-1)
- p.setPen(pen)
- if val > 0:
- p.drawLine (W - i*dx - dx, H , W - i*dx - dx, H - (H - 5) * val/100)
- class VmUpdateInfoWidget(QWidget):
- class VmUpdateInfoItem (QTableWidgetItem):
- def __init__(self, value, vm):
- super(VmUpdateInfoWidget.VmUpdateInfoItem, self).__init__()
- self.value = 0
- self.vm = vm
- self.set_value(value)
- def set_value(self, value):
- if value == "outdated":
- self.value = 30
- elif value == "update":
- self.value = 20
- else:
- self.value = 0
- def __lt__(self, other):
- if self.value == other.value:
- return self.vm.qid < other.vm.qid
- else:
- return self.value < other.value
- def __init__(self, vm, show_text=True, parent = None):
- super (VmUpdateInfoWidget, self).__init__(parent)
- layout = QHBoxLayout ()
- self.show_text = show_text
- if self.show_text:
- self.label=QLabel("")
- layout.addWidget(self.label, alignment=Qt.AlignCenter)
- else:
- self.icon = QLabel("")
- layout.addWidget(self.icon, alignment=Qt.AlignCenter)
- self.setLayout(layout)
- self.previous_outdated = False
- self.previous_update_recommended = None
- self.value = None
- self.tableItem = VmUpdateInfoWidget.VmUpdateInfoItem(self.value, vm)
- def update_outdated(self, vm):
- if vm.type == "HVM":
- return
- outdated = vm.is_outdated()
- if outdated and not self.previous_outdated:
- self.update_status_widget("outdated")
- elif not outdated and self.previous_outdated:
- self.update_status_widget(None)
- self.previous_outdated = outdated
- if not vm.is_updateable():
- return
- if vm.qid == 0:
- update_recommended = self.previous_update_recommended
- if os.path.exists(qubes_dom0_updates_stat_file):
- update_recommended = True
- else:
- update_recommended = False
- else:
- update_recommended = self.previous_update_recommended
- stat_file_path = vm.dir_path + '/' + vm_files["updates_stat_file"]
- if not os.path.exists(stat_file_path):
- update_recommended = False
- else:
- if (not hasattr(vm, "updates_stat_file_read_time")) or vm.updates_stat_file_read_time <= os.path.getmtime(stat_file_path):
- stat_file = open(stat_file_path, "r")
- updates = stat_file.read().strip()
- stat_file.close()
- if updates.isdigit():
- updates = int(updates)
- else:
- updates = 0
- if updates == 0:
- update_recommended = False
- else:
- update_recommended = True
- vm.updates_stat_file_read_time = time.time()
- if update_recommended and not self.previous_update_recommended:
- self.update_status_widget("update")
- elif self.previous_update_recommended and not update_recommended:
- self.update_status_widget(None)
- self.previous_update_recommended = update_recommended
- def update_status_widget(self, state):
- self.value = state
- self.tableItem.set_value(state)
- if state == "update":
- label_text = "<font color=\"#CCCC00\">Check updates</font>"
- icon_path = ":/update-recommended.png"
- tooltip_text = "Updates pending!"
- elif state == "outdated":
- label_text = "<font color=\"red\">VM outdated</font>"
- icon_path = ":/outdated.png"
- tooltip_text = "The VM must be restarted for its filesystem to reflect the template's recent changes."
- elif state == None:
- label_text = ""
- icon_path = None
- tooltip_text = None
- if self.show_text:
- self.label.setText(label_text)
- else:
- self.layout().removeWidget(self.icon)
- self.icon.deleteLater()
- if icon_path != None:
- self.icon = VmIconWidget(icon_path, True, 0.7)
- self.icon.setToolTip(tooltip_text)
- else:
- self.icon = QLabel(label_text)
- self.layout().addWidget(self.icon, alignment=Qt.AlignCenter)
- class VmSizeOnDiskItem (QTableWidgetItem):
- def __init__(self, vm):
- super(VmSizeOnDiskItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.vm = vm
- self.value = 0
- self.update()
- self.setTextAlignment(Qt.AlignVCenter)
- def update(self):
- if self.vm.qid == 0:
- self.setText("n/a")
- else:
- self.value = self.vm.get_disk_utilization()/(1024*1024)
- self.setText( str(self.value) + " MiB")
- def __lt__(self, other):
- if self.value == other.value:
- return self.vm.qid < other.vm.qid
- else:
- return self.value < other.value
- class VmIPItem(QTableWidgetItem):
- def __init__(self, vm):
- super(VmIPItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.ip = vm.ip
- if self.ip:
- self.setText(self.ip)
- else:
- self.setText("n/a")
- class VmIncludeInBackupsItem(QTableWidgetItem):
- def __init__(self, vm):
- super(VmIncludeInBackupsItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.vm = vm
- if self.vm.include_in_backups:
- self.setText("Yes")
- else:
- self.setText("")
- def __lt__(self, other):
- if self.vm.include_in_backups == other.vm.include_in_backups:
- return self.vm.qid < other.vm.qid
- else:
- return self.vm.include_in_backups < other.vm.include_in_backups
- class VmLastBackupItem(QTableWidgetItem):
- def __init__(self, vm):
- super(VmLastBackupItem, self).__init__()
- self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
- self.vm = vm
- if self.vm.backup_timestamp:
- self.setText(str(self.vm.backup_timestamp.date()))
- else:
- self.setText("")
- def __lt__(self, other):
- if self.vm.backup_timestamp == other.vm.backup_timestamp:
- return self.vm.qid < other.vm.qid
- elif not self.vm.backup_timestamp:
- return False
- elif not other.vm.backup_timestamp:
- return True
- else:
- return self.vm.backup_timestamp < other.vm.backup_timestamp
|