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