""" CCP4JobControlGui.py: CCP4 GUI Project Copyright (C) 2016STFC This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law.sstac You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ """ Liz Potterton april 2016 - Gui for remote running """ import os, glob, time, functools from PyQt4 import QtGui,QtCore from CCP4ErrorHandling import * import CCP4Utils,CCP4Modules,CCP4Annotation,CCP4Widgets import CCP4Container,CCP4JobServer class CServerParamsDialog(QtGui.QDialog): insts = None guiDef = { 'machine' : [ 'Server' , 'machine', 'Choose a server'], 'username': [ 'Username on server' , 'Your username on server'], 'password' : [ 'Password on server','Your password on server' ], 'keyFilename' : [ 'SSH key filename', 'Local file containing SSH public keys' ], 'ccp4Dir' : [ 'CCP4 directory on server', 'Full path of CCP4 on the server machine' ], 'tempDir' : [ 'Work directory on server', 'Full path of temporary work directory server machine' ], 'sge_root' : [ 'SGE_ROOT (Sun gridengine dir)', 'Full path of Sun grid engine root directory server machine' ], 'queueOptionsFile' : [ 'Queue option file', 'File containing options for the job queue' ], 'timeout' : [ 'Timeout in seconds', 'Wait for connection' ] } def userGuiOptions(self,mechanism,validate=None,customCodeFile=None): if mechanism in ['ssh','ssh_shared','qsub_remote','qsub_shared']: if validate is None or validate == 'password': ret = ['username','password','machine'] elif validate == 'key_filename': ret = ['username','key_filename','machine'] elif validate == 'pass_key_filename': ret = ['username','key_filename','password','machine'] if mechanism in ['ssh','qsub_remote']: ret.append('tempDir') #if mechanism in ['qsub_shared','qsub_remote']: ret.append('sge_root') elif mechanism == 'qsub_local': ret = [] elif mechanism == 'custom': if customCodeFile is not None: return CCP4Modules.JOBCONTROLLER().customHandler(customCodeFile=customCodeFile).SHOW_USER_PARAMS else: return ['username','password'] if mechanism in ['qsub_local','qsub_remote','qsub_shared']: ret.append('queueOptionsFile') return ret def getParams(self): p = CCP4JobServer.CServerParams(machine = self.get('machine'), username = self.get('username'), password = self.get('password'), keyFilename = self.get('keyFilename'), mechanism = self.get('mechanism'), validate= self.get('validate'), ccp4Dir = self.get('ccp4Dir'), tempDir = self.get('tempDir'), sge_root = self.get('sge_root'), customCodeFile = self.get('customCodeFile'), queueOptionsFile = self.get('queueOptionsFile')) print 'CServerParamsDialog.getParams',p return p def get(self,param): if not self.widgets.has_key(param): serverGroup = self.container.get('SERVERGROUP'+str(self.modeGroup.checkedId())) if param == 'machine': return str(getattr(serverGroup,'serverList')[0]) elif param == 'timeout': if serverGroup.timeout.isSet(): return float(serverGroup.timeout) else: return None elif param not in ['username','password']: return str(getattr(serverGroup,param)) else: return '' if param in ['queueOptionsFile']: self.widgets['queueOptionsFile'].updateModelFromView() return str(self.queueOptionsFile) else: if isinstance(self.widgets[param],QtGui.QLineEdit): if param == 'timeout': if len(self.widgets[param].text()) == 0: return None else: return float(self.widgets[param].text()) return str(self.widgets[param].text()) else: return str(self.widgets[param].currentText()) def setInfo(self,info=None): if info is None: self.widgets['info'].setText('') else: self.widgets['info'].setText(info) def valid(self): return True def __init__(self,parent=None,params={}): QtGui.QDialog.__init__(self,parent) import CCP4Utils try: self.loadConfig() except: print 'Failed loading server config file from CCP4I2/local_setup/servers_config.params.xml' self.setWindowTitle('Choose server to run job') layout = QtGui.QVBoxLayout() self.setLayout(layout) self.setMinimumWidth(500) frame = QtGui.QFrame(self) frame.setLayout(QtGui.QVBoxLayout()) frame.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain) MARGIN = 6 frame.layout().setSpacing(MARGIN) frame.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN) frame.layout().addWidget(CCP4Widgets.CItalicLabel('Choose group of servers to use..',self)) self.layout().addWidget(frame) self.modeGroup = QtGui.QButtonGroup(self) default = 0 row = 0 for idx in range(1,10): group = self.container.get('SERVERGROUP'+str(idx)) #if group is not None: print 'SERVERGROUP',idx,group, group.isSet(), len(group.serverList) if group is not None and len(group.serverList)>0: but = QtGui.QRadioButton(str(group.name),self) self.modeGroup.addButton(but,idx) frame.layout().addWidget(but) row = row + 1 if default==0: default = idx #print 'default modeGroup',default try: self.modeGroup.button(default).setChecked(True) except: print 'Failed to set modeGroup',default self.customFrame = QtGui.QFrame(self) self.customFrame.setLayout(QtGui.QVBoxLayout()) self.layout().addWidget(self.customFrame) butBox = QtGui.QDialogButtonBox(self) but = butBox.addButton("Submit job",QtGui.QDialogButtonBox.AcceptRole) but.clicked.connect(self.accept) but = butBox.addButton("Help",QtGui.QDialogButtonBox.HelpRole) but.clicked.connect(self.help) but = butBox.addButton("Cancel",QtGui.QDialogButtonBox.RejectRole) but.clicked.connect(self.reject) self.layout().addWidget(butBox) self.connect(self.modeGroup,QtCore.SIGNAL('buttonReleased(int)'),self.loadServers) self.loadServers() def drawDetails(self,serverGroup,params={}): self.widgets = { } self.labels = { } #params['ccp4Dir'] = params.get('ccp4Dir',CCP4Utils.getCCP4Dir()) #params['username'] = params.get('username',CCP4Utils.getUserId()) self.detailsFrame = QtGui.QFrame(self) layout = QtGui.QGridLayout() self.detailsFrame.setLayout(layout) self.detailsFrame.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain) layout.addWidget(CCP4Widgets.CItalicLabel('Details',self),0,0,1,2) row = 1 #for param in ['machine', 'ccp4Dir', 'username', 'password','keyFilename','queueOptionsFile']: #for param in CServerParamsDialog.guiDef.keys(): for param in self.userGuiOptions(mechanism=str(serverGroup.mechanism),validate=str(serverGroup.validate), customCodeFile=str(serverGroup.customCodeFile)): self.labels[param] = QtGui.QLabel(CServerParamsDialog.guiDef[param][0],self) layout.addWidget(self.labels[param] ,row,0) if param == 'machine': self.widgets[param] = QtGui.QComboBox(self) elif param == 'queueOptionsFile': import CCP4File self.queueOptionsFile = CCP4File.CDataFile(parent=self) self.widgets[param] = CCP4Widgets.CDataFileView(self,model=self.queueOptionsFile) else: self.widgets[param] = QtGui.QLineEdit(self) self.widgets[param].setText(params.get(param,'')) self.widgets[param].setToolTip(CServerParamsDialog.guiDef[param][1]) if param == 'password' : self.widgets[param].setEchoMode(QtGui.QLineEdit.Password) layout.addWidget(self.widgets[param],row,1) row = row + 1 if serverGroup.mechanism == 'custom': for i in range(self.customFrame.layout().count()): try: w = self.customFrame.layout().takeAt(0).widget() w.hide() w.deleteLater() except: pass guiFunction = getattr(CCP4Modules.JOBCONTROLLER().customHandler(customCodeFile=serverGroup.customCodeFile),'guiFrame',None) if guiFunction is not None and callable(guiFunction): try: guiFunction(parentFrame=self.customFrame) except Exception as e: print 'Failed drawing custom widgets',str(e) self.customFrame.show() else: self.customFrame.hide() def loadServers(self,mode=None): serverGroup = self.container.get('SERVERGROUP'+str(self.modeGroup.checkedId())) if serverGroup is None: warningMess = QtGui.QMessageBox.warning(self,'Run on server','''To run on server you must first configure servers. See under Utilities -> System administrator tools''') self.hide() return if hasattr(self,'detailsFrame'): idx = self.layout().indexOf(self.detailsFrame) self.layout().takeAt(idx) self.detailsFrame.hide() self.detailsFrame.deleteLater() else: idx = 1 self.drawDetails(serverGroup) self.layout().insertWidget(idx,self.detailsFrame) #print 'loadServers',self.modeGroup.checkedId(),serverGroup,len(serverGroup.serverList) if self.widgets.has_key('machine'): self.widgets['machine'].setEditable(bool(serverGroup.userExtensible)) self.widgets['machine'].clear() for item in serverGroup.serverList: self.widgets['machine'].addItem(str(item)) for key in ['ccp4Dir','keyFilename','tempDir']: if self.widgets.has_key(key): if getattr(serverGroup,key).isSet(): self.widgets[key].setText(str(getattr(serverGroup,key))) else: self.widgets[key].setText('') if self.widgets.has_key('password'): if serverGroup.validate is None or str(serverGroup.validate) == 'password': self.labels['password'].setText('Your password on server') self.widgets['password'].setToolTip('Your password on server') elif str(serverGroup.validate) == 'key_filename': pass elif str(serverGroup.validate) == 'pass_key_filename': self.labels['password'].setText('Your key file password') self.widgets['password'].setToolTip('Your key file password') def loadConfig(self): import CCP4Modules self.container = CCP4Modules.SERVERSETUP() def accept(self): QtGui.QDialog.accept(self) def reject(self): QtGui.QDialog.reject(self) def setInfo(self,text): if not self.widgets.has_key('info'): self.widgets['info'] = QtGui.QLabel(self) self.widgets['info'].setObjectName('warning') self.layout().insertWidget(0,self.widgets['info']) if text is None: self.widgets['info'].setText('') else: self.widgets['info'].setText(text) def help(self): CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='servers.html',newTab=True) class CServerGroupView(CCP4Widgets.CComplexLineWidget): MODEL_CLASS = CCP4Annotation.CServerGroup def __init__(self,parent=None,model=None,qualifiers={}): qualis = { 'vboxLayout' : True } qualis.update(qualifiers) CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis) self.layout().takeAt(0).widget().deleteLater() self.frames = {} self.widgets['name'] = CCP4Widgets.CStringView(self) qualis self.widgets['mechanism'] = CCP4Widgets.CStringView( self,qualifiers=model.mechanism.qualifiers() ) self.widgets['validate'] = CCP4Widgets.CRadioButtonGroup(self) self.widgets['validate'].addRadioButton('password','by password') self.widgets['validate'].addRadioButton('key_filename','by key file') self.widgets['validate'].addRadioButton('pass_key_filename','by passworded key file') self.connect(self.widgets['mechanism'].widget,QtCore.SIGNAL('currentIndexChanged(int)'),self.changeMechanism) self.connect(self.widgets['validate'],QtCore.SIGNAL('buttonReleased(int)'),self.changeValidate) ''' if model is not None: self.widgets['mechanism'].populate(menuItems=model.mechanism.qualifiers('enumerators'),menuText=model.mechanism.qualifiers('menuText')) ''' self.widgets['serverList'] = CCP4Widgets.CListView(self, qualifiers= { 'editorClassName' : 'CStringView' }) self.widgets['userExtensible'] = CCP4Widgets.CBooleanView(self) self.widgets['ccp4Dir'] = CCP4Widgets.CStringView(self,qualifiers={ 'allowUndefined' : True } ) self.widgets['tempDir'] = CCP4Widgets.CStringView(self,qualifiers={ 'allowUndefined' : True } ) self.widgets['sge_root'] = CCP4Widgets.CStringView(self,qualifiers={ 'allowUndefined' : True } ) self.widgets['keyFilename'] = CCP4Widgets.CStringView(self,qualifiers={ 'allowUndefined' : True } ) self.widgets['timeout'] = CCP4Widgets.CFloatView(self,qualifiers={ 'allowUndefined' : True } ) self.widgets['maxTries'] = CCP4Widgets.CIntView(self,qualifiers={ 'allowUndefined' : False } ) self.widgets['customCodeFile'] = CCP4Widgets.CDataFileView(self,qualifiers={ 'allowUndefined' : True , 'jobCombo' : False, 'browseDb': False } ) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Name for server group',self)) line.addWidget(self.widgets['name']) line.addWidget(QtGui.QLabel('using mechanism',self)) line.addWidget(self.widgets['mechanism']) self.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Validate by',self)) for item in ['password','key_filename','pass_key_filename']: line.addWidget(self.widgets['validate'].getButton(item)) line.addStretch(2) self.layout().addLayout(line) line = QtGui.QHBoxLayout() self.widgets['serverList'].setLineWidth(1) self.widgets['serverList'].setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Plain) line.addWidget(self.widgets['serverList']) self.layout().addLayout(line) self.widgets['serverList'].setListVisible(True) line = QtGui.QHBoxLayout() line.addWidget(self.widgets['userExtensible']) line.addWidget(QtGui.QLabel('Allow users to add alternative servers at run time',self)) line.addStretch(2) self.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(CCP4Widgets.CItalicLabel('CCP4 distro directory on these servers',self)) self.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(self.widgets['ccp4Dir']) self.layout().addLayout(line) frame = self.makeFrame('tempDir') line = QtGui.QHBoxLayout() line.addWidget(CCP4Widgets.CItalicLabel('Temporary directory (eg /tmp/$USER)',self)) frame.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(self.widgets['tempDir']) frame.layout().addLayout(line) frame = self.makeFrame('sge_root') line = QtGui.QHBoxLayout() line.addWidget(CCP4Widgets.CItalicLabel('SGE_ROOT (Sun Grid Engine dir)',self)) frame.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(self.widgets['sge_root']) frame.layout().addLayout(line) frame = self.makeFrame('key_filename') frame.layout().addWidget(CCP4Widgets.CItalicLabel('Local SSH key filename (eg $HOME/.ssh/id_rsa)',self)) frame.layout().addWidget(self.widgets['keyFilename']) line = self.makeFrame('timeout',vertical=False) line.layout().addWidget(CCP4Widgets.CItalicLabel('Timeout (seconds)',self)) line.layout().addWidget(self.widgets['timeout']) line.layout().addWidget(CCP4Widgets.CItalicLabel('Maximum tries',self)) line.layout().addWidget(self.widgets['maxTries']) self.layout().addWidget(line) frame = self.makeFrame('customCodeFile') line = QtGui.QHBoxLayout() line.addWidget(CCP4Widgets.CItalicLabel('Custom server code file',self)) frame.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(self.widgets['customCodeFile']) frame.layout().addLayout(line) line = QtGui.QHBoxLayout() self.testButton = QtGui.QToolButton(self) self.testButton.setText('Test server group') self.deleteButton = QtGui.QToolButton(self) self.deleteButton.setText('Delete server group') line.addStretch(5) line.addWidget(self.testButton ) line.addWidget(self.deleteButton ) self.layout().addLayout(line) self.setModel(model=model) self.updateViewFromModel() self.widgets['serverList'].updateViewFromModel() self.widgets['serverList'].handleRowChange(0,force=True) self.widgets['mechanism'].updateViewFromModel() self.changeMechanism(0) def makeFrame(self,name,vertical=True): self.frames[name] = QtGui.QFrame() if vertical: self.frames[name].setLayout(QtGui.QVBoxLayout()) else: self.frames[name].setLayout(QtGui.QHBoxLayout()) self.frames[name].layout().setSpacing(1) self.frames[name].layout().setContentsMargins(1,1,1,1) self.layout().addWidget(self.frames[name]) return self.frames[name] def updateViewFromModel(self): # Exclude the serverList which is behaving oddly because # dataChanged signal from serverList object is relayed by # CServerGroup and causes this method to be invoked for item in ['name','userExtensible','ccp4Dir','tempDir','keyFilename','customCodeFile','timeout','maxTries']: self.widgets[item].updateViewFromModel() self.widgets['validate'].setValue(str(self.model.validate)) self.changeValidate(self.model.validate.qualifiers('enumerators').index(str(self.model.validate))) def updateModelFromView(self): for item in ['name','userExtensible','ccp4Dir','tempDir','keyFilename','serverList','customCodeFile','timeout','maxTries']: self.widgets[item].updateModelFromView() self.model.validate.set(self.widgets['validate'].getValue()) #print 'CServerGroupView.updateModelFromView',self.model.validate def changeMechanism(self,indx): mechanism = self.widgets['mechanism'].getWidgetText() #print 'CServerGroupView.changeMechanism',indx,mechanism if mechanism in ['custom']: self.frames['customCodeFile'].show() else: self.frames['customCodeFile'].hide() if mechanism in ['ssh','ssh_shared','qsub_remote','qsub_shared']: self.frames['timeout'].show() else: self.frames['timeout'].hide() if mechanism in ['ssh','qsub_remote','custom']: self.frames['tempDir'].show() else: self.frames['tempDir'].hide() if mechanism in ['qsub_local','qsub_remote','qsub_shared']: self.frames['sge_root'].show() else: self.frames['sge_root'].hide() def changeValidate(self,id): #print 'changeValidate',id self.model.validate.set(self.widgets['validate'].getValue()) if id in [0]: self.frames['key_filename'].hide() else: self.frames['key_filename'].show() class CServerSetupWindow(QtGui.QDialog): ''' This is the glue to get display the CTaskServerSetup as an independent window ''' def __init__(self,parent): QtGui.QDialog.__init__(self,parent) self.setWindowTitle('Configure servers for remote running') #self.setMaximumWidth(650) #self.setMinimumHeight(500) self.setLayout(QtGui.QVBoxLayout()) MARGIN = 2 self.layout().setSpacing(MARGIN) self.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN) self.widgets= {} import CCP4Modules,CCP4Widgets self.container = CCP4Modules.SERVERSETUP() self.container.load() line = QtGui.QHBoxLayout() self.sourceLabel = QtGui.QLabel('Server setup loaded from ',self) line.addWidget(self.sourceLabel) self.layout().addLayout(line) self.altSourceBut = QtGui.QPushButton('Load alternate setup',self) line.addWidget(self.altSourceBut) self.connect(self.altSourceBut,QtCore.SIGNAL('clicked()'),self.handleLoadAlt) self.scrollArea= QtGui.QScrollArea(self) self.scrollArea.setMaximumWidth(570) self.scrollArea.setMinimumWidth(570) self.scrollArea.setMinimumHeight(500) self.layout().addWidget(self.scrollArea) self.contentFrame = QtGui.QFrame(self) self.scrollArea.setWidget(self.contentFrame) self.scrollArea.setWidgetResizable(1) self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.contentLayout = QtGui.QVBoxLayout() self.contentLayout.setSpacing(MARGIN) self.contentLayout.setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN) self.contentFrame.setLayout(self.contentLayout) self.makeAllWidgets() line = QtGui.QHBoxLayout() self.layout().addLayout(line) addButton = QtGui.QToolButton(self) addButton.setText('Add another server group') line.addStretch(4) line.addWidget(addButton) self.layout().addLayout(line) self.connect(addButton,QtCore.SIGNAL('clicked()'),self.handleAdd) self.buttons = QtGui.QDialogButtonBox(self) but = self.buttons.addButton(QtGui.QDialogButtonBox.Help) self.connect(but,QtCore.SIGNAL('clicked()'),self.help) if self.container.writeAccess('installation'): but = self.buttons.addButton('Save for CCP4 installation',QtGui.QDialogButtonBox.ApplyRole) self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.doApply,'installation')) but = self.buttons.addButton('Save for me only',QtGui.QDialogButtonBox.ApplyRole) self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.doApply,'user')) but = self.buttons.addButton(QtGui.QDialogButtonBox.Close) self.connect(but,QtCore.SIGNAL('clicked()'),self.close) line = QtGui.QHBoxLayout() line.addStretch(0.2) line.addWidget(self.buttons) line.addStretch(0.2) self.layout().addLayout(line) def makeAllWidgets(self): #print 'makeAllWidgets',self.container,self.container._value,self.getNServerGroups() n = 1 while self.container.get('SERVERGROUP'+str(n)) is not None: self.makeWidget(n) n = n + 1 def clear(self): for key,w in self.widgets.items(): w.hide() w.deleteLater() self.widgets = {} def makeWidget(self,n): self.widgets['SERVERGROUP'+str(n)] = CServerGroupView(self,self.container.get('SERVERGROUP'+str(n))) self.widgets['SERVERGROUP'+str(n)].setMaximumWidth(550) self.widgets['SERVERGROUP'+str(n)].setMinimumWidth(550) self.connect(self.widgets['SERVERGROUP'+str(n)].deleteButton,QtCore.SIGNAL('clicked()'),functools.partial(self.handleDelete,n)) self.connect(self.widgets['SERVERGROUP'+str(n)].testButton,QtCore.SIGNAL('clicked()'),functools.partial(self.handleTest,n)) #self.layout().addWidget(self.widgets['SERVERGROUP'+str(n)]) ''' self.widgets['SERVERGROUP'+str(n)].updateViewFromModel() self.widgets['SERVERGROUP'+str(n)].widgets['serverList'].updateViewFromModel() self.widgets['SERVERGROUP'+str(n)].widgets['serverList'].handleRowChange(0,force=True) ''' self.contentLayout.addWidget(self.widgets['SERVERGROUP'+str(n)]) def show(self): self.setAltSourceButton() QtGui.QDialog.show(self) def setAltSourceButton(self): if self.container.source is not None: self.sourceLabel.setText('Server setup loaded from '+str(self.container.source)) altSource = ['user','installation'][1 - ['user','installation'].index( self.container.source )] if os.path.exists(self.container.preferencesFile(altSource)[0]): self.altSourceBut.setText('Load '+altSource+' setup') self.altSourceBut.setEnabled(True) return self.altSourceBut.setText('Load alternate setup') self.altSourceBut.setEnabled(False) def handleLoadAlt(self): altSource = ['user','installation'][1 - ['user','installation'].index( self.container.source )] #print 'handleAltSource',altSource self.clear() self.container.clear() self.container.load(altSource) self.makeAllWidgets() self.setAltSourceButton() def getNServerGroups(self): n = 0 while self.container.get('SERVERGROUP'+str(n+1)) is not None: n = n + 1 return n def handleAdd(self): n = self.getNServerGroups() + 1 self.container.setContents( { 'SERVERGROUP'+str(n) : { 'class' :CCP4Annotation.CServerGroup }} ) self.container.get('SERVERGROUP'+str(n)).setObjectName('SERVERGROUP'+str(n)) self.makeWidget(n) def handleDelete(self,n): #print 'CServerSetupWindow.handleDelete',n,self.getNServerGroups() nTot = self.getNServerGroups() if nTot==1: return self.widgets['SERVERGROUP'+str(n)].hide() self.widgets['SERVERGROUP'+str(n)].deleteLater() del self.widgets['SERVERGROUP'+str(n)] self.container.get('SERVERGROUP'+str(n)).blockSignals(True) self.container.deleteObject('SERVERGROUP'+str(n)) if n < nTot: for ii in range(n,nTot): self.widgets['SERVERGROUP'+str(ii+1)].setObjectName('SERVERGROUP'+str(ii)) self.widgets['SERVERGROUP'+str(ii)] = self.widgets.pop('SERVERGROUP'+str(ii+1)) self.container.renameObject('SERVERGROUP'+str(ii+1),'SERVERGROUP'+str(ii)) def handleTest(self,indx): if hasattr(self,'testDialog'): self.testDialog.hide() self.testDialog.deleteLater() self.testDialog = QtGui.QDialog(self) self.testDialog.setWindowTitle('Test remote server') self.testDialog.setLayout(QtGui.QVBoxLayout()) self.testDialog.layout().addWidget(QtGui.QLabel('Test existance of CCP4 directory and temporary directory on each machine',self)) line = QtGui.QHBoxLayout() self.testDialog.layout().addLayout(line) self.testDialogUsername = QtGui.QLineEdit(self.testDialog) line.addWidget(QtGui.QLabel('Test with username',self.testDialog)) line.addWidget(self.testDialogUsername) line = QtGui.QHBoxLayout() self.testDialog.layout().addLayout(line) self.testDialogPassword = QtGui.QLineEdit(self.testDialog) self.testDialogPassword.setEchoMode(QtGui.QLineEdit.Password) line.addWidget(QtGui.QLabel('Test with password',self.testDialog)) line.addWidget(self.testDialogPassword) buttons = QtGui.QDialogButtonBox(self.testDialog) but = buttons.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('clicked()'),self.testDialog.close) but = buttons.addButton(QtGui.QDialogButtonBox.Apply) self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.showTests,indx)) line = QtGui.QHBoxLayout() line.addStretch(0.2) line.addWidget(buttons) line.addStretch(0.2) self.testDialog.layout().addLayout(line) self.testDialog.show() self.testDialog.raise_() def showTests(self,indx): self.testDialog.close() if hasattr(self,'testMessage'): self.testMessage.hide() self.testMessage.deleteLater() else: self.connect(self,QtCore.SIGNAL('testMessage'),self.updateTestReport) message = '' self.testMessage = QtGui.QDialog(self) self.testMessage.setWindowTitle('Testing remote server') self.testMessage.setLayout(QtGui.QVBoxLayout()) self.testMessage.layout().addWidget(QtGui.QLabel('Test existance of CCP4 directory and temp directory',self.testMessage)) self.testReport = QtGui.QTextEdit(self.testDialog) self.testMessage.layout().addWidget(self.testReport) buttons = QtGui.QDialogButtonBox(self.testDialog) but = buttons.addButton(QtGui.QDialogButtonBox.Cancel) self.testMessage.layout().addWidget(buttons) self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.cancelTests,indx,True)) self.testMessage.show() self.testMessage.raise_() self.connect(CCP4Modules.JOBCONTROLLER(),QtCore.SIGNAL('testMessage'),self.updateTestReport) import UtilityThread if not hasattr(self,'testThreads'): self.testThreads = {} self.testThreads[indx] = UtilityThread.UtilityThread(functools.partial(self.runTests,indx)) self.testThreads[indx].finished.connect(functools.partial(self.cancelTests,indx)) self.testThreads[indx].start() #self.runTests(indx) def updateTestReport(self,message): self.testReport.setReadOnly(False) mess=self.testReport.toPlainText() + '\n' + message self.testReport.setPlainText(mess) self.testReport.setReadOnly(True) self.testReport.repaint() def cancelTests(self,indx,force=False): if getattr(self,'testThreads',None) is not None and len(self.testThreads)>indx: if force: self.testThreads[indx].exit() del self.testThreads[indx] if force: self.testMessage.close() def runTests(self,indx): sG = self.widgets['SERVERGROUP'+str(indx)] remoteFileList=[sG.widgets['ccp4Dir'].model.__str__()] if sG.widgets['mechanism'].model.__str__() in ['ssh','qsub_remote']: remoteFileList.append(sG.widgets['tempDir'].model.__str__()) if sG.widgets['timeout'].model.isSet(): timeout = float(sG.widgets['timeout'].model) else: timeout = None maxTries = int(sG.widgets['maxTries'].model) for server in sG.widgets['serverList'].model: #print 'testing server',server self.emit(QtCore.SIGNAL('testMessage'), 'Testing '+str(server)+'\n') try: ret = CCP4Modules.JOBCONTROLLER().testRemoteFiles(machine=str(server),username=str(self.testDialogUsername.text()), password = str(self.testDialogPassword.text()), remoteFileList=remoteFileList,timeout=timeout,maxTries=maxTries) message = '' labelList = ['CCP4 distro','Tmp dir'] for ii in range(len(ret)): message = message + labelList[ii] + ': ' + str(ret[ii]) +'\n' except CException as e: message = ' Failed connection\n ' + e.description()[0] + '\n ' + str(e._reports[0]['details']) +'\n' except Exception as e: message = ' Failed connection \n'+str(e) self.emit(QtCore.SIGNAL('testMessage'), message) def doApply(self,source): #print 'CServerSetupWindow.doApply', self.widgets['SERVERGROUP1'].widgets['serverList'].listWidget.count() invalidList = [] for key in self.widgets.keys(): self.widgets[key].updateModelFromView() #print 'CServerSetupWindow.doApply',key,self.widgets[key].widgets['mechanism'].currentIndex(),self.container.get(key).mechanism self.widgets[key].validate() if self.widgets[key].isValid is not None and not self.widgets[key].isValid: invalidList.append(key) #print 'CServerSetupWindow.doApply invalidList',invalidList if len(invalidList)>0: QtGui.QMessageBox.warning(self,'Server setup','Invalid data - not saved') else: self.container.save(source) #QtGui.QDialog.close(self) self.setAltSourceButton() import CCP4Modules CCP4Modules.JOBCONTROLLER().resetServersEnabled() def close(self): QtGui.QDialog.close(self) def help(self): CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='servers.html',newTab=True) class CRemoteStatus(QtGui.QDialog): def __init__(self,parent): QtGui.QDialog.__init__(self,parent) self.setModal(False) self.setLayout(QtGui.QVBoxLayout()) self.advice = QtGui.QLabel(self) self.layout().addWidget(self.advice) self.jobList = QtGui.QListWidget(self) self.layout().addWidget(self.jobList ) butLayout = QtGui.QHBoxLayout() for label,connect in [['Close',self.close]]: but = QtGui.QPushButton(label,self) self.connect(but,QtCore.SIGNAL('released()'),connect) butLayout.addWidget(but) self.layout().addLayout(butLayout) def load(self,text='',title=None,advice=None): if title is not None: self.setWindowTitle(title) else: self.setWindowTitle('') if advice is not None: self.advice.setText(advice) else: self.advice.setText('') self.jobList.clear() lineList = text.split('\n') for line in lineList: self.jobList.addItem(line) class CPasswordEntry(QtGui.QDialog): def __init__(self,parent,label='Please reenter password',closeMessage='Ignore remote jobs'): QtGui.QDialog.__init__(self,parent) self.setModal(False) self.setLayout(QtGui.QVBoxLayout()) self.layout().addWidget(QtGui.QLabel(label)) self.passwordEntry = QtGui.QLineEdit(self) self.passwordEntry.setEchoMode(QtGui.QLineEdit.Password) self.layout().addWidget(self.passwordEntry) butLayout = QtGui.QHBoxLayout() for label,connect in [['Apply',self.sendPass],[closeMessage,self.close]]: but = QtGui.QPushButton(label,self) butLayout.addWidget(but) self.connect(but,QtCore.SIGNAL('released()'),connect) self.layout().addLayout(butLayout) def sendPass(self): passw = str(self.passwordEntry.text()) #print 'sendPass',passw self.emit(QtCore.SIGNAL('passwordEntered'),passw) self.close() class CListProcesses(QtGui.QDialog): COLUMNHEADERS = [ 'Project','Job','Taskname','Running process','Pid','Running time','Executable' ] def __init__(self,parent): QtGui.QDialog.__init__(self,parent) self.setWindowTitle('CCP4i2 List running jobs and processes') self.setLayout(QtGui.QVBoxLayout()) self.tableWidget = QtGui.QTableWidget(self) self.tableWidget.setColumnCount(len(self.COLUMNHEADERS)) self.tableWidget.setMinimumWidth(300) self.tableWidget.setColumnWidth(1,50) self.tableWidget.setColumnWidth(4,50) self.tableWidget.setColumnWidth(6,250) self.layout().addWidget(self.tableWidget) butLayout = QtGui.QHBoxLayout() butLayout.addStretch(3) for label,connect in [['Update',self.load],['Help',self.help],['Close',self.close]]: but = QtGui.QPushButton(label,self) self.connect(but,QtCore.SIGNAL('released()'),connect) butLayout.addWidget(but) butLayout.addStretch(3) self.layout().addLayout(butLayout) self.projectNameCache = {} def projectName(self,projectId): if not self.projectNameCache.has_key(projectId): self.projectNameCache[projectId] = CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId,'projectname') return self.projectNameCache[projectId] def load(self): import time now = time.time() procDict = CCP4Modules.JOBCONTROLLER().listLocalProcesses(containsList=['ccp4']) #print 'CListProcesses.load',procDict runningJobs = CCP4Modules.PROJECTSMANAGER().db().getRunningJobs() self.tableWidget.clear() self.tableWidget.setRowCount(0) for ii in range(len(self.COLUMNHEADERS)): self.tableWidget.setHorizontalHeaderItem(ii,QtGui.QTableWidgetItem(self.COLUMNHEADERS[ii])) for jobId,jobNumber,taskName,projectId,processId,parentJobId in runningJobs: if parentJobId is None and processId is not None: taskTitle = CCP4Modules.TASKMANAGER().getTitle(taskName) #print 'CListProcesses.load',jobId,jobNumber,processId projectName = self.projectName(projectId) if procDict.has_key(processId): self.addRow(now,projectName,jobNumber,taskTitle,procDict[processId]) for child in procDict[processId]['children']: if procDict.has_key(child): self.addRow(now,'','','',procDict[child]) else: self.addRow(now,projectName,jobNumber,taskTitle,None) def addRow(self,now,projectName,jobNumber,taskName,procDict): row = self.tableWidget.rowCount() self.tableWidget.insertRow(row) self.tableWidget.setItem(row,0, QtGui.QTableWidgetItem(str(projectName))) self.tableWidget.setItem(row,1, QtGui.QTableWidgetItem(str(jobNumber))) self.tableWidget.setItem(row,2, QtGui.QTableWidgetItem(str(taskName))) if procDict is None: self.tableWidget.setItem(row,3, QtGui.QTableWidgetItem('No process found')) else: self.tableWidget.setItem(row,3, QtGui.QTableWidgetItem(str(procDict['name']))) self.tableWidget.setItem(row,4, QtGui.QTableWidgetItem(str(procDict['pid']))) self.tableWidget.setItem(row,5, QtGui.QTableWidgetItem('%.1f' % (now-procDict['create_time']))) self.tableWidget.setItem(row,6, QtGui.QTableWidgetItem(str(procDict['exe']))) def help(self): CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='tutorial',target='running')