From 96ec1c38a62466c4d69a57835d470fee98a39edc Mon Sep 17 00:00:00 2001 From: smbaker Date: Mon, 17 Oct 2011 14:33:42 -0700 Subject: [PATCH] sliverstatus screen --- sface/mainwindow.py | 2 +- sface/screens/statusscreen.py | 240 ++++++++++++++++++++++++++++++++++ sface/sfidata.py | 15 +++ sface/sfiprocess.py | 15 +++ 4 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 sface/screens/statusscreen.py diff --git a/sface/mainwindow.py b/sface/mainwindow.py index 51ec9e2..dc0e6a0 100644 --- a/sface/mainwindow.py +++ b/sface/mainwindow.py @@ -41,7 +41,7 @@ def load_screens(dirname): # we want the stock screens to show up in a specific order. plugins can # show up in any order afterward. - sort_order = ["mainscreen", "userscreen", "configscreen", "helpscreen"] + sort_order = ["mainscreen", "statusscreen", "userscreen", "configscreen", "helpscreen"] sort_order.reverse() for modname in sort_order: if modname in modnames: diff --git a/sface/screens/statusscreen.py b/sface/screens/statusscreen.py new file mode 100644 index 0000000..1bc3469 --- /dev/null +++ b/sface/screens/statusscreen.py @@ -0,0 +1,240 @@ + +import datetime +import os +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from sfa.rspecs.rspec_parser import parse_rspec +from sface.config import config +from sface.sfirenew import RenewWindow +from sface.sfiprocess import SfiProcess +from sface.screens.sfascreen import SfaScreen +from sface.sfidata import SfiData + +NAME_COLUMN = 0 +GENI_STATUS_COLUMN = 1 + +class StatusView(QTreeView): + def __init__(self, parent): + QTreeView.__init__(self, parent) + + self.setAnimated(True) + self.setItemsExpandable(True) + self.setRootIsDecorated(True) + self.setAlternatingRowColors(True) + self.setAttribute(Qt.WA_MacShowFocusRect, 0) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.setToolTip("Double click on a row to change its status. Right click on a host to add a tag.") + +class StatusFilterProxyModel(QSortFilterProxyModel): + def __init__(self, parent=None): + QSortFilterProxyModel.__init__(self, parent) + self.hostname_filter_regex = None + self.geni_status_filter = None + + def setHostNameFilter(self, hostname): + self.hostname_filter_regex = QRegExp(hostname) + self.invalidateFilter() + + def setNodeStatusFilter(self, status): + if (status == "all"): + self.geni_status_filter = None + else: + self.geni_status_filter = status + self.invalidateFilter() + + def filterAcceptsRow(self, sourceRow, source_parent): + if self.hostname_filter_regex: + name_data = self.sourceModel().index(sourceRow, NAME_COLUMN, source_parent).data().toString() + if (self.hostname_filter_regex.indexIn(name_data) < 0): + return False + if self.geni_status_filter: + geni_status_data = self.sourceModel().index(sourceRow, GENI_STATUS_COLUMN, source_parent).data().toString() + if (geni_status_data != self.geni_status_filter): + return False + return True + +class StatusWidget(QWidget): + def __init__(self, parent): + QWidget.__init__(self, parent) + + self.process = SfiProcess(self) + + self.slicename = QLabel("", self) + self.updateSliceName() + self.slicename.setScaledContents(False) + filterlabel = QLabel ("Filter: ", self) + filterbox = QComboBox(self) + filterbox.addItems(["all", "ready", "failed"]) + searchlabel = QLabel ("Search: ", self) + searchlabel.setScaledContents(False) + searchbox = QLineEdit(self) + searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0) + + self.urn = QLabel("", self) + self.login = QLabel("", self) + + toplayout1 = QHBoxLayout() + toplayout1.addWidget(self.slicename, 0, Qt.AlignLeft) + toplayout1.addStretch() + toplayout1.addWidget(filterlabel, 0, Qt.AlignRight) + toplayout1.addWidget(filterbox, 0, Qt.AlignRight) + toplayout1.addWidget(searchlabel, 0, Qt.AlignRight) + toplayout1.addWidget(searchbox, 0, Qt.AlignRight) + + toplayout2 = QHBoxLayout() + toplayout2.addWidget(self.urn, 0, Qt.AlignRight) + toplayout2.addWidget(self.login, 0, Qt.AlignRight) + toplayout2.addStretch() + + toplayout = QVBoxLayout() + toplayout.addLayout(toplayout1) + toplayout.addLayout(toplayout2) + + self.statusView = StatusView(self) + self.statusModel = QStandardItemModel(0, 4, self) + self.filterModel = StatusFilterProxyModel(self) + + refresh = QPushButton("Refresh", self) + refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) + + bottomlayout = QHBoxLayout() + bottomlayout.addWidget(refresh, 0, Qt.AlignLeft) + bottomlayout.addStretch() + + layout = QVBoxLayout() + layout.addLayout(toplayout) + layout.addWidget(self.statusView) + layout.addLayout(bottomlayout) + self.setLayout(layout) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + self.connect(refresh, SIGNAL('clicked()'), self.refresh) + self.connect(searchbox, SIGNAL('textChanged(QString)'), self.search) + self.connect(filterbox, SIGNAL('currentIndexChanged(QString)'), self.filter) + + self.updateView() + + def sliverStatusFinished(self): + self.disconnect(self.process, SIGNAL('finished()'), self.sliverStatusFinished) + + faultString = self.process.getFaultString() + if not faultString: + self.setStatus("Sliver status refreshed.", timeout=5000) + else: + self.setStatus("Sliver status failed: %s" % (faultString)) + + self.updateView() + + def setStatus(self, msg, timeout=None): + self.parent().setStatus(msg, timeout) + + def checkRunningProcess(self): + if self.process.isRunning(): + self.setStatus("There is already a process running. Please wait.") + return True + return False + + def search(self, search_string): + self.filterModel.setHostNameFilter(str(search_string)) + + def filter(self, filter_string): + self.filterModel.setNodeStatusFilter(str(filter_string)) + + def itemStatus(self, item): + statusItem = item.parent().child(item.row(), MEMBERSHIP_STATUS_COLUMN) + return str(statusItem.data(Qt.DisplayRole).toString()) + + def itemText(self, item): + return str(item.data(Qt.DisplayRole).toString()) + + def refresh(self): + if not config.getSlice(): + self.setStatus("Slice not set yet!") + return + + if self.process.isRunning(): + self.setStatus("There is already a process running. Please wait.") + return + + self.connect(self.process, SIGNAL('finished()'), self.sliverStatusFinished) + + self.process.sliverStatus() + self.setStatus("Refreshing Sliver Status. This will take some time...") + + def updateView(self): + self.statusModel.clear() + + sliverStatus = SfiData().getSliverStatus() + if not sliverStatus: + return None + + geni_urn = sliverStatus.get("geni_urn","") + self.urn.setText("URN: " + geni_urn) + + login = sliverStatus.get("pl_login","") + self.login.setText("Login: " + login) + + rootItem = self.statusModel.invisibleRootItem() + + resources = sliverStatus.get("geni_resources", []) + + for resource in resources: + # try to pick the most useful thing + name = resource.get("pl_hostname","") + if not name: + name = resource.get("geni_urn","") + + if not name: + continue + + status = resource.get("geni_status","") + error = resource.get("error", "") + lastContact = resource.get("pl_last_contact", "") + + # some of these return None, and we need to convert that back to + # an empty string + if not status: + status = "" + if not error: + error = "" + if not lastContact: + lastContact = "" + + nameItem = QStandardItem(QString(name)) + statusItem = QStandardItem(QString(status)) + errorItem = QStandardItem(QString(error)) + lastContact = QStandardItem(QString(lastContact)) + rootItem.appendRow([nameItem, statusItem, errorItem, lastContact]) + + self.filterModel.setSourceModel(self.statusModel) + self.filterModel.setDynamicSortFilter(True) + + headers = QStringList() << "URN" << "Status" << "Error" << "Last Contact" + self.statusModel.setHorizontalHeaderLabels(headers) + + self.statusView.setModel(self.filterModel) + self.statusView.expandAll() + self.statusView.resizeColumnToContents(0) + self.statusView.collapseAll() + + def updateSliceName(self): + self.slicename.setText("Slice : %s" % (config.getSlice() or "None")) + +class StatusScreen(SfaScreen): + def __init__(self, parent): + SfaScreen.__init__(self, parent) + + slice = StatusWidget(self) + self.init(slice, "Status", "Sliver Status") + + def rspecUpdated(self): + pass + + def configurationChanged(self): + self.widget.updateSliceName() + self.widget.updateView() + + def nodeSelectionChanged(self, hostname): + pass + diff --git a/sface/sfidata.py b/sface/sfidata.py index fb98ac5..5de402a 100644 --- a/sface/sfidata.py +++ b/sface/sfidata.py @@ -1,4 +1,5 @@ import os +import pickle from PyQt4.QtCore import * from PyQt4.QtGui import * @@ -82,3 +83,17 @@ class SfiData: xml = open(rspec_file).read() return parse_rspec(xml) return None + + def getSliverStatus(self, slice=None): + if not slice: + slice = config.getSlice() + + filename = config.fullpath(slice+".sliverstatus") + + if os.path.exists(filename): + data = open(filename).read() + return pickle.loads(data) + + return None + + diff --git a/sface/sfiprocess.py b/sface/sfiprocess.py index 872fa5a..3c14718 100644 --- a/sface/sfiprocess.py +++ b/sface/sfiprocess.py @@ -1,5 +1,6 @@ import os +import pickle import sys import time @@ -216,6 +217,20 @@ class SfiProcess(QObject): self.__init_command(args) self.start() + def sliverStatus(self, slice=None, filename=None): + if not slice: + slice = config.getSlice() + + if not filename: + filename = config.fullpath(slice+".sliverstatus") + + args = ["-u", config.getUser(), "-a", config.getAuthority(), + "-r", config.getRegistry(), "-s", config.getSlicemgr(), "status", + "-o", filename, "-F", "pickled", slice] + self.__init_command(args) + self.start() + + def start(self): self.respones = [] self.faults = [] -- 2.43.0