sliverstatus screen
authorsmbaker <smbaker@fc8clean.lan>
Mon, 17 Oct 2011 21:33:42 +0000 (14:33 -0700)
committersmbaker <smbaker@fc8clean.lan>
Mon, 17 Oct 2011 21:33:42 +0000 (14:33 -0700)
sface/mainwindow.py
sface/screens/statusscreen.py [new file with mode: 0644]
sface/sfidata.py
sface/sfiprocess.py

index 51ec9e2..dc0e6a0 100644 (file)
@@ -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.
 
     # 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:
     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 (file)
index 0000000..1bc3469
--- /dev/null
@@ -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("<font color='green'>Sliver status refreshed.</font>", timeout=5000)
+        else:
+            self.setStatus("<font color='red'>Sliver status failed: %s</font>" % (faultString))
+
+        self.updateView()
+
+    def setStatus(self, msg, timeout=None):
+        self.parent().setStatus(msg, timeout)
+
+    def checkRunningProcess(self):
+        if self.process.isRunning():
+            self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
+            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("<font color='red'>Slice not set yet!</font>")
+            return
+
+        if self.process.isRunning():
+            self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
+            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
+
index fb98ac5..5de402a 100644 (file)
@@ -1,4 +1,5 @@
 import os
 import os
+import pickle
 from PyQt4.QtCore import *
 from PyQt4.QtGui import *
 
 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
             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
+
+
index 872fa5a..3c14718 100644 (file)
@@ -1,5 +1,6 @@
 
 import os
 
 import os
+import pickle
 import sys
 import time
 
 import sys
 import time
 
@@ -216,6 +217,20 @@ class SfiProcess(QObject):
         self.__init_command(args)
         self.start()
 
         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 = []
     def start(self):
         self.respones = []
         self.faults = []