5 from PyQt4.QtCore import *
6 from PyQt4.QtGui import *
8 #from sfa.util.rspecHelper import RSpec
9 from sfa.util.xrn import Xrn
10 from sface.config import config
11 from sface.sfirenew import RenewWindow
12 from sface.sfiprocess import SfiProcess
13 from sface.screens.sfascreen import SfaScreen
14 from sface.sfidata import SfiData
16 from sface.clislicemgr import ClientSliceManager
20 node_status = { "in": "Already Selected",
21 "out": "Not Selected",
23 "remove": "To be Removed"}
25 tag_status = { "in": "Already Set",
28 "remove": "To be Removed"}
30 color_status = { "in": QColor.fromRgb(0, 250, 250),
31 "add": QColor.fromRgb(0, 250, 0),
32 "remove": QColor.fromRgb(250, 0, 0) }
34 default_tags = "Default tags"
35 settable_tags = ['delegations', 'initscript']
39 NODE_STATUS_COLUMN = 2
40 MEMBERSHIP_STATUS_COLUMN = 3
43 # maximum length of a name to display before clipping
47 if index.parent().parent().isValid():
53 class NodeView(QTreeView):
54 def __init__(self, parent):
55 QTreeView.__init__(self, parent)
57 self.setAnimated(True)
58 self.setItemsExpandable(True)
59 self.setRootIsDecorated(True)
60 self.setAlternatingRowColors(True)
61 # self.setSelectionMode(self.MultiSelection)
62 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
63 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
64 self.setToolTip("Double click on a row to change its status. Right click on a host to add a tag.")
66 def keyPressEvent(self, event):
67 if (event.key() == Qt.Key_Space):
68 self.toggleSelection()
70 QTreeView.keyPressEvent(self, event)
72 def mouseDoubleClickEvent(self, event):
73 self.toggleSelection()
75 def toggleSelection(self):
76 index = self.currentIndex()
80 # probably no rspec downloaded yet
83 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
84 status_data = status_index.data().toString()
85 node_index = model.index(index.row(), NAME_COLUMN, index.parent())
86 node_data = node_index.data().toString()
88 if itemType(node_index) == "tag":
89 data = node_index.data().toString()
90 tagname, value = data.split(": ")
91 if tagname not in settable_tags:
93 QMessageBox.warning(self, "Not settable", "Insufficient permission to change '%s' tag" % tagname)
95 if status_data == tag_status['in']:
96 model.setData(status_index, QString(tag_status['remove']))
97 elif status_data == tag_status['add']:
98 model.setData(status_index, QString(tag_status['out']))
99 elif status_data == tag_status['remove']:
100 model.setData(status_index, QString(tag_status['in']))
101 else: model.setData(status_index, QString(node_status['out']))
104 if status_data == node_status['in']:
105 model.setData(status_index, QString(node_status['remove']))
106 elif status_data == node_status['out']:
107 model.setData(status_index, QString(node_status['add']))
108 elif status_data in (node_status['add'], node_status['remove']):
109 if node_data in already_in_nodes: model.setData(status_index, QString(node_status['in']))
110 else: model.setData(status_index, QString(node_status['out']))
112 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
114 def appendRow(self, parent, name, nodeStatus="", nodeType="", membership="", kind = ""):
115 # row: name nodeStatus nodeType membership kind
116 item = QStandardItem(QString(str(name)))
118 QStandardItem(QString(str(nodeType))),
119 QStandardItem(QString(str(nodeStatus))),
120 QStandardItem(QString(str(membership))),
121 QStandardItem(QString(str(kind)))]
122 parent.appendRow(row)
125 def mousePressEvent(self, event):
126 QTreeView.mousePressEvent(self, event)
127 if event.button() == Qt.LeftButton:
131 index = self.currentIndex()
132 model = index.model()
135 # probably no rspec downloaded yet
138 status_index = model.index(index.row(), 1, index.parent())
139 status_data = status_index.data().toString()
140 node_index = model.index(index.row(), 0, index.parent())
141 node_data = node_index.data().toString()
143 if itemType(node_index) == "node":
145 if status_data in (node_status['in'], node_status['add'], ""):
146 # Pop up a dialog box for adding a new attribute
147 tagname, ok = QInputDialog.getItem(self, "Add tag",
148 "Tag name:", settable_tags)
150 value, ok = QInputDialog.getText(self, "Add tag",
151 "Value for tag '%s'" % tagname)
153 # We're using the QSortFilterProxyModel here
154 src_index = model.mapToSource(index)
155 src_model = src_index.model()
156 nodeItem = src_model.itemFromIndex(src_index)
158 self.appendRow(nodeItem, "%s: %s" % (tagname, value), membership=tag_status['add'], kind="attribute")
160 elif status_data in (node_status['out'], node_status['remove']):
161 QMessageBox.warning(self, "Not selected", "Can only add tags to selected nodes")
164 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
166 def currentChanged(self, current, previous):
167 model = current.model()
168 node_index = model.index(current.row(), 0, current.parent())
169 node_data = node_index.data().toString()
170 self.emit(SIGNAL('hostnameClicked(QString)'), node_data)
174 class NodeNameDelegate(QStyledItemDelegate):
175 def __init__(self, parent):
176 QStyledItemDelegate.__init__(self, parent)
178 def displayText(self, value, locale):
179 data = str(QStyledItemDelegate.displayText(self, value, locale))
180 if (len(data)>NAME_MAX_LEN):
181 data = data[:(NAME_MAX_LEN-3)] + "..."
184 def paint(self, painter, option, index):
185 model = index.model()
186 data = str(self.displayText(index.data(), QLocale()))
187 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
188 status_data = status_index.data().toString()
190 fm = QFontMetrics(option.font)
191 rect = QRect(option.rect)
193 rect.setHeight(rect.height() - 2)
194 rect.setWidth(fm.width(QString(data)) + 6)
195 rect.setX(rect.x() + 5)
196 rect.setY(rect.y() - 1)
198 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
200 path = QPainterPath()
201 path.addRoundedRect(x - 1, y + 1, w, h, 4, 4)
204 painter.setRenderHint(QPainter.Antialiasing)
206 if option.state & QStyle.State_Selected:
207 painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight))
209 if itemType(index) == "node":
210 for x in node_status.keys():
211 if (node_status[x] == status_data) and (x in color_status):
212 painter.fillPath(path, color_status[x])
214 painter.setPen(QColor.fromRgb(0, 0, 0))
215 painter.drawText(rect, 0, QString(data))
218 for x in tag_status.keys():
219 if (tag_status[x] == status_data) and (x in color_status):
220 painter.fillPath(path, color_status[x])
222 painter.setPen(QColor.fromRgb(0, 0, 0))
223 painter.drawText(rect, 0, QString(data))
227 class NodeStatusDelegate(QStyledItemDelegate):
228 def __init__(self, parent):
229 QStyledItemDelegate.__init__(self, parent)
231 def paint(self, painter, option, index):
232 model = index.model()
233 nodestatus_index = model.index(index.row(), NODE_STATUS_COLUMN, index.parent())
234 nodestatus_data = nodestatus_index.data().toString()
236 fm = QFontMetrics(option.font)
237 rect = QRect(option.rect)
239 data = index.data().toString()
240 rect.setHeight(rect.height() - 2)
241 rect.setWidth(fm.width(QString(data)) + 6)
242 rect.setX(rect.x() + 5)
243 rect.setY(rect.y() - 1)
245 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
247 path = QPainterPath()
248 path.addRoundedRect(x - 1, y + 1, w, h, 4, 4)
251 painter.setRenderHint(QPainter.Antialiasing)
253 if option.state & QStyle.State_Selected:
254 painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight))
256 if (nodestatus_data == ""):
257 painter.setPen(QColor.fromRgb(0, 0, 0))
258 painter.drawText(rect, 0, QString(data))
259 elif (nodestatus_data == "boot"):
260 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
261 painter.setPen(QColor.fromRgb(0, 0, 0))
262 painter.drawText(rect, 0, QString(data))
264 painter.fillPath(path, QColor.fromRgb(250, 0, 0))
265 painter.setPen(QColor.fromRgb(0, 0, 0))
266 painter.drawText(rect, 0, QString(data))
270 class NodeFilterProxyModel(QSortFilterProxyModel):
271 def __init__(self, parent=None):
272 QSortFilterProxyModel.__init__(self, parent)
273 self.hostname_filter_regex = None
274 self.nodestatus_filter = None
276 def setHostNameFilter(self, hostname):
277 self.hostname_filter_regex = QRegExp(hostname)
278 self.invalidateFilter()
280 def setNodeStatusFilter(self, status):
281 if (status == "all"):
282 self.nodestatus_filter = None
284 self.nodestatus_filter = status
285 self.invalidateFilter()
287 def filterAcceptsRow(self, sourceRow, source_parent):
288 kind_data = self.sourceModel().index(sourceRow, KIND_COLUMN, source_parent).data().toString()
289 if (kind_data == "node"):
290 if self.hostname_filter_regex:
291 name_data = self.sourceModel().index(sourceRow, NAME_COLUMN, source_parent).data().toString()
292 if (self.hostname_filter_regex.indexIn(name_data) < 0):
294 if self.nodestatus_filter:
295 nodestatus_data = self.sourceModel().index(sourceRow, NODE_STATUS_COLUMN, source_parent).data().toString()
296 if (nodestatus_data != self.nodestatus_filter):
300 def lessThan(self, left, right):
301 l_str = str(left.data().toString())
302 r_str = str(right.data().toString())
304 # make sure default_tags appears before everything else
305 if l_str.startswith(default_tags):
308 if r_str.startswith(default_tags):
311 return (l_str < r_str)
314 class SliceWidget(QWidget):
315 def __init__(self, parent):
316 QWidget.__init__(self, parent)
318 self.network_names = []
319 self.process = SfiProcess(self)
321 self.slicename = QLabel("", self)
322 self.updateSliceName()
323 self.slicename.setScaledContents(False)
324 filterlabel = QLabel ("Filter: ", self)
325 filterbox = QComboBox(self)
326 filterbox.addItems(["all", "boot", "disabled", "reinstall", "safeboot"])
327 searchlabel = QLabel ("Search: ", self)
328 searchlabel.setScaledContents(False)
329 searchbox = QLineEdit(self)
330 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
332 toplayout = QHBoxLayout()
333 toplayout.addWidget(self.slicename, 0, Qt.AlignLeft)
334 toplayout.addStretch()
335 toplayout.addWidget(filterlabel, 0, Qt.AlignRight)
336 toplayout.addWidget(filterbox, 0, Qt.AlignRight)
337 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
338 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
340 self.nodeView = NodeView(self)
341 self.nodeModel = QStandardItemModel(0, 4, self)
342 self.filterModel = NodeFilterProxyModel(self)
344 self.nodeNameDelegate = NodeNameDelegate(self)
345 self.nodeStatusDelegate = NodeStatusDelegate(self)
347 refresh = QPushButton("Refresh Slice Data", self)
348 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
349 renew = QPushButton("Renew Slice", self)
350 renew.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
351 submit = QPushButton("Submit", self)
352 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
354 bottomlayout = QHBoxLayout()
355 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
356 bottomlayout.addWidget(renew, 0, Qt.AlignLeft)
357 bottomlayout.addStretch()
358 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
360 layout = QVBoxLayout()
361 layout.addLayout(toplayout)
362 layout.addWidget(self.nodeView)
363 layout.addLayout(bottomlayout)
364 self.setLayout(layout)
365 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
367 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
368 self.connect(renew, SIGNAL('clicked()'), self.renew)
369 self.connect(submit, SIGNAL('clicked()'), self.submit) # _pg_compat)
370 self.connect(searchbox, SIGNAL('textChanged(QString)'), self.search)
371 self.connect(filterbox, SIGNAL('currentIndexChanged(QString)'), self.filter)
372 self.connect(self.nodeView, SIGNAL('hostnameClicked(QString)'),
373 self.nodeSelectionChanged)
377 def submitFinished(self):
378 self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
380 faultString = self.process.getFaultString()
382 self.setStatus("<font color='green'>Slice data submitted.</font>")
384 self.setStatus("<font color='red'>Slice submit failed: %s</font>" % (faultString))
387 self.parent().signalAll("rspecUpdated")
389 def deleteSliversFinished(self):
390 self.disconnect(self.process, SIGNAL('finished()'), self.deleteSliversFinished)
392 faultString = self.process.getFaultString()
394 self.setStatus("<font color='green'>Slice data submitted.</font>")
395 QTimer.singleShot(2500, self.refresh)
397 self.setStatus("<font color='red'>Slice submit failed: %s</font>" % (faultString))
399 def refreshResourcesFinished(self):
400 self.disconnect(self.process, SIGNAL('finished()'), self.refreshResourcesFinished)
402 faultString = self.process.getFaultString()
404 self.setStatus("Refreshing slice RSpec.")
405 self.connect(self.process, SIGNAL('finished()'), self.refreshRSpecFinished)
406 self.process.retrieveRspec()
408 self.setStatus("<font color='red'>Resources refresh failed: %s</font>" % (faultString))
410 def refreshRSpecFinished(self):
411 self.disconnect(self.process, SIGNAL('finished()'), self.refreshRSpecFinished)
413 faultString = self.process.getFaultString()
415 self.setStatus("<font color='green'>Slice data refreshed.</font>", timeout=5000)
417 self.setStatus("<font color='red'>Slice refresh failed: %s</font>" % (faultString))
420 self.parent().signalAll("rspecUpdated")
422 def setStatus(self, msg, timeout=None):
423 self.parent().setStatus(msg, timeout)
425 def checkRunningProcess(self):
426 if self.process.isRunning():
427 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
431 def search(self, search_string):
432 self.filterModel.setHostNameFilter(str(search_string))
434 def filter(self, filter_string):
435 self.filterModel.setNodeStatusFilter(str(filter_string))
437 def itemStatus(self, item):
438 statusItem = item.parent().child(item.row(), MEMBERSHIP_STATUS_COLUMN)
439 return str(statusItem.data(Qt.DisplayRole).toString())
441 def itemText(self, item):
442 return str(item.data(Qt.DisplayRole).toString())
444 # Recursively walk the tree, making changes to the RSpec
445 def process_subtree(self, rspec, rspec_node_names, resource_node_names, item, depth = 0):
450 model = self.nodeModel
454 elif depth == 2: # Hostname
455 hostname = self.itemText(item)
456 testbed = self.itemText(item.parent())
457 status = self.itemStatus(item)
458 if status == node_status['add']:
459 print "Add hostname: %s" % hostname
461 resource_node = resource_node_names.get(hostname, None)
463 if resource_node==None:
464 print "Error: Failed to find %s in resources rspec" % hostname
466 if not (hostname in rspec_node_names):
467 network_name = Xrn(resource_node['component_manager_id']).get_hrn()
468 rspec.version.add_network(network_name)
469 rspec.version.add_nodes([resource_node])
470 rspec.version.add_slivers([str(hostname)])
472 elif status == node_status['remove']:
473 print "Remove hostname: %s" % hostname
474 rspec.version.remove_slivers([str(hostname)])
476 elif depth == 3: # Tag
477 tag, value = self.itemText(item).split(": ")
478 status = self.itemStatus(item)
479 tag = "%s" % tag # Prevent weird error from lxml
480 value = "%s" % value # Prevent weird error from lxml
481 hostname = self.itemText(item.parent())
482 testbed = self.itemText(item.parent().parent())
483 if status == tag_status['add']:
484 print "Add tag to (%s, %s): %s/%s " % (testbed, hostname, tag, value)
485 if hostname.startswith(default_tags):
486 rspec.version.add_default_sliver_attribute(tag, value, testbed)
488 node = rspec_node_names.get(hostname, None)
490 rspec.version.add_sliver_attribute(node['component_id'], tag, value, testbed)
492 elif status == tag_status['remove']:
493 print "Remove tag from (%s, %s): %s/%s " % (testbed, hostname, tag, value)
494 if hostname.startswith(default_tags):
495 rspec.version.remove_default_sliver_attribute(tag, value, testbed)
497 node = rspec_node_names.get(hostname, None)
499 rspec.version.remove_sliver_attribute(node['component_id'], tag, value, testbed)
502 children = item.rowCount()
503 for row in range(0, children):
504 (c, ca, cr, cat, crt) = self.process_subtree(rspec, rspec_node_names, resource_node_names, item.child(row), depth + 1)
505 changeAdd = changeAdd + ca
506 changeRemove = changeRemove + cr
507 changeAddTag = changeAddTag + cat
508 changeRemoveTag = changeRemoveTag + crt
510 change = changeAdd + changeRemove + changeAddTag + changeRemoveTag
512 return (change, changeAdd, changeRemove, changeAddTag, changeRemoveTag)
515 if self.checkRunningProcess():
518 rspec = SfiData().getSliceRSpec()
519 resources = SfiData().getResourcesRSpec()
521 resource_node_names = self.nodesByName(resources.version.get_nodes())
522 rspec_node_names = self.nodesByName(rspec.version.get_nodes_with_slivers())
524 (change, changeAdd, changeRemove, changeAddTag, changeRemoveTag) = \
525 self.process_subtree(rspec, rspec_node_names, resource_node_names, self.nodeModel.invisibleRootItem())
528 self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
531 if (changeRemove > 0) and (rspec.version.get_nodes_with_slivers() == []):
532 # removing the last sliver in a slice requires us to call deleteSlivers
533 self.connect(self.process, SIGNAL('finished()'), self.deleteSliversFinished)
534 self.process.deleteSlivers()
537 # Several aggregates have issues with the <statistics> section in the
538 # rspec, so make sure it's not there.
539 stats_elems = rspec.xml.xpath("//statistics")
540 if len(stats_elems)>0:
541 stats_elem = stats_elems[0]
542 parent = stats_elem.xpath("..")[0]
543 parent.remove(stats_elem)
545 self.connect(self.process, SIGNAL('finished()'), self.submitFinished)
546 self.process.applyRSpec(rspec)
547 self.setStatus("Sending slice data (RSpec). This will take some time...")
549 def submit_pg_compat(self):
550 if self.checkRunningProcess():
553 rspec = SfiData().getSliceRSpec()
554 resources = SfiData().getResourcesRSpec()
556 (change, changeAdd, changeRemove, changeAddTag, changeRemoveTag) = \
557 self.process_subtree(rspec, rspec, resources, self.nodeModel.invisibleRootItem())
560 self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
563 dlg = ClientSliceManager(self)
564 dlg.submit_pg_compat(rspec)
567 self.setStatus("<font color='green'>Finished submitting. %d/%d aggs succeeded.</font>" %
568 (dlg.submit_aggSuccessCount,dlg.submit_aggSuccessCount+dlg.submit_aggFailCount))
569 QTimer.singleShot(2500, self.refresh)
572 dlg = RenewWindow(parent=self)
576 if not config.getSlice():
577 self.setStatus("<font color='red'>Slice not set yet!</font>")
580 if self.process.isRunning():
581 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
584 self.connect(self.process, SIGNAL('finished()'), self.refreshResourcesFinished)
586 self.process.retrieveResources()
587 self.setStatus("Refreshing resources. This will take some time...")
589 def nodesByNetwork(self, nodeList):
591 for node in nodeList:
592 network_name = Xrn(node['component_manager_id']).get_hrn()
594 net = netDict.get(network_name, [])
596 netDict[network_name] = net
602 def nodesByName(self, nodeList, nameDict=None):
605 for node in nodeList:
606 hostname = node.get("component_name", None)
607 if hostname and (not hostname in nameDict):
608 nameDict[hostname] = node
612 def updateView(self):
613 global already_in_nodes
614 already_in_nodes = []
615 self.network_names = []
616 self.nodeModel.clear()
618 rspec = SfiData().getSliceRSpec()
622 resources = SfiData().getResourcesRSpec()
626 rootItem = self.nodeModel.invisibleRootItem()
629 for network in rspec.version.get_networks():
630 network_name = network.get("name", None)
631 if (network_name != None) and (not network_name in networks):
632 networks.append(network_name)
633 for network in resources.version.get_networks():
634 network_name = network.get("name", None)
635 if (network_name != None) and (not network_name in networks):
636 networks.append(network_name)
638 resources_nodes = self.nodesByNetwork(resources.version.get_nodes())
639 rspec_nodes = self.nodesByNetwork(rspec.version.get_nodes_with_slivers())
641 for network in networks:
642 self.network_names.append(network)
644 all_nodes = resources_nodes.get(network, [])
645 sliver_nodes = rspec_nodes.get(network, [])
647 sliver_node_names = self.nodesByName(sliver_nodes)
649 available_nodes = [ node for node in all_nodes if node["component_name"] not in sliver_node_names ]
651 msg = "%s Nodes\t%s Selected" % (len(all_nodes), len(sliver_nodes))
652 networkItem = self.nodeView.appendRow(rootItem, network, membership=msg, kind="network")
654 already_in_nodes += sliver_node_names.keys()
656 # Add default slice tags
657 nodeItem = self.nodeView.appendRow(networkItem, "%s for %s" % (default_tags, network), kind="defaults")
658 attrs = rspec.version.get_default_sliver_attributes(network)
660 name = attr.get("name", None)
661 value = attr.get("value", None)
662 tagstring = QString("%s: %s" % (name, value))
663 self.nodeView.appendRow(nodeItem, tagstring, membership=tag_status['in'], kind = "attribute")
665 for node in sliver_nodes:
667 if ("hardware_types" in node):
668 hardware_types = [x["name"] for x in node["hardware_types"]]
669 nodeType = ",".join(hardware_types)
670 nodeStatus = node.get("boot_state", "")
671 if nodeStatus == None:
673 nodeItem = self.nodeView.appendRow(networkItem,
674 node["component_name"],
675 nodeStatus=nodeStatus,
677 membership=node_status['in'],
680 attrs = rspec.version.get_sliver_attributes(node['component_id'], network)
682 name = attr.get("name", None)
683 value = attr.get("value", None)
684 self.nodeView.appendRow(nodeItem,
685 "%s: %s" % (name, value),
686 membership=tag_status['in'],
689 for node in available_nodes:
691 if ("hardware_types" in node):
692 hardware_types = [x["name"] for x in node["hardware_types"]]
693 nodeType = ",".join(hardware_types)
694 nodeStatus = node.get("boot_state", "")
695 if nodeStatus == None:
697 self.nodeView.appendRow(networkItem,
698 node["component_name"],
699 nodeStatus=nodeStatus,
701 membership=node_status['out'],
704 self.filterModel.setSourceModel(self.nodeModel)
705 self.filterModel.setDynamicSortFilter(True)
706 self.filterModel.sort(NAME_COLUMN)
708 headers = QStringList() << "Hostname or Tag" << "Node Type" << "Node Status" << "Membership Status" << "Kind"
709 self.nodeModel.setHorizontalHeaderLabels(headers)
711 self.nodeView.setItemDelegateForColumn(NAME_COLUMN, self.nodeNameDelegate)
712 self.nodeView.setItemDelegateForColumn(NODE_STATUS_COLUMN, self.nodeStatusDelegate)
713 self.nodeView.setModel(self.filterModel)
714 self.nodeView.hideColumn(KIND_COLUMN)
715 self.nodeView.expandAll()
716 self.nodeView.resizeColumnToContents(0)
717 self.nodeView.collapseAll()
719 def updateSliceName(self):
720 self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
722 def nodeSelectionChanged(self, hostname):
723 self.parent().nodeSelectionChanged(hostname)
725 class MainScreen(SfaScreen):
726 def __init__(self, parent):
727 SfaScreen.__init__(self, parent)
729 self.sliceWidget = SliceWidget(self)
730 self.init(self.sliceWidget, "Nodes", "OneLab SFA crawler")
732 def rspecUpdated(self):
733 self.mainwin.rspecWindow.updateView()
735 def configurationChanged(self):
736 self.widget.updateSliceName()
737 self.widget.updateView()
738 self.mainwin.rspecWindow.updateView()
740 def nodeSelectionChanged(self, hostname):
741 self.mainwin.nodeSelectionChanged(hostname)
743 def remoteSliceChanged(self):
744 # we're being notified the slice was changed remotely. Download a new
746 QTimer.singleShot(2500, self.sliceWidget.refresh)