sort the sliverstatus screen
[sface.git] / sface / screens / statusscreen.py
1
2 import datetime
3 import os
4 from PyQt4.QtCore import *
5 from PyQt4.QtGui import *
6
7 from sface.config import config
8 from sface.sfirenew import RenewWindow
9 from sface.sfiprocess import SfiProcess
10 from sface.screens.sfascreen import SfaScreen
11 from sface.sfidata import SfiData
12
13 NAME_COLUMN = 0
14 GENI_STATUS_COLUMN = 1
15
16 class StatusView(QTreeView):
17     def __init__(self, parent):
18         QTreeView.__init__(self, parent)
19
20         self.setAnimated(True)
21         self.setItemsExpandable(True)
22         self.setRootIsDecorated(True)
23         self.setAlternatingRowColors(True)
24         self.setAttribute(Qt.WA_MacShowFocusRect, 0)
25         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
26         self.setToolTip("Double click on a row to change its status.  Right click on a host to add a tag.")
27
28 class StatusFilterProxyModel(QSortFilterProxyModel):
29     def __init__(self, parent=None):
30         QSortFilterProxyModel.__init__(self, parent)
31         self.hostname_filter_regex = None
32         self.geni_status_filter = None
33
34     def setHostNameFilter(self, hostname):
35         self.hostname_filter_regex = QRegExp(hostname)
36         self.invalidateFilter()
37
38     def setNodeStatusFilter(self, status):
39         if (status == "all"):
40             self.geni_status_filter = None
41         else:
42             self.geni_status_filter = status
43         self.invalidateFilter()
44
45     def filterAcceptsRow(self, sourceRow, source_parent):
46         if self.hostname_filter_regex:
47             name_data = self.sourceModel().index(sourceRow, NAME_COLUMN, source_parent).data().toString()
48             if (self.hostname_filter_regex.indexIn(name_data) < 0):
49                 return False
50         if self.geni_status_filter:
51             geni_status_data = self.sourceModel().index(sourceRow, GENI_STATUS_COLUMN, source_parent).data().toString()
52             if (geni_status_data != self.geni_status_filter):
53                 return False
54         return True
55
56 class StatusWidget(QWidget):
57     def __init__(self, parent):
58         QWidget.__init__(self, parent)
59
60         self.process = SfiProcess(self)
61
62         self.slicename = QLabel("", self)
63         self.updateSliceName()
64         self.slicename.setScaledContents(False)
65         filterlabel = QLabel ("Filter: ", self)
66         filterbox = QComboBox(self)
67         filterbox.addItems(["all", "ready", "failed"])
68         searchlabel = QLabel ("Search: ", self)
69         searchlabel.setScaledContents(False)
70         searchbox = QLineEdit(self)
71         searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
72
73         self.urn = QLabel("", self)
74         self.login = QLabel("", self)
75
76         toplayout1 = QHBoxLayout()
77         toplayout1.addWidget(self.slicename, 0, Qt.AlignLeft)
78         toplayout1.addStretch()
79         toplayout1.addWidget(filterlabel, 0, Qt.AlignRight)
80         toplayout1.addWidget(filterbox, 0, Qt.AlignRight)
81         toplayout1.addWidget(searchlabel, 0, Qt.AlignRight)
82         toplayout1.addWidget(searchbox, 0, Qt.AlignRight)
83
84         toplayout2 = QHBoxLayout()
85         toplayout2.addWidget(self.urn, 0, Qt.AlignRight)
86         toplayout2.addWidget(self.login, 0, Qt.AlignRight)
87         toplayout2.addStretch()
88
89         toplayout = QVBoxLayout()
90         toplayout.addLayout(toplayout1)
91         toplayout.addLayout(toplayout2)
92
93         self.statusView = StatusView(self)
94         self.statusModel = QStandardItemModel(0, 4, self)
95         self.filterModel = StatusFilterProxyModel(self)
96
97         refresh = QPushButton("Refresh", self)
98         refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
99
100         bottomlayout = QHBoxLayout()
101         bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
102         bottomlayout.addStretch()
103
104         layout = QVBoxLayout()
105         layout.addLayout(toplayout)
106         layout.addWidget(self.statusView)
107         layout.addLayout(bottomlayout)
108         self.setLayout(layout)
109         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
110
111         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
112         self.connect(searchbox, SIGNAL('textChanged(QString)'), self.search)
113         self.connect(filterbox, SIGNAL('currentIndexChanged(QString)'), self.filter)
114
115         self.updateView()
116
117     def sliverStatusFinished(self):
118         self.disconnect(self.process, SIGNAL('finished()'), self.sliverStatusFinished)
119
120         faultString = self.process.getFaultString()
121         if not faultString:
122             self.setStatus("<font color='green'>Sliver status refreshed.</font>", timeout=5000)
123         else:
124             self.setStatus("<font color='red'>Sliver status failed: %s</font>" % (faultString))
125
126         self.updateView()
127
128     def setStatus(self, msg, timeout=None):
129         self.parent().setStatus(msg, timeout)
130
131     def checkRunningProcess(self):
132         if self.process.isRunning():
133             self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
134             return True
135         return False
136
137     def search(self, search_string):
138         self.filterModel.setHostNameFilter(str(search_string))
139
140     def filter(self, filter_string):
141         self.filterModel.setNodeStatusFilter(str(filter_string))
142
143     def itemStatus(self, item):
144         statusItem = item.parent().child(item.row(), MEMBERSHIP_STATUS_COLUMN)
145         return str(statusItem.data(Qt.DisplayRole).toString())
146
147     def itemText(self, item):
148         return str(item.data(Qt.DisplayRole).toString())
149
150     def refresh(self):
151         if not config.getSlice():
152             self.setStatus("<font color='red'>Slice not set yet!</font>")
153             return
154
155         if self.process.isRunning():
156             self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
157             return
158
159         self.connect(self.process, SIGNAL('finished()'), self.sliverStatusFinished)
160
161         self.process.sliverStatus()
162         self.setStatus("Refreshing Sliver Status. This will take some time...")
163
164     def updateView(self):
165         self.statusModel.clear()
166
167         sliverStatus = SfiData().getSliverStatus()
168         if not sliverStatus:
169             return None
170
171         geni_urn = sliverStatus.get("geni_urn","")
172         self.urn.setText("URN: " + geni_urn)
173
174         login = sliverStatus.get("pl_login","")
175         self.login.setText("Login: " + login)
176
177         rootItem = self.statusModel.invisibleRootItem()
178
179         resources = sliverStatus.get("geni_resources", [])
180
181         for resource in resources:
182             # try to pick the most useful thing
183             name = resource.get("pl_hostname","")
184             if not name:
185                 name = resource.get("geni_urn","")
186
187             if not name:
188                 continue
189
190             status = resource.get("geni_status","")
191             error = resource.get("error", "")
192             lastContact = resource.get("pl_last_contact", "")
193
194             # some of these return None, and we need to convert that back to
195             # an empty string
196             if not status:
197                 status = ""
198             if not error:
199                 error = ""
200             if not lastContact:
201                 lastContact = ""
202
203             nameItem = QStandardItem(QString(name))
204             statusItem = QStandardItem(QString(status))
205             errorItem = QStandardItem(QString(error))
206             lastContact = QStandardItem(QString(lastContact))
207             rootItem.appendRow([nameItem, statusItem, errorItem, lastContact])
208
209         self.filterModel.setSourceModel(self.statusModel)
210         self.filterModel.setDynamicSortFilter(True)
211         self.filterModel.sort(NAME_COLUMN)
212
213         headers = QStringList() << "URN" << "Status" << "Error" << "Last Contact"
214         self.statusModel.setHorizontalHeaderLabels(headers)
215
216         self.statusView.setModel(self.filterModel)
217         self.statusView.expandAll()
218         self.statusView.resizeColumnToContents(0)
219         self.statusView.collapseAll()
220
221     def updateSliceName(self):
222         self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
223
224 class StatusScreen(SfaScreen):
225     def __init__(self, parent):
226         SfaScreen.__init__(self, parent)
227
228         slice = StatusWidget(self)
229         self.init(slice, "Status", "Sliver Status")
230
231     def rspecUpdated(self):
232         pass
233
234     def configurationChanged(self):
235         self.widget.updateSliceName()
236         self.widget.updateView()
237
238     def nodeSelectionChanged(self, hostname):
239         pass
240