source: palm/trunk/SCRIPTS/palm_wd @ 1618

Last change on this file since 1618 was 1618, checked in by maronga, 9 years ago

watchdog is now steered via configuration file

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 35.0 KB
Line 
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#--------------------------------------------------------------------------------#
4# This file is part of PALM.
5#
6# PALM is free software: you can redistribute it and/or modify it under the terms
7# of the GNU General Public License as published by the Free Software Foundation,
8# either version 3 of the License, or (at your option) any later version.
9#
10# PALM is distributed in the hope that it will be useful, but WITHOUT ANY
11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# PALM. If not, see <http://www.gnu.org/licenses/>.
16#
17# Copyright 1997-2015  Leibniz Universitaet Hannover
18#--------------------------------------------------------------------------------#
19#
20# Current revisions:
21# -----------------
22# Added steering via configuration file .wd.config, to be place in the local
23# palm directory. Watchdog save files are automatically created upon first start.
24#
25# Former revisions:
26# -----------------
27# $Id: palm_wd 1618 2015-07-13 06:52:15Z maronga $
28#
29# 1613 2015-07-08 14:53:29Z maronga
30# Bugfix: tooltip for queuing name did not show up on first update.
31# New: added contect menu for showing the parameter file and the run control
32# output
33#
34# 1611 2015-07-07 12:23:22Z maronga
35# Initial revision
36#
37#
38# Description:
39# ------------
40# PALM watchdog client for monitoring batch jobs on a variety of hosts specified
41# by the user
42#
43# Instructions:
44# -------------
45# 1) Modify the header section of palm_wd
46# 2) Move .wd.olddata and .wd.newdata to your palm directory
47#    (e.g. /home/user/current_version/.wd.newdata etc.)
48# 3) Modify a copy of palm_wdd for each host to be monitored and move it to the
49#    respective hosts
50# 4) Start the client either from mrungui or from shell by "nohup palm_wd&"
51
52# To do:
53# ------
54# 1) Add "Options", "Help" and "Manual"
55# 2) Move user settings to a configuration file
56#------------------------------------------------------------------------------!
57
58import ConfigParser
59import datetime
60import os
61from PyQt4 import QtGui, QtCore, uic
62from PyQt4.QtCore import pyqtSlot,SIGNAL,SLOT
63import shutil
64import subprocess as sub
65import sys
66import time
67
68# Determine PALM directories
69try: 
70   devnull = open(os.devnull, 'w')
71   out = sub.check_output("echo $PALM_BIN", shell=True, stderr=sub.STDOUT)
72   palm_bin = out.rstrip()
73   palm_dir = out.split("palm")[0] + "palm/" + out.split("palm")[1].split("/")[1]
74   out = None
75except:   
76   print "Error. $PALM_BIN is not set."
77   raise SystemExit
78
79
80# Read configuration file
81# First check if the configuration file exists
82if ( os.path.exists(palm_dir + '/.wd.config') == False ):
83    print "Error. No configuration file .wd.config found in " + palm_dir
84    raise SystemExit     
85
86config = ConfigParser.RawConfigParser()
87config.read(palm_dir + '/.wd.config')
88
89description = []
90hostname = []
91username = []
92
93for i in range(0,len(config.sections())):
94
95    description_tmp = config.sections()[i]
96 
97    if ( description_tmp != 'Settings' ):
98       description.append(description_tmp)
99       hostname.append(config.get(description_tmp, 'hostname'))
100       username.append(config.get(description_tmp, 'username')) 
101    else:
102       update_frequency = int(config.get(description_tmp, 'update_frequency'))*60000
103
104# Check if .wd.olddata and .wd.newdata exist. Otherwise create the files
105if ( os.path.exists(palm_dir + '/.wd.olddata') == False ):
106   print "No .wd.olddata found. Creating..."
107   file1 = open(palm_dir + '/.wd.olddata', 'a')
108   file1.close()
109   
110
111if ( os.path.exists(palm_dir + '/.wd.newdata') == False ):
112   print "No .wd.newdata found. Creating..." 
113   file1 = open(palm_dir + '/.wd.newdata', 'a')
114   file1.close()
115
116# Dummy variables for the jobname and the progressbars
117jobname = ""
118timestamp = ""
119pbars = []
120job_data_list = []
121
122class MessageBoxScroll(QtGui.QMessageBox):
123    def __init__(self):
124        QtGui.QMessageBox.__init__(self)
125        self.setSizeGripEnabled(True)
126
127    def event(self, e):
128        result = QtGui.QMessageBox.event(self, e)
129
130        self.setMinimumHeight(100)
131        self.setMaximumHeight(16777215)
132        self.setMinimumWidth(400)
133        self.setMaximumWidth(16777215)
134        self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
135
136        textEdit = self.findChild(QtGui.QTextEdit)
137        if textEdit != None :
138            textEdit.setMinimumHeight(800)
139            textEdit.setMaximumHeight(16777215)
140            textEdit.setMinimumWidth(1000)
141            textEdit.setMaximumWidth(16777215)
142            textEdit.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
143            f = QtGui.QFont('not_a_font') #
144            f.setStyleHint(f.TypeWriter, f.PreferDefault)
145            textEdit.setFont(f)
146
147        return result
148
149
150# MainWindow class
151class Watchdog(QtGui.QMainWindow):
152   
153    def __init__(self):
154        super(Watchdog, self).__init__()
155       
156        self.InitUi()   
157       
158   
159    # Initialized MainWindow UI
160    def InitUi(self):
161
162        # Load predefined mainwindow
163        uic.loadUi(palm_bin + '/palm_wd_files/wd.ui', self)
164
165        # Resize columns and set number of rows to zero. Column 6 is hidden and
166        # contains the remote jobname (e.g. hannover.174610)
167        self.table.setColumnWidth(0,230)
168        self.table.setColumnWidth(1,80)
169        self.table.setColumnWidth(2,50)
170        self.table.setColumnWidth(3,80)     
171        self.table.setColumnWidth(4,180)
172        self.table.setColumnWidth(5,115)   
173        self.table.setColumnHidden(6, True)
174        self.table.setRowCount(0) 
175   
176        # Display MainWindow
177        self.show()
178        QtGui.QApplication.processEvents()
179
180        # Start refresh timer. On timeout perform update
181        self.timer = QtCore.QTimer(self)
182        self.timer.timeout.connect(self.Refresh)
183        self.timer.setSingleShot(False)
184        self.timer.start(update_frequency)
185
186        # The timetimer counts the time since last update
187        self.timetimer= QtCore.QElapsedTimer()
188        self.timetimer.start()
189
190        # The labeltimer induces the update of the remaining time in the UI
191        self.labeltimer = QtCore.QTimer(self)
192        self.labeltimer.timeout.connect(self.UpdateLabel)
193        self.labeltimer.setSingleShot(False)
194
195        # Update in the UI will be performed at each 1/10 of the update interval
196        self.labeltimer.start(update_frequency/10)   
197        self.label.setText("Next update in " + str(update_frequency/1000/60) + "min")
198
199        # Update the list now
200        self.Refresh()
201       
202
203    # Add a custom context menu
204    def OpenContextMenu(self, position):
205        menu = QtGui.QMenu()
206       
207        # "Show details" -> fetch details for the selected job
208        detailsjobAction = menu.addAction('Show job details')     
209        detailsjobAction.setStatusTip('Display detailed info for job')
210        detailsjobAction.triggered.connect(self.ShowDetails)
211
212        # "Show parameter file" -> show the contents of PARIN
213        parinAction = menu.addAction('Show parameter file')     
214        parinAction.setStatusTip('Display the parameter file of the job (e.g. PARIN / _p3d)')
215        parinAction.triggered.connect(self.ShowPARIN)
216       
217        rcAction = menu.addAction('Show run control file')     
218        rcAction.setStatusTip('Display the current run control file (e.g. RUN_CONTROL / _rc)')
219        rcAction.triggered.connect(self.ShowRC)
220       
221        # "Cancel job" -> remove job from queuing system
222        canceljobAction = menu.addAction('Cancel job')
223        canceljobAction.setStatusTip('Remove job from queueing system')
224        canceljobAction.triggered.connect(self.CancelJob)
225       
226        # "Force stop" -> force termination of job
227        stopjobAction = menu.addAction('Force stop')
228        stopjobAction.setStatusTip('Terminate job properly')
229        stopjobAction.triggered.connect(self.DoStop)
230   
231        # "Force restart" -> force rermination and restart of job
232        restartjobAction = menu.addAction('Force restart')
233        restartjobAction.setStatusTip('Terminate job properly and initiate restart')
234        restartjobAction.triggered.connect(self.DoRestart)
235 
236        # "Remove from list" -> remove a completed job from the list
237        removefromlistAction = menu.addAction('Remove from list')     
238        removefromlistAction.setStatusTip('Remove finished job')
239        removefromlistAction.triggered.connect(lambda: self.RemoveFromList(-1))     
240
241       
242        # Activate/deactive contect menu items based on the status of the runs
243        state = self.table.item(self.table.currentRow(),3).text()
244        if (state == "Canceled" or state == "Completed" or state == "Terminated"):
245            detailsjobAction.setEnabled(False)
246            canceljobAction.setEnabled(False) 
247            removefromlistAction.setEnabled(True) 
248        else:
249            detailsjobAction.setEnabled(True)
250            canceljobAction.setEnabled(True) 
251            removefromlistAction.setEnabled(False)   
252 
253        if ( state == "Running" ):
254            stopjobAction.setEnabled(True) 
255            restartjobAction.setEnabled(True)   
256            parinAction.setEnabled(True)
257            rcAction.setEnabled(True)         
258        else:
259            stopjobAction.setEnabled(False) 
260            restartjobAction.setEnabled(False)   
261            parinAction.setEnabled(False)
262            rcAction.setEnabled(False) 
263           
264        # Show the context menu
265        action = menu.exec_(self.table.mapToGlobal(position))
266
267
268    # Collecting watchdog data and display in the UI
269    def Refresh(self):
270     
271        # Use global variables
272        global jobs_save
273        global pbars
274
275        # Deactivate timer processes until update is finished
276        # QtGui.QApplication.processEvents() updates the UI whenever needed
277        self.labeltimer.stop() 
278        self.label.setText("Updating...") 
279        QtGui.QApplication.processEvents()
280        self.setEnabled(False)
281        QtGui.QApplication.processEvents()
282
283        # Get current timestamp
284        timestamp = datetime.datetime.now().strftime("%d/%m/%Y %H:%M")
285       
286        # Open an empty file for the watchdog data       
287        file = open(palm_dir + '/.wd.newdata', 'w')
288       
289 
290        # Set all required variables to their initial values. Sorting must be
291        # disabled.
292        self.table.setRowCount(0) 
293        self.table.setSortingEnabled(False)
294        i = -1
295        job_data_list = []
296        pbars = []
297       
298        # Scan all hosts in the variable hostname. For each host perform the
299        # update loop
300        for h in range(0,len(hostname)):
301         
302            # Perform ssh command
303            host    = username[h] + "@" + hostname[h]
304            ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd queue " + username[h]],
305                           shell=False,
306                           stdout=sub.PIPE,
307                           stderr=sub.PIPE)
308            result = ssh.stdout.readlines()
309            result = filter(None, result)
310 
311 
312            # Delete all job data
313            job_data_tmp = []
314            job_data = []
315            job_data_n = []   
316            job_data_old = []
317            job_data_old_n = []
318            job_progress = 0
319
320            # In case of errors display error message
321            if ( len(result) < 1 ):
322                error = ssh.stderr.readlines()
323                if ( error != [] ):
324                    notify = QtGui.QMessageBox.warning(self,'Collect data',"Error. An error occured during read of job data for user " + username[h] +" for host " + description[h] + ".\n\nError message:\n" + ''.join(error))
325
326            # No error -> save job data
327            else:       
328
329                # Successively write to job_data
330                for j in range(0,len(result)):
331   
332                    job_data_tmp.append(j)
333                    job_data_tmp[j] = result[j].split(" ")
334                    job_data_tmp[j] = filter(None, job_data_tmp[j])
335 
336                    job_data.append(j)   
337                    job_data[j] = [job_data_tmp[j][0], description[h], job_data_tmp[j][2],                 
338                                   job_data_tmp[j][3], int(float(job_data_tmp[j][4])*100), job_data_tmp[j][5], 
339                                   job_data_tmp[j][1], job_data_tmp[j][6].rstrip()]
340                    job_data_n.append(j)
341                    job_data_n[j] = job_data_tmp[j][1]
342
343            del(result)
344
345
346
347            # Now read the data from the last update from file. These data are
348            # already in the prefered format
349            file2 = open(palm_dir + '/.wd.olddata', 'r')
350            result = file2.readlines()
351            file2.close()
352           
353            for j in range(0,len(result)):
354                if ( result[j].split()[1] == description[h] ):
355                    job_data_old.append(j)
356                    job_data_old[j] = result[j].split(" ")
357                    job_data_old[j] = filter(None, job_data_old[j])
358                    job_data_old[j] = [line.rstrip('\n') for line in job_data_old[j]]
359               
360                    job_data_old_n.append(j)
361                    job_data_old_n[j] = job_data_old[j][6]
362
363            # Merge the job_data and job_data_old to find completed, new and
364            # still queued jobs     
365            if ( len(job_data_n) > 0 and len(job_data_old_n) > 0 ):
366               jobs_full  = list(set(job_data_old_n) | set(job_data_n))
367               jobs_known    = list(set(job_data_old_n) & set(job_data_n))
368               jobs_complete = set(job_data_old_n) - set(job_data_n)
369               jobs_new      = set(job_data_n) - set(job_data_old_n)
370            elif ( len(job_data_n) > 0 ):
371               jobs_full  = job_data_n
372               jobs_known    = []
373               jobs_new = job_data_n
374               jobs_complete = []
375            elif ( len(job_data_old_n) > 0 ):
376               jobs_full  = job_data_old_n
377               jobs_known    = []
378               jobs_new = []
379               jobs_complete = job_data_old_n   
380            else:
381               jobs_full  = []
382               jobs_known    = []         
383               jobs_new = []
384               jobs_complete = []
385
386            # Display all known jobs
387            to_display = [list(job_data_n).index(item) for item in list(jobs_known) if list(jobs_known) and list(job_data_n)]
388            for j in range(0,len(to_display)):
389                i = i + 1
390                self.table.insertRow(i)
391                item = QtGui.QTableWidgetItem(job_data[to_display[j]][0])
392                item.setToolTip("Queuing name: " + job_data[to_display[j]][6])
393                self.table.setItem(i, 0, item)           
394                self.table.setItem(i, 1, QtGui.QTableWidgetItem(job_data[to_display[j]][1])) 
395                self.table.setItem(i, 2, QtGui.QTableWidgetItem(job_data[to_display[j]][2]))   
396                self.table.setItem(i, 3, QtGui.QTableWidgetItem(job_data[to_display[j]][3]))
397                item = QtGui.QTableWidgetItem(job_data[to_display[j]][5])
398                item.setToolTip("Estimated job start: " + job_data[to_display[j]][7])               
399                self.table.setItem(i, 5, item) 
400                self.table.setItem(i, 6, QtGui.QTableWidgetItem(job_data[to_display[j]][6])) 
401                self.table.item(i,2).setTextAlignment(QtCore.Qt.AlignRight)
402                self.table.item(i,5).setTextAlignment(QtCore.Qt.AlignRight)
403                pbars.append(i)
404                pbars[i] = QtGui.QProgressBar(self)
405                pbars[i].setValue(int(job_data[to_display[j]][4])) 
406 
407                # Apply a color depending on the status of the job
408                if ( job_data[to_display[j]][3] == "Running" ):
409                    self.table.setCellWidget(i,4,pbars[j]) 
410                    self.table.item(i,0).setBackground(QtGui.QColor(255, 251, 168))
411                    self.table.item(i,1).setBackground(QtGui.QColor(255, 251, 168))
412                    self.table.item(i,2).setBackground(QtGui.QColor(255, 251, 168))
413                    self.table.item(i,3).setBackground(QtGui.QColor(255, 251, 168))
414                    self.table.item(i,5).setBackground(QtGui.QColor(255, 251, 168))
415                    self.table.item(i,6).setBackground(QtGui.QColor(255, 251, 168))
416                else:
417                    pbars[j].setEnabled(False)
418                    self.table.setCellWidget(i,4,pbars[j])
419                    self.table.item(i,0).setBackground(QtGui.QColor(255, 112, 118))
420                    self.table.item(i,1).setBackground(QtGui.QColor(255, 112, 118))
421                    self.table.item(i,2).setBackground(QtGui.QColor(255, 112, 118))
422                    self.table.item(i,3).setBackground(QtGui.QColor(255, 112, 118))
423                    self.table.item(i,5).setBackground(QtGui.QColor(255, 112, 118))
424                    self.table.item(i,6).setBackground(QtGui.QColor(255, 112, 118))
425               
426                # Save the job data to file
427                job_data_list.append(i)
428                job_data_list[i] = job_data[to_display[j]][0]
429               
430                printstring = str(job_data[to_display[j]][0]) + " " + \
431                              str(job_data[to_display[j]][1]) + " " + \
432                              str(job_data[to_display[j]][2]) + " " + \
433                              str(job_data[to_display[j]][3]) + " " + \
434                              str(job_data[to_display[j]][4]) + " " + \
435                              str(job_data[to_display[j]][5]) + " " + \
436                              str(job_data[to_display[j]][6]) + " " + \
437                              str(job_data[to_display[j]][7]) + "\n"
438                file.write(printstring)
439
440
441            # Display all new jobs
442            to_display = [list(job_data_n).index(item) for item in list(jobs_new) if list(jobs_new) and list(job_data_n)]
443            for j in range(0,len(to_display)):
444                i = i + 1
445                self.table.insertRow(i)
446                item = QtGui.QTableWidgetItem(job_data[to_display[j]][0])
447                item.setToolTip("Queuing name: " + job_data[to_display[j]][6])
448                self.table.setItem(i, 0, item)   
449                self.table.setItem(i, 1, QtGui.QTableWidgetItem(job_data[to_display[j]][1])) 
450                self.table.setItem(i, 2, QtGui.QTableWidgetItem(job_data[to_display[j]][2]))   
451                self.table.setItem(i, 3, QtGui.QTableWidgetItem(job_data[to_display[j]][3])) 
452                item = QtGui.QTableWidgetItem(job_data[to_display[j]][5])
453                item.setToolTip("Estimated job start: " + job_data[to_display[j]][7])               
454                self.table.setItem(i, 5, item) 
455                self.table.setItem(i, 6, QtGui.QTableWidgetItem(job_data[to_display[j]][6])) 
456                self.table.item(i,2).setTextAlignment(QtCore.Qt.AlignRight)
457                self.table.item(i,5).setTextAlignment(QtCore.Qt.AlignRight)
458               
459                pbars.append(i)
460                pbars[i] = QtGui.QProgressBar(self)
461                pbars[i].setValue(int(job_data[to_display[j]][4]))   
462
463                # Apply a color depending on the status of the job
464                if ( job_data[to_display[j]][3] == "Running" ):
465                    self.table.setCellWidget(i,4,pbars[i])           
466                    self.table.item(i,0).setBackground(QtGui.QColor(255, 251, 168))
467                    self.table.item(i,1).setBackground(QtGui.QColor(255, 251, 168))
468                    self.table.item(i,2).setBackground(QtGui.QColor(255, 251, 168))
469                    self.table.item(i,3).setBackground(QtGui.QColor(255, 251, 168))
470                    self.table.item(i,5).setBackground(QtGui.QColor(255, 251, 168))
471                    self.table.item(i,6).setBackground(QtGui.QColor(255, 251, 168))
472                else:
473                    pbars[j].setEnabled(False)
474                    self.table.setCellWidget(i,4,pbars[i])
475                    self.table.item(i,0).setBackground(QtGui.QColor(255, 112, 118))
476                    self.table.item(i,1).setBackground(QtGui.QColor(255, 112, 118))
477                    self.table.item(i,2).setBackground(QtGui.QColor(255, 112, 118))
478                    self.table.item(i,3).setBackground(QtGui.QColor(255, 112, 118))
479                    self.table.item(i,5).setBackground(QtGui.QColor(255, 112, 118))
480                    self.table.item(i,6).setBackground(QtGui.QColor(255, 112, 118))
481         
482                # Save job data to file
483                job_data_list.append(i)
484                job_data_list[i] = job_data[to_display[j]][0]
485               
486                printstring = str(job_data[to_display[j]][0]) + " " + \
487                              str(job_data[to_display[j]][1]) + " " + \
488                              str(job_data[to_display[j]][2]) + " " + \
489                              str(job_data[to_display[j]][3]) + " " + \
490                              str(job_data[to_display[j]][4]) + " " + \
491                              str(job_data[to_display[j]][5]) + " " + \
492                              str(job_data[to_display[j]][6]) + " " + \
493                              str(job_data[to_display[j]][7]) + "\n"
494                file.write(printstring)
495
496
497            # Display all completed/canceled/aborted jobs. The watchdog cannot
498            # distinguish why the job has been finished
499            to_display = [list(job_data_old_n).index(item) for item in list(jobs_complete) if list(jobs_complete) and list(job_data_old_n)]
500            for j in range(0,len(to_display)):
501                i = i + 1
502                self.table.insertRow(i)
503                item = QtGui.QTableWidgetItem(job_data_old[to_display[j]][0])
504                item.setToolTip("Queuing name: " + job_data_old[to_display[j]][6])
505                self.table.setItem(i, 0, item)         
506                self.table.setItem(i, 1, QtGui.QTableWidgetItem(job_data_old[to_display[j]][1])) 
507                self.table.setItem(i, 2, QtGui.QTableWidgetItem(job_data_old[to_display[j]][2]))   
508                self.table.setItem(i, 3, QtGui.QTableWidgetItem(job_data_old[to_display[j]][3])) 
509                pbars.append(i)
510                pbars[j] = QtGui.QProgressBar(self)
511                pbars[j].setValue(int(job_data_old[to_display[j]][4])) 
512                pbars[j].setEnabled(False)
513                self.table.setCellWidget(i,4,pbars[j]) 
514                item = QtGui.QTableWidgetItem(job_data_old[to_display[j]][5])
515                item.setToolTip("Estimated job start: " + job_data_old[to_display[j]][7])               
516                self.table.setItem(i, 5, item) 
517                self.table.setItem(i, 6, QtGui.QTableWidgetItem(job_data_old[to_display[j]][6])) 
518                self.table.item(i,2).setTextAlignment(QtCore.Qt.AlignRight)
519                self.table.item(i,5).setTextAlignment(QtCore.Qt.AlignRight)
520                self.table.setItem(i, 3, QtGui.QTableWidgetItem("Completed"))
521               
522                # Apply a color depending on the status of the job
523                self.table.item(i,0).setBackground(QtGui.QColor(172, 252, 175))
524                self.table.item(i,1).setBackground(QtGui.QColor(172, 252, 175))
525                self.table.item(i,2).setBackground(QtGui.QColor(172, 252, 175))
526                self.table.item(i,3).setBackground(QtGui.QColor(172, 252, 175))
527                self.table.item(i,5).setBackground(QtGui.QColor(172, 252, 175))
528                self.table.item(i,6).setBackground(QtGui.QColor(172, 252, 175))
529
530         
531                # Save job data to file
532                job_data_list.append(i)
533                job_data_list[i] = job_data_old[to_display[j]][0]
534
535                printstring = str(job_data_old[to_display[j]][0]) + " " + \
536                              str(job_data_old[to_display[j]][1]) + " " + \
537                              str(job_data_old[to_display[j]][2]) + " " + \
538                              str(job_data_old[to_display[j]][3]) + " " + \
539                              str(job_data_old[to_display[j]][4]) + " " + \
540                              str(job_data_old[to_display[j]][5]) + " " + \
541                              str(job_data_old[to_display[j]][6]) + " " + \
542                              str(job_data_old[to_display[j]][7]) + "\n"
543                file.write(printstring)
544
545            del(jobs_complete)
546            del(jobs_new)
547            del(result)
548
549        file.close()
550
551       
552        # Update is complete, sorting can thus be re-enabled               
553        self.table.setSortingEnabled(True) 
554
555        # Update the logfile
556        if ( os.path.isfile(palm_dir + '/.wd.newdata') ):
557            shutil.copy(palm_dir + '/.wd.newdata',palm_dir + '/.wd.olddata')
558
559        # Re-enable timer processes             
560        self.setEnabled(True)
561        self.label2.setText("Last update: " + timestamp)
562        self.label.setText("Update complete.")
563        self.timetimer.restart()
564        self.timer.start()
565        self.labeltimer.start()
566        QtGui.QApplication.processEvents()
567       
568    # Canceling selected job from table
569    def CancelJob(self):
570
571        # Return internal jobname
572        jobname = self.table.item(self.table.currentRow(),6).text()
573 
574        # Check description of job in order to login on the correct host
575        descr = self.table.item(self.table.currentRow(),1).text()
576        for h in range(0,len(description)):
577            if ( descr == description[h] ):
578                host = username[h] + "@" + hostname[h]
579
580        ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd cancel " + jobname],
581                           shell=False,
582                           stdout=sub.PIPE,
583                           stderr=sub.PIPE)
584        result = ssh.stdout.readlines()
585        result = filter(None, result)
586       
587        # In case of error display a warning message
588        if ( len(result) == 0 ):
589            error = ssh.stderr.readlines()
590            notify = QtGui.QMessageBox.warning(self,'Cancel job',"Error. Could not cancel job " + jobname + ".\n\nError message:\n" + ''.join(error))
591
592        # Otherwise inform the user and mark the job in the table
593        else:
594            self.table.setItem(self.table.currentRow(),3,QtGui.QTableWidgetItem("Canceled"))
595            notify = QtGui.QMessageBox.information(self,'Cancel job',"Job" + jobname + " canceled!\n\nServer message:\n" + ''.join(result))
596
597
598    # Show detailed information on job. For documentation see above
599    def ShowDetails(self):
600
601        jobname = self.table.item(self.table.currentRow(),6).text()
602        descr   = self.table.item(self.table.currentRow(),1).text()
603        for h in range(0,len(description)):
604            if ( descr == description[h] ):
605                host = username[h] + "@" + hostname[h]
606
607        ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd check " + jobname],
608                           shell=False,
609                           stdout=sub.PIPE,
610                           stderr=sub.PIPE)
611        result = ssh.stdout.readlines()
612        result = filter(None, result)
613
614        if ( len(result) == 0 ):
615            error = ssh.stderr.readlines()
616            notify = QtGui.QMessageBox.warning(self,'Show job details',"Error. Could not fetch job details for " + jobname + ".\n\nError message:\n" + ''.join(error))
617        else:
618            notify = QtGui.QMessageBox.information(self,'Job details',"Details for job " + jobname + ":\n\n" + ''.join(result))
619
620
621    # Perform a forced stop on the job
622    def DoStop(self):
623
624        # Return internal jobname
625        jobname = self.table.item(self.table.currentRow(),6).text()
626 
627        # Check description of job in order to login on the correct host
628        descr = self.table.item(self.table.currentRow(),1).text()
629        for h in range(0,len(description)):
630            if ( descr == description[h] ):
631                host = username[h] + "@" + hostname[h]
632                user = username[h]
633
634        ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd stop " + user + " " + jobname],
635                           shell=False,
636                           stdout=sub.PIPE,
637                           stderr=sub.PIPE)
638        result = ssh.stdout.readlines()
639        result = filter(None, result) 
640       
641        # In case of error display a warning message
642        if ( len(result) == 0 ):
643            error = ssh.stderr.readlines()
644            notify = QtGui.QMessageBox.warning(self,'Proper termination of job',"Error. Could not stop job " + jobname + ".\n\nError message:\n" + ''.join(error))
645
646        # Otherwise inform the user and mark the job in the table
647        else:
648            self.table.setItem(self.table.currentRow(),3,QtGui.QTableWidgetItem("Terminated"))
649            notify = QtGui.QMessageBox.information(self,'Terminatie job',"Termination of job " + jobname + " was initiated!\n\nServer message:\n" + ''.join(result))
650
651
652    # Perform a forced stop on the job
653    def DoRestart(self):
654
655        # Return internal jobname
656        jobname = self.table.item(self.table.currentRow(),6).text()
657 
658        # Check description of job in order to login on the correct host
659        descr = self.table.item(self.table.currentRow(),1).text()
660        for h in range(0,len(description)):
661            if ( descr == description[h] ):
662                host = username[h] + "@" + hostname[h]
663                user = username[h]
664
665        ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd restart " + user + " " + jobname],
666                           shell=False,
667                           stdout=sub.PIPE,
668                           stderr=sub.PIPE)
669        result = ssh.stdout.readlines()
670        result = filter(None, result)
671       
672        # In case of error display a warning message
673        if ( len(result) == 0 ):
674            error = ssh.stderr.readlines()
675            notify = QtGui.QMessageBox.warning(self,'Proper termination of job',"Error. Could not stop job " + jobname + ".\n\nError message:\n" + ''.join(error))
676
677        # Otherwise inform the user and mark the job in the table
678        else:
679            self.table.setItem(self.table.currentRow(),3,QtGui.QTableWidgetItem("Terminated"))
680            notify = QtGui.QMessageBox.information(self,'Terminate job for restart',"Restart for job" + jobname + " was initiated!\n\nServer message:\n" + ''.join(result))
681           
682
683    # Read the PARIN file of the job
684    def ShowPARIN(self):
685
686        # Return internal jobname
687        jobname = self.table.item(self.table.currentRow(),6).text()
688        jobrealname = self.table.item(self.table.currentRow(),0).text()     
689 
690        # Check description of job in order to login on the correct host
691        descr = self.table.item(self.table.currentRow(),1).text()
692        for h in range(0,len(description)):
693            if ( descr == description[h] ):
694                host = username[h] + "@" + hostname[h]
695                user = username[h]
696
697        ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd parin " + user + " " + jobname],
698                           shell=False,
699                           stdout=sub.PIPE,
700                           stderr=sub.PIPE)
701        result = ssh.stdout.readlines()
702        result = filter(None, result)
703       
704        # In case of error display a warning message
705        if ( len(result) == 0 ):
706            error = ssh.stderr.readlines()
707            notify = QtGui.QMessageBox.warning(self,'Showing parameter file',"Error. Could not fetch parameter file for job " + jobrealname + " (" + jobname + ").\n\nError message:\n" + ''.join(error))
708
709        # Otherwise inform the user and mark the job in the table
710        else:
711           mb = MessageBoxScroll()
712           mb.setText("Parameter file for job: " + jobrealname + "")
713           mb.setDetailedText(''.join(result))
714           mb.exec_()
715
716    # Read the PARIN file of the job
717    def ShowRC(self):
718
719        # Return internal jobname and real job name
720        jobname = self.table.item(self.table.currentRow(),6).text()
721        jobrealname = self.table.item(self.table.currentRow(),0).text()     
722 
723        # Check description of job in order to login on the correct host
724        descr = self.table.item(self.table.currentRow(),1).text()
725        for h in range(0,len(description)):
726            if ( descr == description[h] ):
727                host = username[h] + "@" + hostname[h]
728                user = username[h]
729
730        ssh = sub.Popen(["ssh", "%s" % host, "/sw/tools/python/2.7.6/generic/bin/python palm_wdd rc " + user + " " + jobname],
731                           shell=False,
732                           stdout=sub.PIPE,
733                           stderr=sub.PIPE)
734        result = ssh.stdout.readlines()
735        result = filter(None, result)
736       
737        # In case of error display a warning message
738        if ( len(result) == 0 ):
739            error = ssh.stderr.readlines()
740            notify = QtGui.QMessageBox.warning(self,'Showing run control',"Error. Could not fetch run control file for job " + jobrealname + "(" + jobname + ").\n\nError message:\n" + ''.join(error))
741
742        # Otherwise inform the user and mark the job in the table
743        else:
744           mb = MessageBoxScroll()
745           lastline = result[len(result)-2].split()[2]
746           mb.setText("Simulated time for job " + jobrealname + " is currently: " + lastline)
747           mb.setDetailedText(''.join(result))
748           mb.exec_()
749
750    # Remove a job from list - removes the current row from the table and from
751    # save file
752    def RemoveFromList(self, row):
753        if ( row == -1 ):
754            row = self.table.currentRow()
755
756        # Read data from save file
757        job_to_delete = self.table.item(row,6).text()
758        self.table.removeRow(row)
759        file = open(palm_dir + '/.wd.olddata', 'r')
760        result = file.readlines()
761        result = filter(None, result)
762        file.close()
763        file = open(palm_dir + '/.wd.olddata', 'w')
764       
765        job_data_old = []
766       
767        if ( len(result) == 0 ):
768            notify = QtGui.QMessageBox.warning(self,'Read from .wd.olddata',"Error message:\n\nCould not read from file. I/O error.")
769        else: 
770            # Save data in array job_data_old
771            for j in range(0,len(result)):
772                job_data_old.append(j)
773                job_data_old[j] = result[j].split(" ")
774                job_data_old[j] = filter(None, job_data_old[j])
775                job_data_old[j] = [line.rstrip('\n') for line in job_data_old[j]]
776               
777                # Check if line j is the selected job, if not -> save to file
778                if ( job_data_old[j][6] != job_to_delete ):
779                    printstring = str(job_data_old[j][0]) + " " + \
780                                  str(job_data_old[j][1]) + " " + \
781                                  str(job_data_old[j][2]) + " " + \
782                                  str(job_data_old[j][3]) + " " + \
783                                  str(job_data_old[j][4]) + " " + \
784                                  str(job_data_old[j][5]) + " " + \
785                                  str(job_data_old[j][6]) + " " + \
786                                  str(job_data_old[j][7]) + "\n"
787                    file.write(printstring)
788           
789        file.close() 
790
791    # Remove all completed jobs from list
792    def ClearList(self):
793     
794       num_of_lines = self.table.rowCount()
795       
796       # Delete all lines with completed/canceled jobs from list. The counter
797       # must decrease as the line numbers would be messed up otherwise
798       for j in range(num_of_lines-1,-1,-1): 
799           state = self.table.item(j,3).text()
800           if ( state == "Completed" or state == "Canceled" or state == "Terminated"):
801              self.RemoveFromList(j)
802         
803    # Update the label
804    def UpdateLabel(self):
805        remaining_time = (update_frequency - self.timetimer.elapsed()) / 1000 / 60
806        self.label.setText("Next update in " + str(remaining_time) + " min")
807       
808
809# Main loop       
810def main():
811   
812    app = QtGui.QApplication(sys.argv)
813    res = Watchdog() 
814    sys.exit(app.exec_())
815
816
817if __name__ == '__main__':
818    main()   
Note: See TracBrowser for help on using the repository browser.