support for hardware_type
[sface.git] / sface / screens / mainscreen.py
index 3c1be13..a435185 100644 (file)
@@ -6,12 +6,15 @@ from PyQt4.QtCore import *
 from PyQt4.QtGui import *
 
 #from sfa.util.rspecHelper import RSpec
 from PyQt4.QtGui import *
 
 #from sfa.util.rspecHelper import RSpec
+from sfa.util.xrn import Xrn
 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
 
 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
 
+from sface.clislicemgr import ClientSliceManager
+
 already_in_nodes = []
 
 node_status = { "in": "Already Selected",
 already_in_nodes = []
 
 node_status = { "in": "Already Selected",
@@ -32,9 +35,10 @@ default_tags = "Default tags"
 settable_tags = ['delegations', 'initscript']
 
 NAME_COLUMN = 0
 settable_tags = ['delegations', 'initscript']
 
 NAME_COLUMN = 0
-NODE_STATUS_COLUMN = 1
-MEMBERSHIP_STATUS_COLUMN = 2
-KIND_COLUMN = 3
+NODE_TYPE_COLUMN = 1
+NODE_STATUS_COLUMN = 2
+MEMBERSHIP_STATUS_COLUMN = 3
+KIND_COLUMN = 4
 
 # maximum length of a name to display before clipping
 NAME_MAX_LEN = 48
 
 # maximum length of a name to display before clipping
 NAME_MAX_LEN = 48
@@ -102,6 +106,17 @@ class NodeView(QTreeView):
 
         model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
 
 
         model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
 
+    def appendRow(self, parent, name, nodeStatus="", nodeType="", membership="", kind = ""):
+        # row: name nodeStatus nodeType membership kind
+        item = QStandardItem(QString(str(name)))
+        row = [item,
+                QStandardItem(QString(str(nodeType))),
+                QStandardItem(QString(str(nodeStatus))),
+                QStandardItem(QString(str(membership))),
+                QStandardItem(QString(str(kind)))]
+        parent.appendRow(row)
+        return item
+
     def mousePressEvent(self, event):
         QTreeView.mousePressEvent(self, event)
         if event.button() == Qt.LeftButton:
     def mousePressEvent(self, event):
         QTreeView.mousePressEvent(self, event)
         if event.button() == Qt.LeftButton:
@@ -125,23 +140,12 @@ class NodeView(QTreeView):
                     value, ok = QInputDialog.getText(self, "Add tag",
                                                      "Value for tag '%s'" % tagname)
                     if ok:
                     value, ok = QInputDialog.getText(self, "Add tag",
                                                      "Value for tag '%s'" % tagname)
                     if ok:
-                        # Add a new row to the model for the tag
-
-                        # For testing with the QStandardItemModel
-                        #nodeItem = model.itemFromIndex(index)
-                        #tagstring = QString("%s: %s" % (tagname, value))
-                        #tagItem = QStandardItem(tagstring)
-                        #status = QStandardItem(QString(tag_status['add']))
-                        #nodeItem.appendRow([tagItem, status])
-
                         # We're using the QSortFilterProxyModel here
                         src_index = model.mapToSource(index)
                         src_model = src_index.model()
                         nodeItem = src_model.itemFromIndex(src_index)
                         # We're using the QSortFilterProxyModel here
                         src_index = model.mapToSource(index)
                         src_model = src_index.model()
                         nodeItem = src_model.itemFromIndex(src_index)
-                        tagstring = QString("%s: %s" % (tagname, value))
-                        tagItem = QStandardItem(tagstring)
-                        status = QStandardItem(QString(tag_status['add']))
-                        nodeItem.appendRow([tagItem, QStandardItem(QString("")), status])
+
+                        self.appendRow(nodeItem, "%s: %s" % (tagname, value), membership=tag_status['add'], kind="attribute")
 
             elif status_data in (node_status['out'], node_status['remove']):
                 QMessageBox.warning(self, "Not selected", "Can only add tags to selected nodes")
 
             elif status_data in (node_status['out'], node_status['remove']):
                 QMessageBox.warning(self, "Not selected", "Can only add tags to selected nodes")
@@ -283,6 +287,20 @@ class NodeFilterProxyModel(QSortFilterProxyModel):
                     return False
         return True
 
                     return False
         return True
 
+    def lessThan(self, left, right):
+        l_str = str(left.data().toString())
+        r_str = str(right.data().toString())
+
+        # make sure default_tags appears before everything else
+        if l_str.startswith(default_tags):
+            return True
+
+        if r_str.startswith(default_tags):
+            return False
+
+        return (l_str < r_str)
+
+
 class SliceWidget(QWidget):
     def __init__(self, parent):
         QWidget.__init__(self, parent)
 class SliceWidget(QWidget):
     def __init__(self, parent):
         QWidget.__init__(self, parent)
@@ -338,7 +356,7 @@ class SliceWidget(QWidget):
 
         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
         self.connect(renew, SIGNAL('clicked()'), self.renew)
 
         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
         self.connect(renew, SIGNAL('clicked()'), self.renew)
-        self.connect(submit, SIGNAL('clicked()'), self.submit_pg_compat)
+        self.connect(submit, SIGNAL('clicked()'), self.submit) # _pg_compat)
         self.connect(searchbox, SIGNAL('textChanged(QString)'), self.search)
         self.connect(filterbox, SIGNAL('currentIndexChanged(QString)'), self.filter)
         self.connect(self.nodeView, SIGNAL('hostnameClicked(QString)'),
         self.connect(searchbox, SIGNAL('textChanged(QString)'), self.search)
         self.connect(filterbox, SIGNAL('currentIndexChanged(QString)'), self.filter)
         self.connect(self.nodeView, SIGNAL('hostnameClicked(QString)'),
@@ -404,7 +422,7 @@ class SliceWidget(QWidget):
         return str(item.data(Qt.DisplayRole).toString())
 
     # Recursively walk the tree, making changes to the RSpec
         return str(item.data(Qt.DisplayRole).toString())
 
     # Recursively walk the tree, making changes to the RSpec
-    def process_subtree(self, rspec, resources, item, depth = 0):
+    def process_subtree(self, rspec, rspec_node_names, resource_node_names, item, depth = 0):
         change = False
         model = self.nodeModel
 
         change = False
         model = self.nodeModel
 
@@ -417,43 +435,50 @@ class SliceWidget(QWidget):
             if status == node_status['add']:
                 print "Add hostname: %s" % hostname
 
             if status == node_status['add']:
                 print "Add hostname: %s" % hostname
 
-                resource_node = resources.get_node_element(hostname)
+                resource_node = resource_node_names.get(hostname, None)
 
                 if resource_node==None:
                     print "Error: Failed to find %s in resources rspec" % hostname
                 else:
 
                 if resource_node==None:
                     print "Error: Failed to find %s in resources rspec" % hostname
                 else:
-                    rspec.merge_node(resource_node, testbed)
-                    rspec.add_slivers([{"hostname": str(hostname)}], testbed)
+                    if not (hostname in rspec_node_names):
+                        network_name = Xrn(resource_node['component_manager_id']).get_hrn()
+                        rspec.version.add_network(network_name)
+                        rspec.version.add_nodes([resource_node])
+                    rspec.version.add_slivers([str(hostname)])
                     change = True
             elif status == node_status['remove']:
                 print "Remove hostname: %s" % hostname
                     change = True
             elif status == node_status['remove']:
                 print "Remove hostname: %s" % hostname
-                rspec.remove_slivers([{"hostname": str(hostname)}], testbed)
+                rspec.version.remove_slivers([str(hostname)])
                 change = True
         elif depth == 3: # Tag
             tag, value = self.itemText(item).split(": ")
             status = self.itemStatus(item)
             tag = "%s" % tag     # Prevent weird error from lxml
             value = "%s" % value # Prevent weird error from lxml
                 change = True
         elif depth == 3: # Tag
             tag, value = self.itemText(item).split(": ")
             status = self.itemStatus(item)
             tag = "%s" % tag     # Prevent weird error from lxml
             value = "%s" % value # Prevent weird error from lxml
-            node = self.itemText(item.parent())
+            hostname = self.itemText(item.parent())
             testbed = self.itemText(item.parent().parent())
             if status == tag_status['add']:
             testbed = self.itemText(item.parent().parent())
             if status == tag_status['add']:
-                print "Add tag to (%s, %s): %s/%s " % (testbed, node, tag, value)
-                if node.startswith(default_tags):
-                    rspec.add_default_sliver_attribute(tag, value, testbed)
+                print "Add tag to (%s, %s): %s/%s " % (testbed, hostname, tag, value)
+                if hostname.startswith(default_tags):
+                    rspec.version.add_default_sliver_attribute(tag, value, testbed)
                 else:
                 else:
-                    rspec.add_sliver_attribute(node, tag, value, testbed)
+                    node = rspec_node_names.get(hostname, None)
+                    if node:
+                        rspec.version.add_sliver_attribute(node['component_id'], tag, value, testbed)
                 change = True
             elif status == tag_status['remove']:
                 change = True
             elif status == tag_status['remove']:
-                print "Remove tag from (%s, %s): %s/%s " % (testbed, node, tag, value)
-                if node.startswith(default_tags):
-                    rspec.remove_default_sliver_attribute(tag, value, testbed)
+                print "Remove tag from (%s, %s): %s/%s " % (testbed, hostname, tag, value)
+                if hostname.startswith(default_tags):
+                    rspec.version.remove_default_sliver_attribute(tag, value, testbed)
                 else:
                 else:
-                    rspec.remove_sliver_attribute(node, tag, value, testbed)
+                    node = rspec_node_names.get(hostname, None)
+                    if node:
+                        rspec.version.remove_sliver_attribute(node['component_id'], tag, value, testbed)
                 change = True
 
         children = item.rowCount()
         for row in range(0, children):
                 change = True
 
         children = item.rowCount()
         for row in range(0, children):
-            status = self.process_subtree(rspec, resources, item.child(row), depth + 1)
+            status = self.process_subtree(rspec, rspec_node_names, resource_node_names, item.child(row), depth + 1)
             change = change or status
 
         return change
             change = change or status
 
         return change
@@ -464,7 +489,11 @@ class SliceWidget(QWidget):
 
         rspec = SfiData().getSliceRSpec()
         resources = SfiData().getResourcesRSpec()
 
         rspec = SfiData().getSliceRSpec()
         resources = SfiData().getResourcesRSpec()
-        change = self.process_subtree(rspec, resources, self.nodeModel.invisibleRootItem())
+
+        resource_node_names = self.nodesByName(resources.version.get_nodes())
+        rspec_node_names = self.nodesByName(rspec.version.get_nodes_with_slivers())
+
+        change = self.process_subtree(rspec, rspec_node_names, resource_node_names, self.nodeModel.invisibleRootItem())
 
         if not change:
             self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
 
         if not change:
             self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
@@ -482,9 +511,6 @@ class SliceWidget(QWidget):
         self.process.applyRSpec(rspec)
         self.setStatus("Sending slice data (RSpec). This will take some time...")
 
         self.process.applyRSpec(rspec)
         self.setStatus("Sending slice data (RSpec). This will take some time...")
 
-    # ProtoGENI-compatible submit. Contact each aggregate individually rather
-    # than using the slice manager.
-    # This code will be removed when ProtoGENI slicemanager is patched.
     def submit_pg_compat(self):
         if self.checkRunningProcess():
             return
     def submit_pg_compat(self):
         if self.checkRunningProcess():
             return
@@ -497,92 +523,13 @@ class SliceWidget(QWidget):
             self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
             return
 
             self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
             return
 
-        # Several aggregates have issues with the <statistics> section in the
-        # rspec, so make sure it's not there.
-        stats_elems = rspec.xml.xpath("//statistics")
-        if len(stats_elems)>0:
-            stats_elem = stats_elems[0]
-            parent = stats_elem.xpath("..")[0]
-            parent.remove(stats_elem)
-
-        self.submit_aggSuccessCount = 0
-        self.submit_aggFailCount = 0
-        self.submit_rspec = rspec
-        self.connect(self.process, SIGNAL('finished()'), self.getVersionFinished)
-        self.process.getSliceMgrVersion()
-        self.setStatus("Getting aggregate directory...")
-
-    def getVersionFinished(self):
-        self.disconnect(self.process, SIGNAL('finished()'), self.getVersionFinished)
-
-        faultString = self.process.getFaultString()
-        if not faultString:
-            peers = SfiData().getSliceMgrVersion()["peers"]
-            self.submit_aggs = [(key, peers[key]) for key in peers.keys()]
-            self.delete_aggs = [(key, peers[key]) for key in peers.keys() if key.startswith("emulab")]
-            str = "<font color='green'>Successfully retrieved agg list.</font> "
-            #self.submitNextAgg(str)
-            self.deleteNextAgg(str)
-        else:
-            self.setStatus("<font color='red'>getSliceMgrVersion failed: %s</font>" % (faultString))
-
-    def deleteNextAgg(self, statusStr=""):
-        if (self.delete_aggs == []):
-            self.submitNextAgg(statusStr)
-            return
-
-        self.delete_agg = self.delete_aggs.pop()
-
-        urlParts = urlparse.urlsplit(self.delete_agg[1])
-        amPort = urlParts.port
-        amAddr = urlParts.hostname+urlParts.path
-
-        self.setStatus(statusStr + "Deleting slivers on %s..." % (self.delete_agg[0]))
-
-        self.connect(self.process, SIGNAL('finished()'), self.deleteNextAggFinished)
-        self.process.deleteSlivers(aggAddr = amAddr, aggPort = amPort)
-
-    def submitNextAgg(self, statusStr=""):
-        if (self.submit_aggs == []):
-            self.setStatus(statusStr + "<font color='green'>Finished submitting. %d/%d aggs succeeded.</font>" %
-                           (self.submit_aggSuccessCount,self.submit_aggSuccessCount+self.submit_aggFailCount))
-            QTimer.singleShot(2500, self.refresh)
-            return
-
-        self.submit_agg = self.submit_aggs.pop()
-
-        urlParts = urlparse.urlsplit(self.submit_agg[1])
-        amPort = urlParts.port
-        amAddr = urlParts.hostname+urlParts.path
-
-        self.setStatus(statusStr + "Submitting to %s..." % (self.submit_agg[0]))
-
-        self.connect(self.process, SIGNAL('finished()'), self.submitNextAggFinished)
-        self.process.applyRSpec(self.submit_rspec, aggAddr = amAddr, aggPort = amPort, saveObtained=False)
-
-    def submitNextAggFinished(self):
-        self.disconnect(self.process, SIGNAL('finished()'), self.submitNextAggFinished)
-
-        faultString = self.process.getFaultString()
-        if not faultString:
-            self.submit_aggSuccessCount+=1
-            str = "<font color='green'>Succeeded on %s.</font> " % (self.submit_agg[0])
-        else:
-            self.submit_aggFailCount+=1
-            str = "<font color='red'>Failed on %s.</font> " % (self.submit_agg[0])  # , faultString)
-
-        self.submitNextAgg(str)
-
-    def deleteNextAggFinished(self):
-        self.disconnect(self.process, SIGNAL('finished()'), self.deleteNextAggFinished)
-
-        faultString = self.process.getFaultString()
-        if not faultString:
-            str = "<font color='green'>Succeeded deleteslivers on %s.</font> " % (self.delete_agg[0])
-        else:
-            str = "<font color='red'>Failed deleteslivers on %s.</font> " % (self.delete_agg[0])  # , faultString)
+        dlg = ClientSliceManager(self)
+        dlg.submit_pg_compat(rspec)
+        dlg.exec_()
 
 
-        self.deleteNextAgg(str)
+        self.setStatus("<font color='green'>Finished submitting. %d/%d aggs succeeded.</font>" %
+                      (dlg.submit_aggSuccessCount,dlg.submit_aggSuccessCount+dlg.submit_aggFailCount))
+        QTimer.singleShot(2500, self.refresh)
 
     def renew(self):
         dlg = RenewWindow(parent=self)
 
     def renew(self):
         dlg = RenewWindow(parent=self)
@@ -602,6 +549,29 @@ class SliceWidget(QWidget):
         self.process.retrieveResources()
         self.setStatus("Refreshing resources. This will take some time...")
 
         self.process.retrieveResources()
         self.setStatus("Refreshing resources. This will take some time...")
 
+    def nodesByNetwork(self, nodeList):
+        netDict = {}
+        for node in nodeList:
+            network_name = Xrn(node['component_manager_id']).get_hrn()
+            if network_name:
+                net = netDict.get(network_name, [])
+                if net == []:
+                    netDict[network_name] = net
+
+                net.append(node)
+
+        return netDict
+
+    def nodesByName(self, nodeList, nameDict=None):
+        if nameDict==None:
+            nameDict = {}
+        for node in nodeList:
+            hostname = node.get("component_name", None)
+            if hostname and (not hostname in nameDict):
+                nameDict[hostname] = node
+
+        return nameDict
+
     def updateView(self):
         global already_in_nodes
         already_in_nodes = []
     def updateView(self):
         global already_in_nodes
         already_in_nodes = []
@@ -617,67 +587,86 @@ class SliceWidget(QWidget):
             return None
 
         rootItem = self.nodeModel.invisibleRootItem()
             return None
 
         rootItem = self.nodeModel.invisibleRootItem()
-        networks = rspec.get_networks()
 
 
-        for network in resources.get_networks():
-            if not network in networks:
-                networks.append(network)
+        networks = []
+        for network in rspec.version.get_networks():
+            network_name = network.get("name", None)
+            if (network_name != None) and (not network_name in networks):
+                networks.append(network_name)
+        for network in resources.version.get_networks():
+            network_name = network.get("name", None)
+            if (network_name != None) and (not network_name in networks):
+                networks.append(network_name)
+
+        resources_nodes = self.nodesByNetwork(resources.version.get_nodes())
+        rspec_nodes = self.nodesByNetwork(rspec.version.get_nodes_with_slivers())
 
         for network in networks:
             self.network_names.append(network)
 
 
         for network in networks:
             self.network_names.append(network)
 
-            all_nodes = resources.get_nodes(network)
-            sliver_nodes = rspec.get_nodes_with_slivers(network)
+            all_nodes = resources_nodes.get(network, [])
+            sliver_nodes = rspec_nodes.get(network, [])
 
 
-            available_nodes = [ node for node in all_nodes if node not in sliver_nodes ]
+            sliver_node_names = self.nodesByName(sliver_nodes)
+
+            available_nodes = [ node for node in all_nodes if node["component_name"] not in sliver_node_names ]
 
 
-            networkItem = QStandardItem(QString(network))
             msg = "%s Nodes\t%s Selected" % (len(all_nodes), len(sliver_nodes))
             msg = "%s Nodes\t%s Selected" % (len(all_nodes), len(sliver_nodes))
-            rootItem.appendRow([networkItem, QStandardItem(QString("")), QStandardItem(QString(msg)), QStandardItem(QString("network"))])
+            networkItem = self.nodeView.appendRow(rootItem, network, membership=msg, kind="network")
 
 
-            already_in_nodes += sliver_nodes
+            already_in_nodes += sliver_node_names.keys()
 
             # Add default slice tags
 
             # Add default slice tags
-            nodeItem = QStandardItem(QString("%s for %s" % (default_tags, network)))
-            statusItem = QStandardItem(QString(""))
-            nodeStatus = QStandardItem(QString(""))
-            networkItem.appendRow([nodeItem, nodeStatus, statusItem, QStandardItem(QString("defaults"))])
-            attrs = rspec.get_default_sliver_attributes(network)
-            for (name, value) in attrs:
+            nodeItem = self.nodeView.appendRow(networkItem, "%s for %s" % (default_tags, network), kind="defaults")
+            attrs = rspec.version.get_default_sliver_attributes(network)
+            for attr in attrs:
+                    name = attr.get("name", None)
+                    value = attr.get("value", None)
                     tagstring = QString("%s: %s" % (name, value))
                     tagstring = QString("%s: %s" % (name, value))
-                    tagItem = QStandardItem(tagstring)
-                    status = QStandardItem(QString(tag_status['in']))
-                    nodeStatus = QStandardItem(QString(""))
-                    nodeItem.appendRow([tagItem, nodeStatus, status, QStandardItem(QString("attribute"))])
+                    self.nodeView.appendRow(nodeItem, tagstring, membership=tag_status['in'], kind = "attribute")
 
             for node in sliver_nodes:
 
             for node in sliver_nodes:
-                nodeItem = QStandardItem(QString(node))
-                statusItem = QStandardItem(QString(node_status['in']))
-                nodeStatus = QStandardItem(QString(rspec.get_node_element(node, network).attrib.get("boot_state","")))
-                networkItem.appendRow([nodeItem, nodeStatus, statusItem, QStandardItem(QString("node"))])
-
-                attrs = rspec.get_sliver_attributes(node, network)
-                for (name, value) in attrs:
-                    tagstring = QString("%s: %s" % (name, value))
-                    tagItem = QStandardItem(tagstring)
-                    statusItem = QStandardItem(QString(tag_status['in']))
-                    nodeStatus = QStandardItem(QString(""))
-                    nodeItem.appendRow([tagItem, nodeStatus, statusItem, QStandardItem(QString("attribute"))])
+                nodeType = None
+                if ("hardware_types" in node):
+                    hardware_types = [x["name"] for x in node["hardware_types"]]
+                    nodeType = ",".join(hardware_types)
+                nodeItem = self.nodeView.appendRow(networkItem,
+                               node["component_name"],
+                               nodeStatus=node.get("boot_state", ""),
+                               nodeType=nodeType,
+                               membership=node_status['in'],
+                               kind="node")
+
+                attrs = rspec.version.get_sliver_attributes(node['component_id'], network)
+                for attr in attrs:
+                    name = attr.get("name", None)
+                    value = attr.get("value", None)
+                    self.nodeView.appendRow(nodeItem,
+                                            "%s: %s" % (name, value),
+                                            membership=tag_status['in'],
+                                            kind="attribute")
 
             for node in available_nodes:
 
             for node in available_nodes:
-                nodeItem = QStandardItem(QString(node))
-                statusItem = QStandardItem(QString(node_status['out']))
-                nodeStatus = QStandardItem(QString(resources.get_node_element(node, network).attrib.get("boot_state","")))
-                networkItem.appendRow([nodeItem, nodeStatus, statusItem, QStandardItem(QString("node"))])
+                nodeType = None
+                if ("hardware_types" in node):
+                    hardware_types = [x["name"] for x in node["hardware_types"]]
+                    nodeType = ",".join(hardware_types)
+                self.nodeView.appendRow(networkItem,
+                               node["component_name"],
+                               nodeStatus = node.get("boot_state", ""),
+                               nodeType=nodeType,
+                               membership=node_status['out'],
+                               kind="node")
 
         self.filterModel.setSourceModel(self.nodeModel)
         self.filterModel.setDynamicSortFilter(True)
 
         self.filterModel.setSourceModel(self.nodeModel)
         self.filterModel.setDynamicSortFilter(True)
+        self.filterModel.sort(NAME_COLUMN)
 
 
-        headers = QStringList() << "Hostname or Tag" << "Node Status" << "Membership Status" << "Kind"
+        headers = QStringList() << "Hostname or Tag" << "Node Type" << "Node Status" << "Membership Status" << "Kind"
         self.nodeModel.setHorizontalHeaderLabels(headers)
 
         self.nodeModel.setHorizontalHeaderLabels(headers)
 
-        self.nodeView.setItemDelegateForColumn(0, self.nodeNameDelegate)
-        self.nodeView.setItemDelegateForColumn(1, self.nodeStatusDelegate)
+        self.nodeView.setItemDelegateForColumn(NAME_COLUMN, self.nodeNameDelegate)
+        self.nodeView.setItemDelegateForColumn(NODE_STATUS_COLUMN, self.nodeStatusDelegate)
         self.nodeView.setModel(self.filterModel)
         self.nodeView.hideColumn(KIND_COLUMN)
         self.nodeView.expandAll()
         self.nodeView.setModel(self.filterModel)
         self.nodeView.hideColumn(KIND_COLUMN)
         self.nodeView.expandAll()