def __init__(self, parent=None):
     """Constructor."""
     super(DsgManagementToolsDialog, self).__init__(parent)
     # Set up the user interface from Designer.
     # After setupUI you can access any designer object by doing
     # self.<objectname>, and you can use autoconnect slots - see
     # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
     # #widgets-and-dialogs-with-auto-connect
     self.setupUi(self)
     
     #Ip validator
     regex = QtCore.QRegExp("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")
     self.validator=QtGui.QRegExpValidator(regex, self.ipLineEdit)
     self.ipLineEdit.setValidator(self.validator)
     
     settings = QSettings()
     settings.beginGroup('Slony/server/')
     host = settings.value('host')
     if not host:
         host = '127.0.0.1'
     settings.endGroup()
     
     self.ipLineEdit.setText(host)
     #--------------
     self.utils = Utils(host)
     self.populatePostGISConnectionsCombo()
 def on_saveServerButton_clicked(self):
     settings = QSettings()
     settings.beginGroup('Slony/server/')
     settings.setValue('host', self.ipLineEdit.text())
     settings.endGroup()
     
     self.utils = Utils(self.ipLineEdit.text())       
class DsgManagementToolsDialog(QtGui.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(DsgManagementToolsDialog, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        
        #Ip validator
        regex = QtCore.QRegExp("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")
        self.validator=QtGui.QRegExpValidator(regex, self.ipLineEdit)
        self.ipLineEdit.setValidator(self.validator)
        
        settings = QSettings()
        settings.beginGroup('Slony/server/')
        host = settings.value('host')
        if not host:
            host = '127.0.0.1'
        settings.endGroup()
        
        self.ipLineEdit.setText(host)
        #--------------
        self.utils = Utils(host)
        self.populatePostGISConnectionsCombo()
        
    @pyqtSlot(bool)
    def on_saveServerButton_clicked(self):
        settings = QSettings()
        settings.beginGroup('Slony/server/')
        settings.setValue('host', self.ipLineEdit.text())
        settings.endGroup()
        
        self.utils = Utils(self.ipLineEdit.text())       
    
    def setCredentials(self, db, conInfo, user):
        (success, user, password) = QgsCredentials.instance().get(conInfo, user, None)
        if not success:
            return
        else:
            db.setPassword(password)
            if not db.open():
                self.setCredentials(db, conInfo, user)
            else:
                QgsCredentials.instance().put(conInfo, user, password)
                                    
    def populatePostGISConnectionsCombo(self):
        """
        Populate all the combo boxes with PostGIS connections
        """

        self.serverCombo.clear()
        self.serverCombo.addItem(self.tr("Select Database"))
        self.serverCombo.addItems(self.utils.getPostGISConnections())

        self.clientCombo.clear()
        self.clientCombo.addItem(self.tr("Select Database"))
        self.clientCombo.addItems(self.utils.getPostGISConnections())

        self.serverCombo_2.clear()
        self.serverCombo_2.addItem(self.tr("Select Database"))
        self.serverCombo_2.addItems(self.utils.getPostGISConnections())

        self.clientCombo_2.clear()
        self.clientCombo_2.addItem(self.tr("Select Database"))
        self.clientCombo_2.addItems(self.utils.getPostGISConnections())
        
        self.serverCombo_3.clear()
        self.serverCombo_3.addItem(self.tr("Select Database"))
        self.serverCombo_3.addItems(self.utils.getPostGISConnections())
        
        self.serverCombo_4.clear()
        self.serverCombo_4.addItem(self.tr("Select Database"))
        self.serverCombo_4.addItems(self.utils.getPostGISConnections())
        
    def queryClusterNames(self, connectionParameters):
        """
        Get clusters names for a PostGIS Connection
        """
        clusternames = []

        (conndb, connhost, connport, connuser, connpass) = self.utils.getPostGISConnectionParameters(connectionParameters)
        if conndb and connhost and connport and connuser:
            db = self.getConnection(conndb, connhost, connport, connuser, connpass)

            sql = 'select schema_name from information_schema.schemata'
            query = QSqlQuery(sql, db)
            while query.next():
                schema = str(query.value(0))
                if schema[0] == '_':
                    clusternames.append(schema)
        else:
            QMessageBox.warning(self, self.tr("Warning!"), self.tr('It is not possible to get all server parameters. Please, use DsgTools to edit the server.'))
                    
        return clusternames

    @pyqtSlot(int)
    def on_serverCombo_3_currentIndexChanged(self, index):
        """
        Populate the clusters tree widget
        """
        self.treeWidget.clear()
        
        if index == 0:
            return
        
        clusternames = self.queryClusterNames(self.serverCombo_3.currentText())
        for clustername in clusternames:
            self.insertClusterItem(self.treeWidget, clustername)
                
    @pyqtSlot(int)
    def on_serverCombo_4_currentIndexChanged(self, index):
        """
        Populate the clusters tree widget
        """
        self.treeWidget_2.clear()

        if index == 0:
            return
        
        clusternames = self.queryClusterNames(self.serverCombo_4.currentText())
        for clustername in clusternames:
            self.insertClusterItem(self.treeWidget_2, clustername)
                
    def getConnection(self, conndb, connhost, connport, connuser, connpass):
        """
        Get a QSqlDatabase for a specific set of connection parameters
        """

        db = None
        
        db = QSqlDatabase("QPSQL")
        db.setDatabaseName(conndb)
        db.setHostName(connhost)
        db.setPort(int(connport))
        db.setUserName(connuser)

        if not connpass or connpass == '':
            conInfo = 'host='+connhost+' port='+connport+' dbname='+conndb
            self.setCredentials(db, conInfo, connuser)
        else:
            db.setPassword(connpass)

        if not db.open():
            QMessageBox.critical(self, self.tr("Critical!"), self.tr('Database connection problem: \n') + db.lastError().text())
            
        return db
            
    def insertClusterItem(self, tree, text):
        """
        Creates an item for the tree widget for a specific parent item and a specific text (cluster name)
        """
        parent = tree.invisibleRootItem()
        item = QTreeWidgetItem(parent)
        item.setExpanded(True)
        item.setText(0,text)

    @pyqtSlot(int)
    def on_serverCombo_currentIndexChanged(self, index):
        """
        Sets the cluster name on the fly
        """

        slavedb = self.serverCombo.currentText()
        masterdb = self.clientCombo.currentText()
        self.clusterEdit.setText(masterdb+separador+slavedb)

    @pyqtSlot(int)
    def on_clientCombo_currentIndexChanged(self, index):
        """
        Sets the cluster name on the fly
        """

        slavedb = self.serverCombo.currentText()
        masterdb = self.clientCombo.currentText()
        self.clusterEdit.setText(masterdb+separador+slavedb)
        
    def checkPasswordSupply(self, db, host, port, user, password):
        if not password or password == '':
            conInfo = 'host='+host+' port='+port+' dbname='+db
            (success, user, password) = QgsCredentials.instance().get(conInfo, user, None)
            if not success:
                QMessageBox.warning(self, self.tr('Warning!'), self.tr('Password not supplied. Nothing can be done!'))
                return False, None
        return True, password
        
    @pyqtSlot(bool)
    def on_createClusterButton_clicked(self):
        """
        Creates a cluster, subscribes master and slave and starts the daemons to begin replication 
        """

        cluster = self.clusterEdit.text()
        if len(cluster) > 48:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Cluster name too long'))
            return
        
        (slavedb, slavehost, slaveport, slaveuser, slavepass) = self.utils.getPostGISConnectionParameters(self.serverCombo.currentText())
        supplied, slavepass = self.checkPasswordSupply(slavedb, slavehost, slaveport, slaveuser, slavepass)
        if not supplied:
            return
        
        #checking slave's database version
        db = self.getConnection(slavedb, slavehost, slaveport, slaveuser, slavepass)
        slaveversion = self.getDatabaseVersion(db)

        (masterdb, masterhost, masterport, masteruser, masterpass) = self.utils.getPostGISConnectionParameters(self.clientCombo.currentText())
        supplied, masterpass = self.checkPasswordSupply(masterdb, masterhost, masterport, masteruser, masterpass)
        if not supplied:
            return

        #checking master's database version
        db = self.getConnection(masterdb, masterhost, masterport, masteruser, masterpass)
        masterversion = self.getDatabaseVersion(db)
        
        if masterversion != slaveversion:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Slave and Master databases have different versions!'))
            return

        req = self.utils.makeRequest('configurecluster.py', masterdb, slavedb, masterhost, slavehost, masterport, slaveport, masteruser, masterpass, slaveuser, slavepass, cluster, masterversion)
        (ret, success) = self.utils.run(req)
        ret = ret.decode(encoding='UTF-8')
        if success:
            QMessageBox.information(self, self.tr('Information!'), ret.strip())
        else:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Error while creating cluster:')+'\n'+ret.strip())
        
    @pyqtSlot(bool)
    def on_startReplicationButton_clicked(self):
        """
        Starts daemons for the selected master and slave databases
        """

        slavedb = self.serverCombo_2.currentText()
        masterdb = self.clientCombo_2.currentText()
        cluster = masterdb+separador+slavedb
        
        (slavedb, slavehost, slaveport, slaveuser, slavepass) = self.utils.getPostGISConnectionParameters(self.serverCombo_2.currentText())
        supplied, slavepass = self.checkPasswordSupply(slavedb, slavehost, slaveport, slaveuser, slavepass)
        if not supplied:
            return

        #checking slave's database version
        db = self.getConnection(slavedb, slavehost, slaveport, slaveuser, slavepass)
        slaveversion = self.getDatabaseVersion(db)

        (masterdb, masterhost, masterport, masteruser, masterpass) = self.utils.getPostGISConnectionParameters(self.clientCombo_2.currentText())
        supplied, masterpass = self.checkPasswordSupply(masterdb, masterhost, masterport, masteruser, masterpass)
        if not supplied:
            return
        
        #checking master's database version
        db = self.getConnection(masterdb, masterhost, masterport, masteruser, masterpass)
        masterversion = self.getDatabaseVersion(db)
        
        if masterversion != slaveversion:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Slave and Master databases have different versions!'))
            return

        req = self.utils.makeRequest('startreplication.py', masterdb, slavedb, masterhost, slavehost, masterport, slaveport, masteruser, masterpass, slaveuser, slavepass, cluster, masterversion)
        (ret, success) = self.utils.run(req)
        ret = ret.decode(encoding='UTF-8')
        if success:
            QMessageBox.information(self, self.tr('Information!'), ret.strip())
        else:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Error while starting replication:')+'\n'+ret.strip())

    @pyqtSlot(bool)
    def on_removeClusterButton_clicked(self):
        """
        Removes a selected cluster.
        Before the actual removal a check for running daemons is made.
        """

        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self, self.tr("Warning!"), self.tr('Please, select a cluster to be removed.'))
            return

        item = self.treeWidget.selectedItems()[0]
        
        self.checkAndKillSlonDaemons(item.text(0))
        
        self.removeClusters(item.text(0))
        
    @pyqtSlot(bool)
    def on_stopReplicationButton_clicked(self):
        """
        Check for running slon daemons and kill them.
        """

        #case no item is selected we should warn the user
        if len(self.treeWidget_2.selectedItems()) == 0:
            QMessageBox.warning(self, self.tr("Warning!"), self.tr('Please, select a cluster to be removed.'))
            return

        item = self.treeWidget_2.selectedItems()[0]
        
        self.checkAndKillSlonDaemons(item.text(0))
        
    def removeClusters(self, clustername):
        """
        Remove cluster from master and slave databases
        """

        cluster = clustername[1::]
        split = cluster.split(separador)
        
        self.removeCluster(clustername, split[0])
        self.removeCluster(clustername, split[1])        
        
    def removeCluster(self, clustername, conn):
        """
        Removes a cluster
        """

        (conndb, connhost, connport, connuser, connpass) = self.utils.getPostGISConnectionParameters(conn)
        supplied, connpass = self.checkPasswordSupply(conndb, connhost, connport, connuser, connpass)
        if not supplied:
            return

        #Gets the connection
        db = self.getConnection(conndb, connhost, connport, connuser, connpass)

        #Drop schema query
        sql = 'DROP SCHEMA '+clustername+' CASCADE'
        query = QSqlQuery(db)
        query.exec_(sql)        
    
    def checkAndKillSlonDaemons(self, clustername):
        """
        Check for active slon daemons and kill them
        """

        cluster = clustername[1::]
        split = cluster.split(separador)
        slave = split[1]
        (slavedb, slavehost, slaveport, slaveuser, slavepass) = self.utils.getPostGISConnectionParameters(slave)
        supplied, slavepass = self.checkPasswordSupply(slavedb, slavehost, slaveport, slaveuser, slavepass)
        if not supplied:
            return
        
        req = self.utils.makeKillRequest('stopreplication.py', cluster, slavehost)
        (ret, success) = self.utils.run(req)
        ret = ret.decode(encoding='UTF-8')
        if success:
            QMessageBox.information(self, self.tr('Information!'), ret.strip())
        else:
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Error while stopping replication:')+'\n'+ret.strip())
       
    @pyqtSlot(bool) 
    def on_refreshButton_clicked(self):
        """
        Check running daemons and make a user readable information
        """
        
        req = self.utils.makeGetRunningDaemonsRequest('getrunningdaemons.py')
        (ret, success) = self.utils.run(req)
        ret = ret.decode(encoding='UTF-8')
        if not success:
            QMessageBox.warning(self, self.tr("Warning!"), self.tr('Error while checking for active replications:')+'\n'+ret)
            return
        split = ret.strip().split('*')
        
        self.daemonsTreeWidget.clear()
        parent = self.daemonsTreeWidget.invisibleRootItem()

        children = []
        #checking if the text is already in the tree widget
        for text in split:
            if text not in children and text != '':
                item = QTreeWidgetItem(parent)
                item.setExpanded(True)
                item.setText(0,text)
                children.append(text)

    def getDatabaseVersion(self, db):
        version = '-1'
        if not db.open():
            return version
        sqlVersion = 'SELECT edgvversion FROM db_metadata LIMIT 1'
        queryVersion = QSqlQuery(sqlVersion, db)
        while queryVersion.next():
            version = queryVersion.value(0)
        return version