rda-python-common 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rda_python_common/PgCMD.py +603 -0
- rda_python_common/PgDBI.py +2306 -0
- rda_python_common/PgFile.py +3118 -0
- rda_python_common/PgLOG.py +1689 -0
- rda_python_common/PgLock.py +640 -0
- rda_python_common/PgOPT.py +1740 -0
- rda_python_common/PgSIG.py +1164 -0
- rda_python_common/PgSplit.py +299 -0
- rda_python_common/PgUtil.py +1854 -0
- rda_python_common/__init__.py +0 -0
- rda_python_common/pg_cmd.py +493 -0
- rda_python_common/pg_dbi.py +1885 -0
- rda_python_common/pg_file.py +2462 -0
- rda_python_common/pg_lock.py +533 -0
- rda_python_common/pg_log.py +1352 -0
- rda_python_common/pg_opt.py +1447 -0
- rda_python_common/pg_pass.py +92 -0
- rda_python_common/pg_sig.py +879 -0
- rda_python_common/pg_split.py +260 -0
- rda_python_common/pg_util.py +1534 -0
- rda_python_common/pgpassword.py +92 -0
- rda_python_common-2.0.0.dist-info/METADATA +20 -0
- rda_python_common-2.0.0.dist-info/RECORD +27 -0
- rda_python_common-2.0.0.dist-info/WHEEL +5 -0
- rda_python_common-2.0.0.dist-info/entry_points.txt +3 -0
- rda_python_common-2.0.0.dist-info/licenses/LICENSE +21 -0
- rda_python_common-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1689 @@
|
|
|
1
|
+
#
|
|
2
|
+
###############################################################################
|
|
3
|
+
#
|
|
4
|
+
# Title : PgLOG.py -- Module for logging messages.
|
|
5
|
+
# Author : Zaihua Ji, zji@ucar.edu
|
|
6
|
+
# Date : 03/02/2016
|
|
7
|
+
# 2025-01-10 transferred to package rda_python_common from
|
|
8
|
+
# https://github.com/NCAR/rda-shared-libraries.git
|
|
9
|
+
# Purpose : Python library module to log message and also do other things
|
|
10
|
+
# according to the value of logact, like display the error
|
|
11
|
+
# message on screen and exit script
|
|
12
|
+
#
|
|
13
|
+
# Github : https://github.com/NCAR/rda-python-common.git
|
|
14
|
+
#
|
|
15
|
+
###############################################################################
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
import pwd
|
|
21
|
+
import grp
|
|
22
|
+
import shlex
|
|
23
|
+
import smtplib
|
|
24
|
+
from email.message import EmailMessage
|
|
25
|
+
from subprocess import Popen, PIPE
|
|
26
|
+
from os import path as op
|
|
27
|
+
import time
|
|
28
|
+
import socket
|
|
29
|
+
import shutil
|
|
30
|
+
import traceback
|
|
31
|
+
from unidecode import unidecode
|
|
32
|
+
|
|
33
|
+
# define some constants for logging actions
|
|
34
|
+
MSGLOG = (0x00001) # logging message
|
|
35
|
+
WARNLG = (0x00002) # show logging message as warning
|
|
36
|
+
EXITLG = (0x00004) # exit after logging
|
|
37
|
+
LOGWRN = (0x00003) # MSGLOG|WARNLG
|
|
38
|
+
LOGEXT = (0x00005) # MSGLOG|EXITLG
|
|
39
|
+
WRNEXT = (0x00006) # WARNLG|EXITLG
|
|
40
|
+
LGWNEX = (0x00007) # MSGLOG|WARNLG|EXITLG
|
|
41
|
+
EMLLOG = (0x00008) # append message to email buffer
|
|
42
|
+
LGWNEM = (0x0000B) # MSGLOG|WARNLG|EMLLOG
|
|
43
|
+
LWEMEX = (0x0000F) # MSGLOG|WARNLG|EMLLOG|EXITLG
|
|
44
|
+
ERRLOG = (0x00010) # error log only, output to STDERR
|
|
45
|
+
LOGERR = (0x00011) # MSGLOG|ERRLOG
|
|
46
|
+
LGEREX = (0x00015) # MSGLOG|ERRLOG|EXITLG
|
|
47
|
+
LGEREM = (0x00019) # MSGLOG|ERRLOG|EMLLOG
|
|
48
|
+
DOLOCK = (0x00020) # action to lock table record(s)
|
|
49
|
+
ENDLCK = (0x00040) # action to end locking table record(s)
|
|
50
|
+
AUTOID = (0x00080) # action to retrieve the last auto added id
|
|
51
|
+
DODFLT = (0x00100) # action to set empty values to default ones
|
|
52
|
+
SNDEML = (0x00200) # action to send email now
|
|
53
|
+
RETMSG = (0x00400) # action to return the message back
|
|
54
|
+
FRCLOG = (0x00800) # force logging message
|
|
55
|
+
SEPLIN = (0x01000) # add a separating line for email/STDOUT/STDERR
|
|
56
|
+
BRKLIN = (0x02000) # add a line break for email/STDOUT/STDERR
|
|
57
|
+
EMLTOP = (0x04000) # prepend message to email buffer
|
|
58
|
+
RCDMSG = (0x00814) # make sure to record logging message
|
|
59
|
+
MISLOG = (0x00811) # cannot access logfile
|
|
60
|
+
EMLSUM = (0x08000) # record as email summary
|
|
61
|
+
EMEROL = (0x10000) # record error as email only
|
|
62
|
+
EMLALL = (0x1D208) # all email acts
|
|
63
|
+
DOSUDO = (0x20000) # add 'sudo -u PGLOG['GDEXUSER']'
|
|
64
|
+
NOTLOG = (0x40000) # do not log any thing
|
|
65
|
+
OVRIDE = (0x80000) # do override existing file or record
|
|
66
|
+
NOWAIT = (0x100000) # do not wait on globus task to finish
|
|
67
|
+
ADDTBL = (0x200000) # action to add a new table if it does not exist
|
|
68
|
+
SKPTRC = (0x400000) # action to skip tracing when log errors
|
|
69
|
+
UCNAME = (0x800000) # action to change query field names to upper case
|
|
70
|
+
UCLWEX = (0x800015) # UCNAME|MSGLOG|WARNLG|EXITLG
|
|
71
|
+
PFSIZE = (0x1000000) # total file size under a path
|
|
72
|
+
|
|
73
|
+
SUCCESS = 1 # Successful function call
|
|
74
|
+
FINISH = 2 # go through a function, including time out
|
|
75
|
+
FAILURE = 0 # Unsuccessful function call
|
|
76
|
+
|
|
77
|
+
PGLOG = { # more defined in untaint_suid() with environment variables
|
|
78
|
+
'EMLADDR' : '',
|
|
79
|
+
'CCDADDR' : '',
|
|
80
|
+
'SEPLINE' : "===========================================================\n",
|
|
81
|
+
'TWOGBS' : 2147483648,
|
|
82
|
+
'ONEGBS' : 1073741824,
|
|
83
|
+
'MINSIZE' : 100, # minimal file size in bytes to be valid
|
|
84
|
+
'LOGMASK' : (0xFFFFFF), # log mask to turn off certain log action bits
|
|
85
|
+
'BCKGRND' : 0, # background process flag -b
|
|
86
|
+
'ERRCNT' : 0, # record number of errors for email
|
|
87
|
+
'ERRMSG' : '', # record error message for email
|
|
88
|
+
'SUMMSG' : '', # record summary message for email
|
|
89
|
+
'EMLMSG' : '', # record detail message for email
|
|
90
|
+
'PRGMSG' : '', # record progressing message for email, replaced each time
|
|
91
|
+
'GMTZ' : 0, # 0 - use local time, 1 - use greenwich mean time
|
|
92
|
+
'NOLEAP' : 0, # 1 - skip 29 of Feburary while add days to date
|
|
93
|
+
'GMTDIFF' : 6, # gmt is 6 hours ahead of us
|
|
94
|
+
'CURUID' : None, # the login name who executes the program
|
|
95
|
+
'SETUID' : '', # the login name for suid if it is different to the CURUID
|
|
96
|
+
'FILEMODE': 0o664, # default 8-base file mode
|
|
97
|
+
'EXECMODE': 0o775, # default 8-base executable file mode or directory mode
|
|
98
|
+
'ARCHHOST': "hpss", # change to hpss from mss
|
|
99
|
+
'ARCHROOT': "/FS/DECS", # root path for segregated tape on hpss
|
|
100
|
+
'BACKROOT': "/DRDATA/DECS", # backup path for desaster recovering tape on hpss
|
|
101
|
+
'OLDAROOT': "/FS/DSS", # old root path on hpss
|
|
102
|
+
'OLDBROOT': "/DRDATA/DSS", # old backup tape on hpss
|
|
103
|
+
'GDEXUSER' : "gdexdata", # common gdex user name
|
|
104
|
+
'GDEXEMAIL' : "zji", # specialist to receipt email intead of common gdex user name
|
|
105
|
+
'SUDOGDEX' : 0, # 1 to allow sudo to PGLOG['GDEXUSER']
|
|
106
|
+
'HOSTNAME' : '', # current host name the process in running on
|
|
107
|
+
'OBJCTSTR' : "object",
|
|
108
|
+
'BACKUPNM' : "quasar",
|
|
109
|
+
'DRDATANM' : "drdata",
|
|
110
|
+
'GPFSNAME' : "glade",
|
|
111
|
+
'SLMNAME' : "SLURM",
|
|
112
|
+
'PBSNAME' : "PBS",
|
|
113
|
+
'DSIDCHRS' : "d",
|
|
114
|
+
'DOSHELL' : False,
|
|
115
|
+
'NEWDSID' : True,
|
|
116
|
+
'PUSGDIR' : None,
|
|
117
|
+
'BCHHOSTS' : "PBS",
|
|
118
|
+
'HOSTTYPE' : 'dav', # default HOSTTYPE
|
|
119
|
+
'EMLMAX' : 256, # up limit of email line count
|
|
120
|
+
'PGBATCH' : '', # current batch service name, SLURM or PBS
|
|
121
|
+
'PGBINDIR' : '',
|
|
122
|
+
'SLMTIME' : 604800, # max runtime for SLURM bath job, (7x24x60x60 seconds)
|
|
123
|
+
'PBSTIME' : 86400, # max runtime for PBS bath job, (24x60x60 seconds)
|
|
124
|
+
'MSSGRP' : None, # set if set to different HPSS group
|
|
125
|
+
'GDEXGRP' : "decs",
|
|
126
|
+
'EMLSEND' : None, # path to sendmail, None if not exists
|
|
127
|
+
'DSCHECK' : None, # carry some cached dscheck information
|
|
128
|
+
'PGDBBUF' : None, # reference to a connected database object
|
|
129
|
+
'HPSSLMT' : 10, # up limit of HPSS streams
|
|
130
|
+
'NOQUIT' : 0, # do not quit if this flag is set for daemons
|
|
131
|
+
'DBRETRY' : 2, # db retry count after error
|
|
132
|
+
'TIMEOUT' : 15, # default timeout (in seconds) for tosystem()
|
|
133
|
+
'CMDTIME' : 120, # default command time (in seconds) for pgsystem() to record end time
|
|
134
|
+
'SYSERR' : None, # cache the error message generated inside pgsystem()
|
|
135
|
+
'ERR2STD' : [], # if non-empty reference to array of strings, change stderr to stdout if match
|
|
136
|
+
'STD2ERR' : [], # if non-empty reference to array of strings, change stdout to stderr if match
|
|
137
|
+
'MISSFILE': "No such file or directory",
|
|
138
|
+
'GITHUB' : "https://github.com" , # github server
|
|
139
|
+
'EMLSRVR' : "ndir.ucar.edu", # UCAR email server and port
|
|
140
|
+
'EMLPORT' : 25
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
PGLOG['RDAUSER'] = PGLOG['GDEXUSER']
|
|
144
|
+
PGLOG['RDAGRP'] = PGLOG['GDEXGRP']
|
|
145
|
+
PGLOG['RDAEMAIL'] = PGLOG['GDEXEMAIL']
|
|
146
|
+
PGLOG['SUDORDA'] = PGLOG['SUDOGDEX']
|
|
147
|
+
|
|
148
|
+
HOSTTYPES = {
|
|
149
|
+
'rda' : 'dsg_mach',
|
|
150
|
+
'casper' : 'dav',
|
|
151
|
+
'crhtc' : 'dav',
|
|
152
|
+
'cron' : 'dav',
|
|
153
|
+
'cheyenne' : 'ch',
|
|
154
|
+
'chadmin' : 'ch'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
CPID = {
|
|
158
|
+
'PID' : "",
|
|
159
|
+
'CTM' : int(time.time()),
|
|
160
|
+
'CMD' : "",
|
|
161
|
+
'CPID' : "",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
BCHCMDS = {'PBS' : 'qsub', 'SLURM' : 'sbatch'}
|
|
165
|
+
|
|
166
|
+
# global dists to cashe information
|
|
167
|
+
COMMANDS = {}
|
|
168
|
+
SLMHOSTS = []
|
|
169
|
+
SLMSTATS = {}
|
|
170
|
+
PBSHOSTS = []
|
|
171
|
+
PBSSTATS = {}
|
|
172
|
+
|
|
173
|
+
#
|
|
174
|
+
# get time string in format YYMMDDHHNNSS for given ctime; or current time if ctime is 0
|
|
175
|
+
#
|
|
176
|
+
def current_datetime(ctime = 0):
|
|
177
|
+
|
|
178
|
+
if PGLOG['GMTZ']:
|
|
179
|
+
dt = time.gmtime(ctime) if ctime else time.gmtime()
|
|
180
|
+
else:
|
|
181
|
+
dt = time.localtime(ctime) if ctime else time.localtime()
|
|
182
|
+
|
|
183
|
+
return "{:02}{:02}{:02}{:02}{:02}{:02}".format(dt[0], dt[1], dt[2], dt[3], dt[4], dt[5])
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# get an environment variable and untaint it
|
|
187
|
+
#
|
|
188
|
+
def get_environment(name, default = None, logact = 0):
|
|
189
|
+
|
|
190
|
+
env = os.getenv(name, default)
|
|
191
|
+
if env is None and logact:
|
|
192
|
+
pglog(name + ": Environment variable is not defined", logact)
|
|
193
|
+
|
|
194
|
+
return env
|
|
195
|
+
|
|
196
|
+
#
|
|
197
|
+
# cache the msg string to global email entries for later call of send_email()
|
|
198
|
+
#
|
|
199
|
+
def set_email(msg, logact = 0):
|
|
200
|
+
|
|
201
|
+
if logact and msg:
|
|
202
|
+
if logact&EMLTOP:
|
|
203
|
+
if PGLOG['PRGMSG']:
|
|
204
|
+
msg = PGLOG['PRGMSG'] + "\n" + msg
|
|
205
|
+
PGLOG['PRGMSG'] = ""
|
|
206
|
+
if PGLOG['ERRCNT'] == 0:
|
|
207
|
+
if not re.search(r'\n$', msg): msg += "!\n"
|
|
208
|
+
else:
|
|
209
|
+
if PGLOG['ERRCNT'] == 1:
|
|
210
|
+
msg += " with 1 Error:\n"
|
|
211
|
+
else:
|
|
212
|
+
msg += " with {} Errors:\n".format(PGLOG['ERRCNT'])
|
|
213
|
+
msg += break_long_string(PGLOG['ERRMSG'], 512, None, PGLOG['EMLMAX']/2, None, 50, 25)
|
|
214
|
+
PGLOG['ERRCNT'] = 0
|
|
215
|
+
PGLOG['ERRMSG'] = ''
|
|
216
|
+
|
|
217
|
+
if PGLOG['SUMMSG']:
|
|
218
|
+
msg += PGLOG['SEPLINE']
|
|
219
|
+
if PGLOG['SUMMSG']: msg += "Summary:\n"
|
|
220
|
+
msg += break_long_string(PGLOG['SUMMSG'], 512, None, PGLOG['EMLMAX']/2, None, 50, 25)
|
|
221
|
+
|
|
222
|
+
if PGLOG['EMLMSG']:
|
|
223
|
+
msg += PGLOG['SEPLINE']
|
|
224
|
+
if PGLOG['SUMMSG']: msg += "Detail Information:\n"
|
|
225
|
+
|
|
226
|
+
PGLOG['EMLMSG'] = msg + break_long_string(PGLOG['EMLMSG'], 512, None, PGLOG['EMLMAX'], None, 50, 40)
|
|
227
|
+
PGLOG['SUMMSG'] = "" # in case not
|
|
228
|
+
else:
|
|
229
|
+
if logact&ERRLOG: # record error for email summary
|
|
230
|
+
PGLOG['ERRCNT'] += 1
|
|
231
|
+
if logact&BRKLIN: PGLOG['ERRMSG'] += "\n"
|
|
232
|
+
PGLOG['ERRMSG'] += "{}. {}".format(PGLOG['ERRCNT'], msg)
|
|
233
|
+
elif logact&EMLSUM:
|
|
234
|
+
if PGLOG['SUMMSG']:
|
|
235
|
+
if logact&BRKLIN: PGLOG['SUMMSG'] += "\n"
|
|
236
|
+
if logact&SEPLIN: PGLOG['SUMMSG'] += PGLOG['SEPLINE']
|
|
237
|
+
PGLOG['SUMMSG'] += msg # append
|
|
238
|
+
|
|
239
|
+
if logact&EMLLOG:
|
|
240
|
+
if PGLOG['EMLMSG']:
|
|
241
|
+
if logact&BRKLIN: PGLOG['EMLMSG'] += "\n"
|
|
242
|
+
if logact&SEPLIN: PGLOG['EMLMSG'] += PGLOG['SEPLINE']
|
|
243
|
+
PGLOG['EMLMSG'] += msg # append
|
|
244
|
+
elif msg is None:
|
|
245
|
+
PGLOG['EMLMSG'] = ""
|
|
246
|
+
|
|
247
|
+
#
|
|
248
|
+
# retrieve the cached email message
|
|
249
|
+
#
|
|
250
|
+
def get_email():
|
|
251
|
+
return PGLOG['EMLMSG']
|
|
252
|
+
|
|
253
|
+
#
|
|
254
|
+
# send a customized email with all entries included
|
|
255
|
+
#
|
|
256
|
+
def send_customized_email(logmsg, emlmsg, logact = LOGWRN):
|
|
257
|
+
|
|
258
|
+
entries = {
|
|
259
|
+
'fr' : ["From", 1, None],
|
|
260
|
+
'to' : ["To", 1, None],
|
|
261
|
+
'cc' : ["Cc", 0, ''],
|
|
262
|
+
'sb' : ["Subject", 1, None]
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if logmsg:
|
|
266
|
+
logmsg += ': '
|
|
267
|
+
else:
|
|
268
|
+
logmsg = ''
|
|
269
|
+
msg = emlmsg
|
|
270
|
+
for ekey in entries:
|
|
271
|
+
entry = entries[ekey][0]
|
|
272
|
+
ms = re.search(r'(^|\n)({}: *(.*)\n)'.format(entry), emlmsg, re.I)
|
|
273
|
+
if ms:
|
|
274
|
+
vals = ms.groups()
|
|
275
|
+
msg = msg.replace(vals[1], '')
|
|
276
|
+
if vals[2]: entries[ekey][2] = vals[2]
|
|
277
|
+
elif entries[ekey][1]:
|
|
278
|
+
return pglog("{}Missing Entry '{}' for sending email".format(logmsg, entry), logact|ERRLOG)
|
|
279
|
+
|
|
280
|
+
ret = send_python_email(entries['sb'][2], entries['to'][2], msg, entries['fr'][2], entries['cc'][2], logact)
|
|
281
|
+
if ret == SUCCESS or not PGLOG['EMLSEND']: return ret
|
|
282
|
+
|
|
283
|
+
# try commandline sendmail
|
|
284
|
+
ret = pgsystem(PGLOG['EMLSEND'], logact, 4, emlmsg)
|
|
285
|
+
logmsg += "Email " + entries['to'][2]
|
|
286
|
+
if entries['cc'][2]: logmsg += " Cc'd " + entries['cc'][2]
|
|
287
|
+
logmsg += " Subject: " + entries['sb'][2]
|
|
288
|
+
if ret:
|
|
289
|
+
log_email(emlmsg)
|
|
290
|
+
pglog(logmsg, logact&(~EXITLG))
|
|
291
|
+
else:
|
|
292
|
+
errmsg = "Error sending email: " + logmsg
|
|
293
|
+
pglog(errmsg, (logact|ERRLOG)&~EXITLG)
|
|
294
|
+
|
|
295
|
+
return ret
|
|
296
|
+
|
|
297
|
+
#
|
|
298
|
+
# send an email; if empty msg send email message saved in PGLOG['EMLMSG'] instead
|
|
299
|
+
#
|
|
300
|
+
def send_email(subject = None, receiver = None, msg = None, sender = None, logact = LOGWRN):
|
|
301
|
+
|
|
302
|
+
return send_python_email(subject, receiver, msg, sender, None, logact)
|
|
303
|
+
|
|
304
|
+
#
|
|
305
|
+
# send an email via python module smtplib; if empty msg send email message saved
|
|
306
|
+
# in PGLOG['EMLMSG'] instead. pass cc = '' for skipping 'Cc: '
|
|
307
|
+
#
|
|
308
|
+
def send_python_email(subject = None, receiver = None, msg = None, sender = None, cc = None, logact = LOGWRN):
|
|
309
|
+
|
|
310
|
+
if not msg:
|
|
311
|
+
if PGLOG['EMLMSG']:
|
|
312
|
+
msg = PGLOG['EMLMSG']
|
|
313
|
+
PGLOG['EMLMSG'] = ''
|
|
314
|
+
else:
|
|
315
|
+
return ''
|
|
316
|
+
|
|
317
|
+
docc = False if cc else True
|
|
318
|
+
if not sender:
|
|
319
|
+
sender = PGLOG['CURUID']
|
|
320
|
+
if sender != PGLOG['GDEXUSER']: docc = False
|
|
321
|
+
if sender == PGLOG['GDEXUSER']: sender = PGLOG['GDEXEMAIL']
|
|
322
|
+
if sender.find('@') == -1: sender += "@ucar.edu"
|
|
323
|
+
if not receiver:
|
|
324
|
+
receiver = PGLOG['EMLADDR'] if PGLOG['EMLADDR'] else PGLOG['CURUID']
|
|
325
|
+
if receiver == PGLOG['GDEXUSER']: receiver = PGLOG['GDEXEMAIL']
|
|
326
|
+
if receiver.find('@') == -1: receiver += "@ucar.edu"
|
|
327
|
+
|
|
328
|
+
if docc and not re.match(PGLOG['GDEXUSER'], sender): add_carbon_copy(sender, 1)
|
|
329
|
+
emlmsg = EmailMessage()
|
|
330
|
+
emlmsg.set_content(msg)
|
|
331
|
+
emlmsg['From'] = sender
|
|
332
|
+
emlmsg['To'] = receiver
|
|
333
|
+
logmsg = "Email " + receiver
|
|
334
|
+
if cc == None: cc = PGLOG['CCDADDR']
|
|
335
|
+
if cc:
|
|
336
|
+
emlmsg['Cc'] = cc
|
|
337
|
+
logmsg += " Cc'd " + cc
|
|
338
|
+
if not subject: subject = "Message from {}-{}".format(PGLOG['HOSTNAME'], get_command())
|
|
339
|
+
# if not re.search(r'!$', subject): subject += '!'
|
|
340
|
+
emlmsg['Subject'] = subject
|
|
341
|
+
if CPID['CPID']: logmsg += " in " + CPID['CPID']
|
|
342
|
+
logmsg += ", Subject: {}\n".format(subject)
|
|
343
|
+
try:
|
|
344
|
+
eml = smtplib.SMTP(PGLOG['EMLSRVR'], PGLOG['EMLPORT'])
|
|
345
|
+
eml.send_message(emlmsg)
|
|
346
|
+
except smtplib.SMTPException as err:
|
|
347
|
+
errmsg = f"Error sending email:\n{err}\n{logmsg}"
|
|
348
|
+
return pglog(errmsg, (logact|ERRLOG)&~EXITLG)
|
|
349
|
+
finally:
|
|
350
|
+
eml.quit()
|
|
351
|
+
log_email(str(emlmsg))
|
|
352
|
+
pglog(logmsg, logact&~EXITLG)
|
|
353
|
+
return SUCCESS
|
|
354
|
+
|
|
355
|
+
#
|
|
356
|
+
# log email sent
|
|
357
|
+
#
|
|
358
|
+
def log_email(emlmsg):
|
|
359
|
+
|
|
360
|
+
if not CPID['PID']: CPID['PID'] = "{}-{}-{}".format(PGLOG['HOSTNAME'], get_command(), PGLOG['CURUID'])
|
|
361
|
+
cmdstr = "{} {} at {}\n".format(CPID['PID'], break_long_string(CPID['CMD'], 40, "...", 1), current_datetime())
|
|
362
|
+
fn = "{}/{}".format(PGLOG['LOGPATH'], PGLOG['EMLFILE'])
|
|
363
|
+
try:
|
|
364
|
+
f = open(fn, 'a')
|
|
365
|
+
f.write(cmdstr + emlmsg)
|
|
366
|
+
f.close()
|
|
367
|
+
except FileNotFoundError as e:
|
|
368
|
+
print(e)
|
|
369
|
+
|
|
370
|
+
#
|
|
371
|
+
# Function: cmdlog(cmdline)
|
|
372
|
+
# cmdline - program name and all arguments
|
|
373
|
+
# ctime - time (in seconds) when the command starts
|
|
374
|
+
#
|
|
375
|
+
def cmdlog(cmdline = None, ctime = 0, logact = None):
|
|
376
|
+
|
|
377
|
+
if logact is None: logact = MSGLOG|FRCLOG
|
|
378
|
+
if not ctime: ctime = int(time.time())
|
|
379
|
+
|
|
380
|
+
if not cmdline or re.match('(end|quit|exit|abort)', cmdline, re.I):
|
|
381
|
+
cmdline = cmdline.capitalize() if cmdline else "Ends"
|
|
382
|
+
cinfo = cmd_execute_time("{} {}".format(CPID['PID'], cmdline), (ctime - CPID['CTM'])) + ": "
|
|
383
|
+
if CPID['CPID']: cinfo += CPID['CPID'] + " <= "
|
|
384
|
+
cinfo += break_long_string(CPID['CMD'], 40, "...", 1)
|
|
385
|
+
if logact: pglog(cinfo, logact)
|
|
386
|
+
else:
|
|
387
|
+
cinfo = current_datetime(ctime)
|
|
388
|
+
if re.match(r'CPID \d+', cmdline):
|
|
389
|
+
CPID['PID'] = "{}({})-{}{}".format(PGLOG['HOSTNAME'], os.getpid(), PGLOG['CURUID'], cinfo)
|
|
390
|
+
if logact: pglog("{}: {}".format(CPID['PID'], cmdline), logact)
|
|
391
|
+
CPID['CPID'] = cmdline
|
|
392
|
+
elif CPID['PID'] and re.match(r'(starts|catches) ', cmdline):
|
|
393
|
+
if logact: pglog("{}: {} at {}".format(CPID['PID'], cmdline, cinfo), logact)
|
|
394
|
+
else:
|
|
395
|
+
CPID['PID'] = "{}({})-{}{}".format(PGLOG['HOSTNAME'], os.getpid(), PGLOG['CURUID'], cinfo)
|
|
396
|
+
if logact: pglog("{}: {}".format(CPID['PID'], cmdline), logact)
|
|
397
|
+
CPID['CMD'] = cmdline
|
|
398
|
+
|
|
399
|
+
CPID['CTM'] = ctime
|
|
400
|
+
|
|
401
|
+
#
|
|
402
|
+
# Function: pglog(msg, logact) return FAILURE or log message if not exit
|
|
403
|
+
# msg -- message to log
|
|
404
|
+
# locact -- logging actions: MSGLOG, WARNLG, ERRLOG, EXITLG, EMLLOG, & SNDEML
|
|
405
|
+
#
|
|
406
|
+
# log and display message/error and exit program according logact value
|
|
407
|
+
#
|
|
408
|
+
def pglog(msg, logact = MSGLOG):
|
|
409
|
+
|
|
410
|
+
retmsg = None
|
|
411
|
+
logact &= PGLOG['LOGMASK'] # filtering the log actions
|
|
412
|
+
if logact&RCDMSG: logact |= MSGLOG
|
|
413
|
+
if PGLOG['NOQUIT']: logact &= ~EXITLG
|
|
414
|
+
if logact&EMEROL:
|
|
415
|
+
if logact&EMLLOG: logact &= ~EMLLOG
|
|
416
|
+
if not logact&ERRLOG: logact &= ~EMEROL
|
|
417
|
+
|
|
418
|
+
msg = msg.lstrip() if msg else '' # remove leading whitespaces for logging message
|
|
419
|
+
if logact&EXITLG:
|
|
420
|
+
ext = "Exit 1 in {}\n".format(os.getcwd())
|
|
421
|
+
if msg: msg = msg.rstrip() + "; "
|
|
422
|
+
msg += ext
|
|
423
|
+
else:
|
|
424
|
+
if msg and not re.search(r'(\n|\r)$', msg): msg += "\n"
|
|
425
|
+
if logact&RETMSG: retmsg = msg
|
|
426
|
+
|
|
427
|
+
if logact&EMLALL:
|
|
428
|
+
if logact&SNDEML or not msg:
|
|
429
|
+
title = (msg if msg else "Message from {}-{}".format(PGLOG['HOSTNAME'], get_command()))
|
|
430
|
+
msg = title
|
|
431
|
+
send_email(title.rstrip())
|
|
432
|
+
elif msg:
|
|
433
|
+
set_email(msg, logact)
|
|
434
|
+
|
|
435
|
+
if not msg: return (retmsg if retmsg else FAILURE)
|
|
436
|
+
|
|
437
|
+
if logact&EXITLG and (PGLOG['EMLMSG'] or PGLOG['SUMMSG'] or PGLOG['ERRMSG'] or PGLOG['PRGMSG']):
|
|
438
|
+
if not logact&EMLALL: set_email(msg, logact)
|
|
439
|
+
title = "ABORTS {}-{}".format(PGLOG['HOSTNAME'], get_command())
|
|
440
|
+
set_email((("ABORTS " + CPID['PID']) if CPID['PID'] else title), EMLTOP)
|
|
441
|
+
msg = title + '\n' + msg
|
|
442
|
+
send_email(title)
|
|
443
|
+
|
|
444
|
+
if logact&LOGERR: # make sure error is always logged
|
|
445
|
+
msg = break_long_string(msg)
|
|
446
|
+
if logact&(ERRLOG|EXITLG):
|
|
447
|
+
cmdstr = get_error_command(int(time.time()), logact)
|
|
448
|
+
msg = cmdstr + msg
|
|
449
|
+
|
|
450
|
+
if not logact&NOTLOG:
|
|
451
|
+
if logact&ERRLOG:
|
|
452
|
+
if not PGLOG['ERRFILE']: PGLOG['ERRFILE'] = re.sub(r'.log$', '.err', PGLOG['LOGFILE'])
|
|
453
|
+
write_message(msg, f"{PGLOG['LOGPATH']}/{PGLOG['ERRFILE']}", logact)
|
|
454
|
+
if logact&EXITLG:
|
|
455
|
+
write_message(cmdstr, f"{PGLOG['LOGPATH']}/{PGLOG['LOGFILE']}", logact)
|
|
456
|
+
else:
|
|
457
|
+
write_message(msg, f"{PGLOG['LOGPATH']}/{PGLOG['LOGFILE']}", logact)
|
|
458
|
+
|
|
459
|
+
if not PGLOG['BCKGRND'] and logact&(ERRLOG|WARNLG):
|
|
460
|
+
write_message(msg, None, logact)
|
|
461
|
+
|
|
462
|
+
if logact&EXITLG:
|
|
463
|
+
pgexit(1)
|
|
464
|
+
else:
|
|
465
|
+
return (retmsg if retmsg else FAILURE)
|
|
466
|
+
|
|
467
|
+
#
|
|
468
|
+
# write a log message
|
|
469
|
+
#
|
|
470
|
+
def write_message(msg, file, logact):
|
|
471
|
+
|
|
472
|
+
doclose = False
|
|
473
|
+
errlog = logact&ERRLOG
|
|
474
|
+
if file:
|
|
475
|
+
try:
|
|
476
|
+
OUT = open(file, 'a')
|
|
477
|
+
doclose = True
|
|
478
|
+
except FileNotFoundError:
|
|
479
|
+
OUT = sys.stderr if logact&(ERRLOG|EXITLG) else sys.stdout
|
|
480
|
+
OUT.write(f"Log File not found: {file}")
|
|
481
|
+
else:
|
|
482
|
+
OUT = sys.stderr if logact&(ERRLOG|EXITLG) else sys.stdout
|
|
483
|
+
if logact&BRKLIN: OUT.write("\n")
|
|
484
|
+
if logact&SEPLIN: OUT.write(PGLOG['SEPLINE'])
|
|
485
|
+
OUT.write(msg)
|
|
486
|
+
if errlog and file and not logact&(EMLALL|SKPTRC): OUT.write(get_call_trace())
|
|
487
|
+
if doclose: OUT.close()
|
|
488
|
+
|
|
489
|
+
#
|
|
490
|
+
# check and disconnet database before exit
|
|
491
|
+
#
|
|
492
|
+
def pgexit(stat = 0):
|
|
493
|
+
|
|
494
|
+
if PGLOG['PGDBBUF']: PGLOG['PGDBBUF'].close()
|
|
495
|
+
sys.exit(stat)
|
|
496
|
+
|
|
497
|
+
#
|
|
498
|
+
# get a command string for error log dump
|
|
499
|
+
#
|
|
500
|
+
def get_error_command(ctime, logact):
|
|
501
|
+
|
|
502
|
+
if not CPID['PID']: CPID['PID'] = "{}-{}-{}".format(PGLOG['HOSTNAME'], get_command(), PGLOG['CURUID'])
|
|
503
|
+
cmdstr = "{} {}".format((("ABORTS" if logact&ERRLOG else "QUITS") if logact&EXITLG else "ERROR"), CPID['PID'])
|
|
504
|
+
cmdstr = cmd_execute_time(cmdstr, (ctime - CPID['CTM']))
|
|
505
|
+
if CPID['CPID']: cmdstr += " {} <=".format(CPID['CPID'])
|
|
506
|
+
cmdstr += " {} at {}\n".format(break_long_string(CPID['CMD'], 40, "...", 1), current_datetime(ctime))
|
|
507
|
+
|
|
508
|
+
return cmdstr
|
|
509
|
+
|
|
510
|
+
#
|
|
511
|
+
# get call trace track
|
|
512
|
+
#
|
|
513
|
+
def get_call_trace(cut = 1):
|
|
514
|
+
|
|
515
|
+
t = traceback.extract_stack()
|
|
516
|
+
n = len(t) - cut
|
|
517
|
+
str = ''
|
|
518
|
+
sep = 'Trace: '
|
|
519
|
+
for i in range(n):
|
|
520
|
+
tc = t[i]
|
|
521
|
+
str += "{}{}({}){}".format(sep, tc[0], tc[1], ("" if tc[2] == '<module>' else "{%s()}" % tc[2]))
|
|
522
|
+
if i == 0: sep = '=>'
|
|
523
|
+
|
|
524
|
+
return str + "\n" if str else ""
|
|
525
|
+
|
|
526
|
+
#
|
|
527
|
+
# get caller file name
|
|
528
|
+
#
|
|
529
|
+
def get_caller_file(cidx = 0):
|
|
530
|
+
|
|
531
|
+
return traceback.extract_stack()[cidx][0]
|
|
532
|
+
|
|
533
|
+
#
|
|
534
|
+
# log message, msg, for degugging processes according to the debug level
|
|
535
|
+
#
|
|
536
|
+
def pgdbg(level, msg = None, do_trace = True):
|
|
537
|
+
|
|
538
|
+
if not PGLOG['DBGLEVEL']: return # no further action
|
|
539
|
+
|
|
540
|
+
if not isinstance(level, int):
|
|
541
|
+
ms = re.match(r'^(\d+)', level)
|
|
542
|
+
level = int(ms.group(1)) if ms else 0
|
|
543
|
+
|
|
544
|
+
levels = [0, 0]
|
|
545
|
+
if isinstance(PGLOG['DBGLEVEL'], int):
|
|
546
|
+
levels[1] = PGLOG['DBGLEVEL']
|
|
547
|
+
else:
|
|
548
|
+
ms = re.match(r'^(\d+)$', PGLOG['DBGLEVEL'])
|
|
549
|
+
if ms:
|
|
550
|
+
levels[1] = int(ms.group(1))
|
|
551
|
+
else:
|
|
552
|
+
ms = re.match(r'(\d*)-(\d*)', PGLOG['DBGLEVEL'])
|
|
553
|
+
if ms:
|
|
554
|
+
levels[0] = int(ms.group(1)) if ms.group(1) else 0
|
|
555
|
+
levels[1] = int(ms.group(2)) if ms.group(2) else 9999
|
|
556
|
+
|
|
557
|
+
if level > levels[1] or level < levels[0]: return # debug level is out of range
|
|
558
|
+
|
|
559
|
+
if 'DBGPATH' in PGLOG:
|
|
560
|
+
dfile = PGLOG['DBGPATH'] + '/' + PGLOG['DBGFILE']
|
|
561
|
+
else:
|
|
562
|
+
dfile = PGLOG['DBGFILE']
|
|
563
|
+
if not msg:
|
|
564
|
+
pglog("Append debug Info (levels {}-{}) to {}".format(levels[0], levels[1], dfile), WARNLG)
|
|
565
|
+
msg = "DEBUG for " + CPID['PID'] + " "
|
|
566
|
+
if CPID['CPID']: msg += CPID['CPID'] + " <= "
|
|
567
|
+
msg += break_long_string(CPID['CMD'], 40, "...", 1)
|
|
568
|
+
|
|
569
|
+
# logging debug info
|
|
570
|
+
DBG = open(dfile, 'a')
|
|
571
|
+
DBG.write("{}:{}\n".format(level, msg))
|
|
572
|
+
if do_trace: DBG.write(get_call_trace())
|
|
573
|
+
DBG.close()
|
|
574
|
+
|
|
575
|
+
#
|
|
576
|
+
# return trimed string (strip leading and trailling spaces); remove comments led by '#' if rmcmt > 0
|
|
577
|
+
#
|
|
578
|
+
def pgtrim(line, rmcmt = 1):
|
|
579
|
+
|
|
580
|
+
if line:
|
|
581
|
+
if rmcmt:
|
|
582
|
+
if re.match(r'^\s*#', line): # comment line
|
|
583
|
+
line = ''
|
|
584
|
+
elif rmcmt > 1:
|
|
585
|
+
ms = re.search(r'^(.+)\s\s+\#', line)
|
|
586
|
+
if ms: line = ms.group(1) # remove comment and its leading whitespaces
|
|
587
|
+
else:
|
|
588
|
+
ms = re.search(r'^(.+)\s+\#', line)
|
|
589
|
+
if ms: line = ms.group(1) # remove comment and its leading whitespace
|
|
590
|
+
|
|
591
|
+
line = line.strip() # remove leading and trailing whitespaces
|
|
592
|
+
|
|
593
|
+
return line
|
|
594
|
+
|
|
595
|
+
#
|
|
596
|
+
# set PGLOG['PUSGDIR'] from the program file with full path
|
|
597
|
+
#
|
|
598
|
+
def set_help_path(progfile):
|
|
599
|
+
|
|
600
|
+
PGLOG['PUSGDIR'] = op.dirname(op.abspath(progfile))
|
|
601
|
+
|
|
602
|
+
#
|
|
603
|
+
# Function: show_usage(progname: Perl program name to get file "progname.usg")
|
|
604
|
+
#
|
|
605
|
+
# show program usage in file "PGLOG['PUSGDIR']/progname.usg" on screen with unix
|
|
606
|
+
# system function 'pg', exit program when done.
|
|
607
|
+
#
|
|
608
|
+
def show_usage(progname, opts = None):
|
|
609
|
+
|
|
610
|
+
if PGLOG['PUSGDIR'] is None: set_help_path(get_caller_file(1))
|
|
611
|
+
usgname = join_paths(PGLOG['PUSGDIR'], progname + '.usg')
|
|
612
|
+
|
|
613
|
+
if opts:
|
|
614
|
+
# show usage for individual option of dsarch
|
|
615
|
+
for opt in opts:
|
|
616
|
+
if opts[opt][0] == 0:
|
|
617
|
+
msg = "Mode"
|
|
618
|
+
elif opts[opt][0] == 1:
|
|
619
|
+
msg = "Single-Value Information"
|
|
620
|
+
elif opts[opt][0] == 2:
|
|
621
|
+
msg = "Multi-Value Information"
|
|
622
|
+
else:
|
|
623
|
+
msg = "Action"
|
|
624
|
+
|
|
625
|
+
sys.stdout.write("\nDescription of {} Option -{}:\n".format(msg, opt))
|
|
626
|
+
IN = open(usgname, 'r')
|
|
627
|
+
nilcnt = begin = 0
|
|
628
|
+
for line in IN:
|
|
629
|
+
if begin == 0:
|
|
630
|
+
rx = " -{} or -".format(opt)
|
|
631
|
+
if re.match(rx, line): begin = 1
|
|
632
|
+
elif re.match(r'^\s*$', line):
|
|
633
|
+
if nilcnt: break
|
|
634
|
+
nilcnt = 1
|
|
635
|
+
else:
|
|
636
|
+
if re.match(r'\d[\.\s\d]', line): break # section title
|
|
637
|
+
if nilcnt and re.match(r' -\w\w or -', line): break
|
|
638
|
+
nilcnt = 0
|
|
639
|
+
if begin: sys.stdout.write(line)
|
|
640
|
+
IN.close()
|
|
641
|
+
else:
|
|
642
|
+
os.system("more " + usgname)
|
|
643
|
+
|
|
644
|
+
pgexit(0)
|
|
645
|
+
|
|
646
|
+
#
|
|
647
|
+
# compare error message to patterns saved in PGLOG['ERR2STD']
|
|
648
|
+
# return 1 if matched; 0 otherwise
|
|
649
|
+
#
|
|
650
|
+
def err2std(line):
|
|
651
|
+
|
|
652
|
+
for err in PGLOG['ERR2STD']:
|
|
653
|
+
if line.find(err) > -1: return 1
|
|
654
|
+
return 0
|
|
655
|
+
|
|
656
|
+
#
|
|
657
|
+
# compare message to patterns saved in PGLOG['STD2ERR']
|
|
658
|
+
# return 1 if matched; 0 otherwise
|
|
659
|
+
#
|
|
660
|
+
def std2err(line):
|
|
661
|
+
|
|
662
|
+
for out in PGLOG['STD2ERR']:
|
|
663
|
+
if line.find(out) > -1: return 1
|
|
664
|
+
return 0
|
|
665
|
+
|
|
666
|
+
#
|
|
667
|
+
# Function: pgsystem(pgcmd, logact, cmdopt, instr)
|
|
668
|
+
# pgcmd - Linux system command, can be a string, "ls -l", or a list, ['ls', '-l']
|
|
669
|
+
# logact - logging action option, defaults to PgLOG.LOGWRN
|
|
670
|
+
# cmdopt - command control option, default to 5 (1+4)
|
|
671
|
+
# 0 - no command control,
|
|
672
|
+
# 1 - log pgcmd (include the sub command calls),
|
|
673
|
+
# 2 - log standard output,
|
|
674
|
+
# 4 - log error output
|
|
675
|
+
# 7 - log all (pgcmd, and standard/error outputs),
|
|
676
|
+
# 8 - log command with time,
|
|
677
|
+
# 16 - return standard output message upon success
|
|
678
|
+
# 32 - log error as standard output
|
|
679
|
+
# 64 - force returning FAILURE if called process aborts
|
|
680
|
+
# 128 - tries 2 times for failed command before quits
|
|
681
|
+
# 256 - cache standard error message
|
|
682
|
+
# 512 - log instr & seconds with pgcmd if cmdopt&1
|
|
683
|
+
# 1024 - turn on shell
|
|
684
|
+
# instr - input string passing to the command via stdin if not None
|
|
685
|
+
# seconds - number of seconds to wait for a timeout process if > 0
|
|
686
|
+
#
|
|
687
|
+
def pgsystem(pgcmd, logact = LOGWRN, cmdopt = 5, instr = None, seconds = 0):
|
|
688
|
+
|
|
689
|
+
ret = SUCCESS
|
|
690
|
+
if not pgcmd: return ret # empty command
|
|
691
|
+
|
|
692
|
+
act = logact&~EXITLG
|
|
693
|
+
if act&ERRLOG:
|
|
694
|
+
act &= ~ERRLOG
|
|
695
|
+
act |= WARNLG
|
|
696
|
+
|
|
697
|
+
if act&MSGLOG: act |= FRCLOG # make sure system calls always logged
|
|
698
|
+
cmdact = act if cmdopt&1 else 0
|
|
699
|
+
doshell = True if cmdopt&1024 else PGLOG['DOSHELL']
|
|
700
|
+
|
|
701
|
+
if isinstance(pgcmd, str):
|
|
702
|
+
cmdstr = pgcmd
|
|
703
|
+
if not doshell and re.search(r'[*?<>|;]', pgcmd): doshell = True
|
|
704
|
+
execmd = pgcmd if doshell else shlex.split(pgcmd)
|
|
705
|
+
else:
|
|
706
|
+
cmdstr = shlex.join(pgcmd)
|
|
707
|
+
execmd = cmdstr if doshell else pgcmd
|
|
708
|
+
|
|
709
|
+
if cmdact:
|
|
710
|
+
if cmdopt&8:
|
|
711
|
+
cmdlog("starts '{}'".format(cmdstr), None, cmdact)
|
|
712
|
+
else:
|
|
713
|
+
pglog("> " + cmdstr, cmdact)
|
|
714
|
+
if cmdopt&512 and (instr or seconds):
|
|
715
|
+
msg = ''
|
|
716
|
+
if seconds: msg = 'Timeout = {} Seconds'.format(seconds)
|
|
717
|
+
if instr: msg += ' With STDIN:\n' + instr
|
|
718
|
+
if msg: pglog(msg, cmdact)
|
|
719
|
+
stdlog = act if cmdopt&2 else 0
|
|
720
|
+
cmdflg = cmdact|stdlog
|
|
721
|
+
abort = -1 if cmdopt&64 else 0
|
|
722
|
+
loops = 2 if cmdopt&128 else 1
|
|
723
|
+
PGLOG['SYSERR'] = error = retbuf = outbuf = errbuf = ''
|
|
724
|
+
for loop in range(1, loops+1):
|
|
725
|
+
last = time.time()
|
|
726
|
+
try:
|
|
727
|
+
if instr:
|
|
728
|
+
FD = Popen(execmd, shell=doshell, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
|
729
|
+
if seconds:
|
|
730
|
+
outbuf, errbuf = FD.communicate(input=instr.encode(), timeout=seconds)
|
|
731
|
+
else:
|
|
732
|
+
outbuf, errbuf = FD.communicate(input=instr.encode())
|
|
733
|
+
else:
|
|
734
|
+
FD = Popen(execmd, shell=doshell, stdout=PIPE, stderr=PIPE)
|
|
735
|
+
if seconds:
|
|
736
|
+
outbuf, errbuf = FD.communicate(timeout=seconds)
|
|
737
|
+
else:
|
|
738
|
+
outbuf, errbuf = FD.communicate()
|
|
739
|
+
except TimeoutError as e:
|
|
740
|
+
errbuf = str(e)
|
|
741
|
+
FD.kill()
|
|
742
|
+
ret = FAILURE
|
|
743
|
+
except Exception as e:
|
|
744
|
+
errbuf = str(e)
|
|
745
|
+
ret = FAILURE
|
|
746
|
+
else:
|
|
747
|
+
ret = FAILURE if FD.returncode else SUCCESS
|
|
748
|
+
if isinstance(outbuf, bytes): outbuf = str(outbuf, errors='replace')
|
|
749
|
+
if isinstance(errbuf, bytes): errbuf = str(errbuf, errors='replace')
|
|
750
|
+
|
|
751
|
+
if errbuf and cmdopt&32:
|
|
752
|
+
outbuf += errbuf
|
|
753
|
+
if cmdopt&256: PGLOG['SYSERR'] = errbuf
|
|
754
|
+
errbuf = ''
|
|
755
|
+
|
|
756
|
+
if outbuf:
|
|
757
|
+
lines = outbuf.split('\n')
|
|
758
|
+
for line in lines:
|
|
759
|
+
line = strip_output_line(line.strip())
|
|
760
|
+
if not line: continue
|
|
761
|
+
if PGLOG['STD2ERR'] and std2err(line):
|
|
762
|
+
if cmdopt&260: error += line + "\n"
|
|
763
|
+
if abort == -1 and re.match('ABORTS ', line): abort = 1
|
|
764
|
+
else:
|
|
765
|
+
if re.match(r'^>+ ', line):
|
|
766
|
+
line = '>' + line
|
|
767
|
+
if cmdflg: pglog(line, cmdflg)
|
|
768
|
+
elif stdlog:
|
|
769
|
+
pglog(line, stdlog)
|
|
770
|
+
if cmdopt&16: retbuf += line + "\n"
|
|
771
|
+
|
|
772
|
+
if errbuf:
|
|
773
|
+
lines = errbuf.split('\n')
|
|
774
|
+
for line in lines:
|
|
775
|
+
line = strip_output_line(line.strip())
|
|
776
|
+
if not line: continue
|
|
777
|
+
if PGLOG['ERR2STD'] and err2std(line):
|
|
778
|
+
if stdlog: pglog(line, stdlog)
|
|
779
|
+
if cmdopt&16: retbuf += line + "\n"
|
|
780
|
+
else:
|
|
781
|
+
if cmdopt&260: error += line + "\n"
|
|
782
|
+
if abort == -1 and re.match('ABORTS ', line): abort = 1
|
|
783
|
+
|
|
784
|
+
if ret == SUCCESS and abort == 1: ret = FAILURE
|
|
785
|
+
end = time.time()
|
|
786
|
+
last = end - last
|
|
787
|
+
|
|
788
|
+
if error:
|
|
789
|
+
if ret == FAILURE:
|
|
790
|
+
error = "Error Execute: {}\n{}".format(cmdstr, error)
|
|
791
|
+
else:
|
|
792
|
+
error = "Error From: {}\n{}".format(cmdstr, error)
|
|
793
|
+
|
|
794
|
+
if loop > 1: error = "Retry "
|
|
795
|
+
if cmdopt&256: PGLOG['SYSERR'] += error
|
|
796
|
+
if cmdopt&4:
|
|
797
|
+
errlog = (act|ERRLOG)
|
|
798
|
+
if ret == FAILURE and loop >= loops: errlog |= logact
|
|
799
|
+
pglog(error, errlog)
|
|
800
|
+
|
|
801
|
+
if last > PGLOG['CMDTIME'] and not re.search(r'(^|/|\s)(dsarch|dsupdt|dsrqst)\s', cmdstr):
|
|
802
|
+
cmdstr = "> {} Ends By {}".format(break_long_string(cmdstr, 100, "...", 1), current_datetime())
|
|
803
|
+
cmd_execute_time(cmdstr, last, cmdact)
|
|
804
|
+
|
|
805
|
+
if ret == SUCCESS or loop >= loops: break
|
|
806
|
+
time.sleep(6)
|
|
807
|
+
|
|
808
|
+
if ret == FAILURE and retbuf and cmdopt&272 == 272:
|
|
809
|
+
if PGLOG['SYSERR']: PGLOG['SYSERR'] += '\n'
|
|
810
|
+
PGLOG['SYSERR'] += retbuf
|
|
811
|
+
retbuf = ''
|
|
812
|
+
|
|
813
|
+
return (retbuf if cmdopt&16 else ret)
|
|
814
|
+
|
|
815
|
+
#
|
|
816
|
+
# strip carrage return '\r', but keep ending newline '\n'
|
|
817
|
+
#
|
|
818
|
+
def strip_output_line(line):
|
|
819
|
+
|
|
820
|
+
ms = re.search(r'\r([^\r]+)\r*$', line)
|
|
821
|
+
if ms: return ms.group(1)
|
|
822
|
+
|
|
823
|
+
ms = re.search(r'\s\.+\s+(\d+)%\s+', line)
|
|
824
|
+
if ms and int(ms.group(1)) != 100: return None
|
|
825
|
+
|
|
826
|
+
return line
|
|
827
|
+
|
|
828
|
+
#
|
|
829
|
+
# show command running time string formated by seconds_to_string_time()
|
|
830
|
+
#
|
|
831
|
+
def cmd_execute_time(cmdstr, last, logact = None):
|
|
832
|
+
|
|
833
|
+
msg = cmdstr
|
|
834
|
+
|
|
835
|
+
if last >= PGLOG['CMDTIME']: # show running for at least one minute
|
|
836
|
+
msg += " ({})".format(seconds_to_string_time(last))
|
|
837
|
+
|
|
838
|
+
if logact:
|
|
839
|
+
return pglog(msg, logact)
|
|
840
|
+
else:
|
|
841
|
+
return msg
|
|
842
|
+
|
|
843
|
+
#
|
|
844
|
+
# convert given seconds to string time with units of S-Second,M-Minute,H-Hour,D-Day
|
|
845
|
+
#
|
|
846
|
+
def seconds_to_string_time(seconds, showzero = 0):
|
|
847
|
+
|
|
848
|
+
msg = ''
|
|
849
|
+
s = m = h = 0
|
|
850
|
+
|
|
851
|
+
if seconds > 0:
|
|
852
|
+
s = seconds%60 # seconds (0-59)
|
|
853
|
+
minutes = int(seconds/60) # total minutes
|
|
854
|
+
m = minutes%60 # minutes (0-59)
|
|
855
|
+
if minutes >= 60:
|
|
856
|
+
hours = int(minutes/60) # total hours
|
|
857
|
+
h = hours%24 # hours (0-23)
|
|
858
|
+
if hours >= 24:
|
|
859
|
+
msg += "{}D".format(int(hours/24)) # days
|
|
860
|
+
if h: msg += "{}H".format(h)
|
|
861
|
+
if m: msg += "{}M".format(m)
|
|
862
|
+
if s:
|
|
863
|
+
msg += "%dS"%(s) if isinstance(s, int) else "{:.3f}S".format(s)
|
|
864
|
+
elif showzero:
|
|
865
|
+
msg = "0S"
|
|
866
|
+
|
|
867
|
+
return msg
|
|
868
|
+
|
|
869
|
+
#
|
|
870
|
+
# wrap function to call pgsystem() with a timeout control
|
|
871
|
+
# return FAILURE if error eval or time out
|
|
872
|
+
#
|
|
873
|
+
def tosystem(cmd, timeout = 0, logact = LOGWRN, cmdopt = 5, instr = None):
|
|
874
|
+
|
|
875
|
+
if not timeout: timeout = PGLOG['TIMEOUT'] # set default timeout if missed
|
|
876
|
+
return pgsystem(cmd, logact, cmdopt, instr, timeout)
|
|
877
|
+
|
|
878
|
+
#
|
|
879
|
+
# insert breaks, default to '\n', for every length, default to 1024,
|
|
880
|
+
# for long string; return specified number lines if mline given
|
|
881
|
+
#
|
|
882
|
+
def break_long_string(lstr, limit = 1024, bsign = "\n", mline = 200, bchars = ' &;', minlmt = 20, eline = 0):
|
|
883
|
+
|
|
884
|
+
length = len(lstr) if lstr else 0
|
|
885
|
+
if length <= limit: return lstr
|
|
886
|
+
|
|
887
|
+
if bsign is None: bsign = "\n"
|
|
888
|
+
if bchars is None: bchars = ' &;'
|
|
889
|
+
addbreak = offset = 0
|
|
890
|
+
retstr = ""
|
|
891
|
+
elines = []
|
|
892
|
+
if eline > mline: eline = mline
|
|
893
|
+
mcnt = mline - eline
|
|
894
|
+
ecnt = 0
|
|
895
|
+
|
|
896
|
+
while offset < length:
|
|
897
|
+
bpos = lstr[offset:].find(bsign)
|
|
898
|
+
blen = bpos if bpos > -1 else (length - offset)
|
|
899
|
+
if blen == 0:
|
|
900
|
+
offset += 1
|
|
901
|
+
substr = "" if addbreak else bsign
|
|
902
|
+
addbreak = 0
|
|
903
|
+
elif blen <= limit:
|
|
904
|
+
blen += 1
|
|
905
|
+
substr = lstr[offset:(offset+blen)]
|
|
906
|
+
offset += blen
|
|
907
|
+
addbreak = 0
|
|
908
|
+
else:
|
|
909
|
+
substr = lstr[offset:(offset+limit)]
|
|
910
|
+
bpos = limit - 1
|
|
911
|
+
while bpos > minlmt:
|
|
912
|
+
char = substr[bpos]
|
|
913
|
+
if bchars.find(char) >= 0: break
|
|
914
|
+
bpos -= 1
|
|
915
|
+
if bpos > minlmt:
|
|
916
|
+
bpos += 1
|
|
917
|
+
substr = substr[:bpos]
|
|
918
|
+
offset += bpos
|
|
919
|
+
else:
|
|
920
|
+
offset += limit
|
|
921
|
+
addbreak = 1
|
|
922
|
+
substr += bsign
|
|
923
|
+
|
|
924
|
+
if mcnt:
|
|
925
|
+
retstr += substr
|
|
926
|
+
mcnt -= 1
|
|
927
|
+
if mcnt == 0 and eline == 0: break
|
|
928
|
+
elif eline > 0:
|
|
929
|
+
elines.append(substr)
|
|
930
|
+
ecnt += 1
|
|
931
|
+
else:
|
|
932
|
+
break
|
|
933
|
+
|
|
934
|
+
if ecnt > 0:
|
|
935
|
+
if ecnt > eline:
|
|
936
|
+
retstr += "..." + bsign
|
|
937
|
+
mcnt = ecnt - eline
|
|
938
|
+
else:
|
|
939
|
+
mcnt = 0
|
|
940
|
+
|
|
941
|
+
while mcnt < ecnt:
|
|
942
|
+
retstr += elines[mcnt]
|
|
943
|
+
mcnt += 1
|
|
944
|
+
|
|
945
|
+
return retstr
|
|
946
|
+
|
|
947
|
+
#
|
|
948
|
+
# join two paths by remove overlapping directories
|
|
949
|
+
# diff = 0: join given pathes
|
|
950
|
+
# 1: remove path1 from path2
|
|
951
|
+
#
|
|
952
|
+
def join_paths(path1, path2, diff = 0):
|
|
953
|
+
|
|
954
|
+
if not path2: return path1
|
|
955
|
+
if not path1 or not diff and re.match('/', path2): return path2
|
|
956
|
+
|
|
957
|
+
if diff:
|
|
958
|
+
ms = re.match(r'{}/(.*)'.format(path1), path2)
|
|
959
|
+
if ms: return ms.group(1)
|
|
960
|
+
|
|
961
|
+
adir1 = path1.split('/')
|
|
962
|
+
adir2 = path2.split('/')
|
|
963
|
+
while adir2 and not adir2[0]: adir2.pop(0)
|
|
964
|
+
while adir1 and adir2 and adir2[0] == "..":
|
|
965
|
+
adir2.pop(0)
|
|
966
|
+
adir1.pop()
|
|
967
|
+
while adir2 and adir2[0] == ".": adir2.pop(0)
|
|
968
|
+
|
|
969
|
+
if adir1 and adir2:
|
|
970
|
+
len1 = len(adir1)
|
|
971
|
+
len2 = len(adir2)
|
|
972
|
+
idx1 = len1-1
|
|
973
|
+
idx2 = mcnt = 0
|
|
974
|
+
while idx2 < len1 and idx2 < len2:
|
|
975
|
+
if adir1[idx1] == adir2[idx2]:
|
|
976
|
+
mcnt = 1
|
|
977
|
+
break
|
|
978
|
+
idx2 += 1
|
|
979
|
+
|
|
980
|
+
if mcnt > 0:
|
|
981
|
+
while mcnt <= idx2:
|
|
982
|
+
if adir1[idx1-mcnt] != adir2[idx2-mcnt]: break
|
|
983
|
+
mcnt += 1
|
|
984
|
+
|
|
985
|
+
if mcnt > idx2: # remove mcnt matching directories
|
|
986
|
+
while mcnt > 0:
|
|
987
|
+
adir2.pop(0)
|
|
988
|
+
mcnt -= 1
|
|
989
|
+
|
|
990
|
+
if diff:
|
|
991
|
+
return '/'.join(adir2)
|
|
992
|
+
else:
|
|
993
|
+
return '/'.join(adir1 + adir2)
|
|
994
|
+
|
|
995
|
+
#
|
|
996
|
+
# validate if a command for a given BATCH host is accessable and executable
|
|
997
|
+
#
|
|
998
|
+
# Return SUCCESS if valid; FAILURE if not
|
|
999
|
+
#
|
|
1000
|
+
def valid_batch_host(host, logact = 0):
|
|
1001
|
+
|
|
1002
|
+
HOST = host.upper()
|
|
1003
|
+
return SUCCESS if HOST in BCHCMDS and valid_command(BCHCMDS[HOST], logact) else FAILURE
|
|
1004
|
+
|
|
1005
|
+
#
|
|
1006
|
+
# validate if a given command is accessable and executable
|
|
1007
|
+
#
|
|
1008
|
+
# Return the full command path if valid; '' if not
|
|
1009
|
+
#
|
|
1010
|
+
def valid_command(cmd, logact = 0):
|
|
1011
|
+
|
|
1012
|
+
ms = re.match(r'^(\S+)( .*)$', cmd)
|
|
1013
|
+
if ms:
|
|
1014
|
+
option = ms.group(2)
|
|
1015
|
+
cmd = ms.group(1)
|
|
1016
|
+
else:
|
|
1017
|
+
option = ''
|
|
1018
|
+
if cmd not in COMMANDS:
|
|
1019
|
+
buf = shutil.which(cmd)
|
|
1020
|
+
if buf is None:
|
|
1021
|
+
if logact: pglog(cmd + ": executable command not found", logact)
|
|
1022
|
+
buf = ''
|
|
1023
|
+
elif option:
|
|
1024
|
+
buf += option
|
|
1025
|
+
COMMANDS[cmd] = buf
|
|
1026
|
+
|
|
1027
|
+
return COMMANDS[cmd]
|
|
1028
|
+
|
|
1029
|
+
#
|
|
1030
|
+
# add carbon copies to PGLOG['CCDADDR']
|
|
1031
|
+
#
|
|
1032
|
+
def add_carbon_copy(cc = None, isstr = None, exclude = 0, specialist = None):
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
if not cc:
|
|
1036
|
+
if cc is None and isstr is None: PGLOG['CCDADDR'] = ''
|
|
1037
|
+
else:
|
|
1038
|
+
emails = re.split(r'[,\s]+', cc) if isstr else cc
|
|
1039
|
+
for email in emails:
|
|
1040
|
+
if not email or email.find('/') >= 0 or email == 'N': continue
|
|
1041
|
+
if email == "S":
|
|
1042
|
+
if not specialist: continue
|
|
1043
|
+
email = specialist
|
|
1044
|
+
|
|
1045
|
+
if email.find('@') == -1: email += "@ucar.edu"
|
|
1046
|
+
if exclude and exclude.find(email) > -1: continue
|
|
1047
|
+
if PGLOG['CCDADDR']:
|
|
1048
|
+
if PGLOG['CCDADDR'].find(email) > -1: continue # email Cc'd already
|
|
1049
|
+
PGLOG['CCDADDR'] += ", "
|
|
1050
|
+
PGLOG['CCDADDR'] += email
|
|
1051
|
+
|
|
1052
|
+
#
|
|
1053
|
+
# get the current host name; or batch sever name if getbatch is 1
|
|
1054
|
+
#
|
|
1055
|
+
def get_host(getbatch = 0):
|
|
1056
|
+
|
|
1057
|
+
if getbatch and PGLOG['CURBID'] != 0:
|
|
1058
|
+
host = PGLOG['PGBATCH']
|
|
1059
|
+
elif PGLOG['HOSTNAME']:
|
|
1060
|
+
return PGLOG['HOSTNAME']
|
|
1061
|
+
else:
|
|
1062
|
+
host = socket.gethostname()
|
|
1063
|
+
|
|
1064
|
+
return get_short_host(host)
|
|
1065
|
+
|
|
1066
|
+
#
|
|
1067
|
+
# strip domain names and retrun the server name itself
|
|
1068
|
+
#
|
|
1069
|
+
def get_short_host(host):
|
|
1070
|
+
|
|
1071
|
+
if not host: return ''
|
|
1072
|
+
ms = re.match(r'^([^\.]+)\.', host)
|
|
1073
|
+
if ms: host = ms.group(1)
|
|
1074
|
+
if PGLOG['HOSTNAME'] and (host == 'localhost' or host == PGLOG['HOSTNAME']): return PGLOG['HOSTNAME']
|
|
1075
|
+
HOST = host.upper()
|
|
1076
|
+
if HOST in BCHCMDS: return HOST
|
|
1077
|
+
|
|
1078
|
+
return host
|
|
1079
|
+
|
|
1080
|
+
#
|
|
1081
|
+
# get a live SLURM host name
|
|
1082
|
+
#
|
|
1083
|
+
def get_slurm_host():
|
|
1084
|
+
|
|
1085
|
+
global SLMHOSTS
|
|
1086
|
+
|
|
1087
|
+
if not SLMSTATS and PGLOG['SLMHOSTS']:
|
|
1088
|
+
SLMHOSTS = PGLOG['SLMHOSTS'].split(':')
|
|
1089
|
+
for host in SLMHOSTS:
|
|
1090
|
+
SLMSTATS[host] = 1
|
|
1091
|
+
|
|
1092
|
+
for host in SLMHOSTS:
|
|
1093
|
+
if host in SLMSTATS and SLMSTATS[host]: return host
|
|
1094
|
+
|
|
1095
|
+
return None
|
|
1096
|
+
|
|
1097
|
+
#
|
|
1098
|
+
# get a live PBS host name
|
|
1099
|
+
#
|
|
1100
|
+
def get_pbs_host():
|
|
1101
|
+
|
|
1102
|
+
global PBSHOSTS
|
|
1103
|
+
|
|
1104
|
+
if not PBSSTATS and PGLOG['PBSHOSTS']:
|
|
1105
|
+
PBSHOSTS = PGLOG['PBSHOSTS'].split(':')
|
|
1106
|
+
for host in PBSHOSTS:
|
|
1107
|
+
PBSSTATS[host] = 1
|
|
1108
|
+
|
|
1109
|
+
for host in PBSHOSTS:
|
|
1110
|
+
if host in PBSSTATS and PBSSTATS[host]: return host
|
|
1111
|
+
|
|
1112
|
+
return None
|
|
1113
|
+
|
|
1114
|
+
#
|
|
1115
|
+
# set host status, 0 dead & 1 live, for one or all avalaible slurm hosts
|
|
1116
|
+
#
|
|
1117
|
+
def set_slurm_host(host = None, stat = 0):
|
|
1118
|
+
|
|
1119
|
+
global SLMHOSTS
|
|
1120
|
+
|
|
1121
|
+
if host:
|
|
1122
|
+
SLMSTATS[host] = stat
|
|
1123
|
+
else:
|
|
1124
|
+
if not SLMHOSTS and PGLOG['SLMHOSTS']:
|
|
1125
|
+
SLMHOSTS = PGLOG['SLMHOSTS'].split(':')
|
|
1126
|
+
for host in SLMHOSTS:
|
|
1127
|
+
SLMSTATS[host] = stat
|
|
1128
|
+
|
|
1129
|
+
#
|
|
1130
|
+
# set host status, 0 dead & 1 live, for one or all avalaible pbs hosts
|
|
1131
|
+
#
|
|
1132
|
+
def set_pbs_host(host = None, stat = 0):
|
|
1133
|
+
|
|
1134
|
+
global PBSHOSTS
|
|
1135
|
+
|
|
1136
|
+
if host:
|
|
1137
|
+
PBSSTATS[host] = stat
|
|
1138
|
+
else:
|
|
1139
|
+
if not PBSHOSTS and PGLOG['PBSHOSTS']:
|
|
1140
|
+
PBSHOSTS = PGLOG['PBSHOSTS'].split(':')
|
|
1141
|
+
for host in PBSHOSTS:
|
|
1142
|
+
PBSSTATS[host] = stat
|
|
1143
|
+
|
|
1144
|
+
#
|
|
1145
|
+
# reset the batch host name in case was not set properly
|
|
1146
|
+
#
|
|
1147
|
+
def reset_batch_host(bhost, logact = LOGWRN):
|
|
1148
|
+
|
|
1149
|
+
BCHHOST = bhost.upper()
|
|
1150
|
+
|
|
1151
|
+
if BCHHOST != PGLOG['PGBATCH']:
|
|
1152
|
+
if PGLOG['CURBID'] > 0:
|
|
1153
|
+
pglog("{}-{}: Batch ID is set, cannot change Batch host to {}".format(PGLOG['PGBATCH'], PGLOG['CURBID'], BCHHOST) , logact)
|
|
1154
|
+
else:
|
|
1155
|
+
ms = re.search(r'(^|:){}(:|$)'.format(BCHHOST), PGLOG['BCHHOSTS'])
|
|
1156
|
+
if ms:
|
|
1157
|
+
PGLOG['PGBATCH'] = BCHHOST
|
|
1158
|
+
if PGLOG['CURBID'] == 0: PGLOG['CURBID'] = -1
|
|
1159
|
+
elif PGLOG['PGBATCH']:
|
|
1160
|
+
PGLOG['PGBATCH'] = ''
|
|
1161
|
+
PGLOG['CURBID'] = 0
|
|
1162
|
+
|
|
1163
|
+
#
|
|
1164
|
+
# return the base command name of the current process
|
|
1165
|
+
#
|
|
1166
|
+
def get_command(cmdstr = None):
|
|
1167
|
+
|
|
1168
|
+
if not cmdstr: cmdstr = sys.argv[0]
|
|
1169
|
+
cmdstr = op.basename(cmdstr)
|
|
1170
|
+
ms = re.match(r'^(.+)\.(py|pl)$', cmdstr)
|
|
1171
|
+
if ms:
|
|
1172
|
+
return ms.group(1)
|
|
1173
|
+
else:
|
|
1174
|
+
return cmdstr
|
|
1175
|
+
|
|
1176
|
+
#
|
|
1177
|
+
# wrap a given command cmd for either sudo or setuid wrapper pgstart_['username']
|
|
1178
|
+
# to run as user asuser
|
|
1179
|
+
#
|
|
1180
|
+
def get_local_command(cmd, asuser = None):
|
|
1181
|
+
|
|
1182
|
+
cuser = PGLOG['SETUID'] if PGLOG['SETUID'] else PGLOG['CURUID']
|
|
1183
|
+
if not asuser or cuser == asuser: return cmd
|
|
1184
|
+
|
|
1185
|
+
if cuser == PGLOG['GDEXUSER']:
|
|
1186
|
+
wrapper = "pgstart_" + asuser
|
|
1187
|
+
if valid_command(wrapper): return "{} {}".format(wrapper, cmd)
|
|
1188
|
+
elif PGLOG['SUDOGDEX'] and asuser == PGLOG['GDEXUSER']:
|
|
1189
|
+
return "sudo -u {} {}".format(PGLOG['GDEXUSER'], cmd) # sudo as user gdexdata
|
|
1190
|
+
|
|
1191
|
+
return cmd
|
|
1192
|
+
|
|
1193
|
+
#
|
|
1194
|
+
# wrap a given command cmd for either sudo or setuid wrapper pgstart_['username']
|
|
1195
|
+
# to run as user asuser on a given remote host
|
|
1196
|
+
#
|
|
1197
|
+
def get_remote_command(cmd, host, asuser = None):
|
|
1198
|
+
|
|
1199
|
+
# if host and not re.match(PGLOG['HOSTNAME'], host): cmd = "ssh {} {}".format(host, cmd)
|
|
1200
|
+
return get_local_command(cmd, asuser)
|
|
1201
|
+
|
|
1202
|
+
#
|
|
1203
|
+
# wrap a given hpss command cmd with sudo either before his of after hsi
|
|
1204
|
+
# to run as user asuser
|
|
1205
|
+
#
|
|
1206
|
+
def get_hpss_command(cmd, asuser = None, hcmd = None):
|
|
1207
|
+
|
|
1208
|
+
cuser = PGLOG['SETUID'] if PGLOG['SETUID'] else PGLOG['CURUID']
|
|
1209
|
+
if not hcmd: hcmd = 'hsi'
|
|
1210
|
+
|
|
1211
|
+
if asuser and cuser != asuser:
|
|
1212
|
+
if cuser == PGLOG['GDEXUSER']:
|
|
1213
|
+
return "{} sudo -u {} {}".format(hcmd, asuser, cmd) # setuid wrapper as user asuser
|
|
1214
|
+
elif PGLOG['SUDOGDEX'] and asuser == PGLOG['GDEXUSER']:
|
|
1215
|
+
return "sudo -u {} {} {}".format(PGLOG['GDEXUSER'], hcmd, cmd) # sudo as user gdexdata
|
|
1216
|
+
|
|
1217
|
+
if cuser != PGLOG['GDEXUSER']:
|
|
1218
|
+
if re.match(r'^ls ', cmd) and hcmd == 'hsi':
|
|
1219
|
+
return "hpss" + cmd # use 'hpssls' instead of 'hsi ls'
|
|
1220
|
+
elif re.match(r'^htar -tvf', hcmd):
|
|
1221
|
+
hcmd.replace('htar -tvf', 'htarmember', 1) # use 'htarmember' instead of 'htar -tvf'
|
|
1222
|
+
elif re.match(r'^hsi ls', hcmd):
|
|
1223
|
+
hcmd.replce('hsi ls', 'hpssls', 1) # use 'hpssls' instead of 'hsi ls'
|
|
1224
|
+
|
|
1225
|
+
return "{} {}".format(hcmd, cmd)
|
|
1226
|
+
|
|
1227
|
+
#
|
|
1228
|
+
# wrap a given sync command for given host name with/without sudo
|
|
1229
|
+
#
|
|
1230
|
+
def get_sync_command(host, asuser = None):
|
|
1231
|
+
|
|
1232
|
+
host = get_short_host(host)
|
|
1233
|
+
|
|
1234
|
+
if (not (PGLOG['SETUID'] and PGLOG['SETUID'] == PGLOG['GDEXUSER']) and
|
|
1235
|
+
(not asuser or asuser == PGLOG['GDEXUSER'])):
|
|
1236
|
+
return "sync" + host
|
|
1237
|
+
|
|
1238
|
+
return host + "-sync"
|
|
1239
|
+
|
|
1240
|
+
#
|
|
1241
|
+
# set PGLOG['SETUID'] as needed
|
|
1242
|
+
#
|
|
1243
|
+
def set_suid(cuid = 0):
|
|
1244
|
+
|
|
1245
|
+
if not cuid: cuid = PGLOG['EUID']
|
|
1246
|
+
if cuid != PGLOG['EUID'] or cuid != PGLOG['RUID']:
|
|
1247
|
+
os.setreuid(cuid, cuid)
|
|
1248
|
+
PGLOG['SETUID'] = pwd.getpwuid(cuid).pw_name
|
|
1249
|
+
if not (PGLOG['SETUID'] == PGLOG['GDEXUSER'] or cuid == PGLOG['RUID']):
|
|
1250
|
+
set_specialist_environments(PGLOG['SETUID'])
|
|
1251
|
+
PGLOG['CURUID'] == PGLOG['SETUID'] # set CURUID to a specific specialist
|
|
1252
|
+
|
|
1253
|
+
#
|
|
1254
|
+
# set comman pglog
|
|
1255
|
+
#
|
|
1256
|
+
def set_common_pglog():
|
|
1257
|
+
|
|
1258
|
+
PGLOG['CURDIR'] = os.getcwd()
|
|
1259
|
+
|
|
1260
|
+
# set current user id
|
|
1261
|
+
PGLOG['RUID'] = os.getuid()
|
|
1262
|
+
PGLOG['EUID'] = os.geteuid()
|
|
1263
|
+
PGLOG['CURUID'] = pwd.getpwuid(PGLOG['RUID']).pw_name
|
|
1264
|
+
try:
|
|
1265
|
+
PGLOG['RDAUID'] = PGLOG['GDEXUID'] = pwd.getpwnam(PGLOG['GDEXUSER']).pw_uid
|
|
1266
|
+
PGLOG['RDAGID'] = PGLOG['GDEXGID'] = grp.getgrnam(PGLOG['GDEXGRP']).gr_gid
|
|
1267
|
+
except:
|
|
1268
|
+
PGLOG['RDAUID'] = PGLOG['GDEXUID'] = 0
|
|
1269
|
+
PGLOG['RDAGID'] = PGLOG['GDEXGID'] = 0
|
|
1270
|
+
if PGLOG['CURUID'] == PGLOG['GDEXUSER']: PGLOG['SETUID'] = PGLOG['GDEXUSER']
|
|
1271
|
+
|
|
1272
|
+
PGLOG['HOSTNAME'] = get_host()
|
|
1273
|
+
for htype in HOSTTYPES:
|
|
1274
|
+
ms = re.match(r'^{}(-|\d|$)'.format(htype), PGLOG['HOSTNAME'])
|
|
1275
|
+
if ms:
|
|
1276
|
+
PGLOG['HOSTTYPE'] = HOSTTYPES[htype]
|
|
1277
|
+
break
|
|
1278
|
+
PGLOG['DEFDSID'] = 'd000000' if PGLOG['NEWDSID'] else 'ds000.0'
|
|
1279
|
+
PGLOG['NOTAROOT'] = '|'.join([PGLOG['OLDAROOT'], PGLOG['OLDBROOT'], PGLOG['BACKROOT']])
|
|
1280
|
+
PGLOG['NOTBROOT'] = '|'.join([PGLOG['OLDAROOT'], PGLOG['OLDBROOT'], PGLOG['ARCHROOT']])
|
|
1281
|
+
PGLOG['ALLROOTS'] = '|'.join([PGLOG['OLDAROOT'], PGLOG['OLDBROOT'], PGLOG['ARCHROOT'], PGLOG['BACKROOT']])
|
|
1282
|
+
SETPGLOG("USRHOME", "/glade/u/home")
|
|
1283
|
+
SETPGLOG("DSSHOME", "/glade/u/home/gdexdata")
|
|
1284
|
+
SETPGLOG("GDEXHOME", "/data/local")
|
|
1285
|
+
SETPGLOG("ADDPATH", "")
|
|
1286
|
+
SETPGLOG("ADDLIB", "")
|
|
1287
|
+
SETPGLOG("OTHPATH", "")
|
|
1288
|
+
SETPGLOG("PSQLHOME", "/usr/pgsql-15")
|
|
1289
|
+
SETPGLOG("DSGHOSTS", "")
|
|
1290
|
+
SETPGLOG("DSIDCHRS", "d")
|
|
1291
|
+
|
|
1292
|
+
if not os.getenv('HOME'): os.environ['HOME'] = "{}/{}".format(PGLOG['USRHOME'], PGLOG['CURUID'])
|
|
1293
|
+
SETPGLOG("HOMEBIN", os.environ.get('HOME') + "/bin")
|
|
1294
|
+
|
|
1295
|
+
if 'SLURM_JOBID' in os.environ:
|
|
1296
|
+
PGLOG['CURBID'] = int(os.getenv('SLURM_JOBID'))
|
|
1297
|
+
PGLOG['PGBATCH'] = PGLOG['SLMNAME']
|
|
1298
|
+
elif 'PBS_JOBID' in os.environ:
|
|
1299
|
+
sbid = os.getenv('PBS_JOBID')
|
|
1300
|
+
ms = re.match(r'^(\d+)', sbid)
|
|
1301
|
+
PGLOG['CURBID'] = int(ms.group(1)) if ms else -1
|
|
1302
|
+
PGLOG['PGBATCH'] = PGLOG['PBSNAME']
|
|
1303
|
+
else:
|
|
1304
|
+
PGLOG['CURBID'] = 0
|
|
1305
|
+
PGLOG['PGBATCH'] = ''
|
|
1306
|
+
|
|
1307
|
+
pgpath = PGLOG['HOMEBIN']
|
|
1308
|
+
PGLOG['LOCHOME'] = "/ncar/rda/setuid"
|
|
1309
|
+
if not op.isdir(PGLOG['LOCHOME']): PGLOG['LOCHOME'] = "/usr/local/decs"
|
|
1310
|
+
pgpath += ":{}/bin".format(PGLOG['LOCHOME'])
|
|
1311
|
+
locpath = "{}/bin/{}".format(PGLOG['DSSHOME'], PGLOG['HOSTTYPE'])
|
|
1312
|
+
if op.isdir(locpath): pgpath += ":" + locpath
|
|
1313
|
+
pgpath = add_local_path("{}/bin".format(PGLOG['DSSHOME']), pgpath, 1)
|
|
1314
|
+
if PGLOG['PSQLHOME']:
|
|
1315
|
+
locpath = PGLOG['PSQLHOME'] + "/bin"
|
|
1316
|
+
if op.isdir(locpath): pgpath += ":" + locpath
|
|
1317
|
+
pgpath = add_local_path(os.getenv('PATH'), pgpath, 1)
|
|
1318
|
+
if PGLOG['HOSTTYPE'] == 'dav': pgpath = add_local_path('/glade/u/apps/opt/qstat-cache/bin:/opt/pbs/bin', pgpath, 1)
|
|
1319
|
+
if 'OTHPATH' in PGLOG and PGLOG['OTHPATH']:
|
|
1320
|
+
pgpath = add_local_path(PGLOG['OTHPATH'], pgpath, 1)
|
|
1321
|
+
if PGLOG['ADDPATH']: pgpath = add_local_path(PGLOG['ADDPATH'], pgpath, 1)
|
|
1322
|
+
pgpath = add_local_path("/bin:/usr/bin:/usr/local/bin:/usr/sbin", pgpath, 1)
|
|
1323
|
+
|
|
1324
|
+
os.environ['PATH'] = pgpath
|
|
1325
|
+
os.environ['SHELL'] = '/bin/sh'
|
|
1326
|
+
# set PGLOG values with environments and defaults
|
|
1327
|
+
SETPGLOG("DSSDBHM", PGLOG['DSSHOME']+"/dssdb") # dssdb home dir
|
|
1328
|
+
SETPGLOG("LOGPATH", PGLOG['DSSDBHM']+"/log") # path to log file
|
|
1329
|
+
SETPGLOG("LOGFILE", "pgdss.log") # log file name
|
|
1330
|
+
SETPGLOG("EMLFILE", "pgemail.log") # email log file name
|
|
1331
|
+
SETPGLOG("ERRFILE", '') # error file name
|
|
1332
|
+
sm = "/usr/sbin/sendmail"
|
|
1333
|
+
if valid_command(sm): SETPGLOG("EMLSEND", f"{sm} -t") # send email command
|
|
1334
|
+
SETPGLOG("DBGLEVEL", '') # debug level
|
|
1335
|
+
SETPGLOG("BAOTOKEN", 's.lh2t2kDjrqs3V8y2BU2zOocT') # OpenBao token
|
|
1336
|
+
SETPGLOG("DBGPATH", PGLOG['DSSDBHM']+"/log") # path to debug log file
|
|
1337
|
+
SETPGLOG("OBJCTBKT", "gdex-data") # default Bucket on Object Store
|
|
1338
|
+
SETPGLOG("BACKUPEP", "gdex-quasar") # default Globus Endpoint on Quasar
|
|
1339
|
+
SETPGLOG("DRDATAEP", "gdex-quasar-drdata") # DRDATA Globus Endpoint on Quasar
|
|
1340
|
+
SETPGLOG("DBGFILE", "pgdss.dbg") # debug file name
|
|
1341
|
+
SETPGLOG("CNFPATH", PGLOG['DSSHOME']+"/config") # path to configuration files
|
|
1342
|
+
SETPGLOG("DSSURL", "https://gdex.ucar.edu") # current dss web URL
|
|
1343
|
+
SETPGLOG("RQSTURL", "/datasets/request") # request URL path
|
|
1344
|
+
SETPGLOG("WEBSERVERS", "") # webserver names for Web server
|
|
1345
|
+
PGLOG['WEBHOSTS'] = PGLOG['WEBSERVERS'].split(':') if PGLOG['WEBSERVERS'] else []
|
|
1346
|
+
SETPGLOG("DBMODULE", '')
|
|
1347
|
+
SETPGLOG("LOCDATA", "/data")
|
|
1348
|
+
|
|
1349
|
+
# set dss web homedir
|
|
1350
|
+
SETPGLOG("DSSWEB", PGLOG['LOCDATA']+"/web")
|
|
1351
|
+
SETPGLOG("DSWHOME", PGLOG['DSSWEB']+"/datasets") # datast web root path
|
|
1352
|
+
PGLOG['HOMEROOTS'] = "{}|{}".format(PGLOG['DSSHOME'], PGLOG['DSWHOME'])
|
|
1353
|
+
SETPGLOG("DSSDATA", "/glade/campaign/collections/gdex") # dss data root path
|
|
1354
|
+
SETPGLOG("DSDHOME", PGLOG['DSSDATA']+"/data") # dataset data root path
|
|
1355
|
+
SETPGLOG("DECSHOME", PGLOG['DSSDATA']+"/decsdata") # dataset decsdata root path
|
|
1356
|
+
SETPGLOG("DSHHOME", PGLOG['DECSHOME']+"/helpfiles") # dataset help root path
|
|
1357
|
+
SETPGLOG("GDEXWORK", "/lustre/desc1/gdex/work") # gdex work path
|
|
1358
|
+
SETPGLOG("UPDTWKP", PGLOG['GDEXWORK']) # dsupdt work root path
|
|
1359
|
+
SETPGLOG("TRANSFER", "/lustre/desc1/gdex/transfer") # gdex transfer path
|
|
1360
|
+
SETPGLOG("RQSTHOME", PGLOG['TRANSFER']+"/dsrqst") # dsrqst home
|
|
1361
|
+
SETPGLOG("DSAHOME", "") # dataset data alternate root path
|
|
1362
|
+
SETPGLOG("RQSTALTH", "") # alternate dsrqst path
|
|
1363
|
+
SETPGLOG("GPFSHOST", "") # empty if writable to glade
|
|
1364
|
+
SETPGLOG("PSQLHOST", "rda-db.ucar.edu") # host name for postgresql server
|
|
1365
|
+
SETPGLOG("SLMHOSTS", "cheyenne:casper") # host names for SLURM server
|
|
1366
|
+
SETPGLOG("PBSHOSTS", "cron:casper") # host names for PBS server
|
|
1367
|
+
SETPGLOG("CHKHOSTS", "") # host names for dscheck daemon
|
|
1368
|
+
SETPGLOG("PVIEWHOST", "pgdb02.k8s.ucar.edu") # host name for view only postgresql server
|
|
1369
|
+
SETPGLOG("PMISCHOST", "pgdb03.k8s.ucar.edu") # host name for misc postgresql server
|
|
1370
|
+
SETPGLOG("FTPUPLD", PGLOG['TRANSFER']+"/rossby") # ftp upload path
|
|
1371
|
+
PGLOG['GPFSROOTS'] = "{}|{}|{}".format(PGLOG['DSDHOME'], PGLOG['UPDTWKP'], PGLOG['RQSTHOME'])
|
|
1372
|
+
|
|
1373
|
+
if 'ECCODES_DEFINITION_PATH' not in os.environ:
|
|
1374
|
+
os.environ['ECCODES_DEFINITION_PATH'] = "/usr/local/share/eccodes/definitions"
|
|
1375
|
+
os.environ['history'] = '0'
|
|
1376
|
+
|
|
1377
|
+
# set tmp dir
|
|
1378
|
+
SETPGLOG("TMPPATH", PGLOG['GDEXWORK'] + "/ptmp")
|
|
1379
|
+
if not PGLOG['TMPPATH']: PGLOG['TMPPATH'] = "/data/ptmp"
|
|
1380
|
+
|
|
1381
|
+
SETPGLOG("TMPDIR", '')
|
|
1382
|
+
if not PGLOG['TMPDIR']:
|
|
1383
|
+
PGLOG['TMPDIR'] = "/lustre/desc1/scratch/" + PGLOG['CURUID']
|
|
1384
|
+
os.environ['TMPDIR'] = PGLOG['TMPDIR']
|
|
1385
|
+
|
|
1386
|
+
# empty diretory for HOST-sync
|
|
1387
|
+
|
|
1388
|
+
PGLOG['TMPSYNC'] = PGLOG['DSSDBHM'] + "/tmp/.syncdir"
|
|
1389
|
+
|
|
1390
|
+
os.umask(2)
|
|
1391
|
+
|
|
1392
|
+
#
|
|
1393
|
+
# check and return TMPSYNC path, and add it if not exists
|
|
1394
|
+
#
|
|
1395
|
+
def get_tmpsync_path():
|
|
1396
|
+
|
|
1397
|
+
if 'DSSHOME' in PGLOG and PGLOG['DSSHOME'] and not op.exists(PGLOG['TMPSYNC']):
|
|
1398
|
+
pgsystem("mkdir " + PGLOG['TMPSYNC'], 0, LGWNEX, 4)
|
|
1399
|
+
pgsystem("chmod 775 " + PGLOG['TMPSYNC'], LOGWRN, 4)
|
|
1400
|
+
return PGLOG['TMPSYNC']
|
|
1401
|
+
|
|
1402
|
+
#
|
|
1403
|
+
# append or prepend locpath to pgpath
|
|
1404
|
+
#
|
|
1405
|
+
def add_local_path(locpath, pgpath, append = 0):
|
|
1406
|
+
|
|
1407
|
+
if not locpath:
|
|
1408
|
+
return pgpath
|
|
1409
|
+
elif not pgpath:
|
|
1410
|
+
return locpath
|
|
1411
|
+
|
|
1412
|
+
paths = locpath.split(':')
|
|
1413
|
+
|
|
1414
|
+
for path in paths:
|
|
1415
|
+
if re.match(r'^\./*$', path): continue
|
|
1416
|
+
path = path.rstrip('\\')
|
|
1417
|
+
ms = re.search(r'(^|:){}(:|$)'.format(path), pgpath)
|
|
1418
|
+
if ms: continue
|
|
1419
|
+
if append:
|
|
1420
|
+
pgpath += ":" + path
|
|
1421
|
+
else:
|
|
1422
|
+
pgpath = path + ":" + pgpath
|
|
1423
|
+
|
|
1424
|
+
return pgpath
|
|
1425
|
+
|
|
1426
|
+
#
|
|
1427
|
+
# set PGLOG value; return a string or an array reference if sep is not emty
|
|
1428
|
+
#
|
|
1429
|
+
def SETPGLOG(name, value = ''):
|
|
1430
|
+
|
|
1431
|
+
oval = PGLOG[name] if name in PGLOG else ''
|
|
1432
|
+
nval = get_environment(name, ('' if re.match('PG', value) else value))
|
|
1433
|
+
PGLOG[name] = nval if nval else oval
|
|
1434
|
+
|
|
1435
|
+
#
|
|
1436
|
+
# set specialist home and return the default shell
|
|
1437
|
+
#
|
|
1438
|
+
def set_specialist_home(specialist):
|
|
1439
|
+
|
|
1440
|
+
if specialist == PGLOG['CURUID']: return # no need reset
|
|
1441
|
+
if 'MAIL' in os.environ and re.search(PGLOG['CURUID'], os.environ['MAIL']):
|
|
1442
|
+
os.environ['MAIL'] = re.sub(PGLOG['CURUID'], specialist, os.environ['MAIL'])
|
|
1443
|
+
|
|
1444
|
+
home = "{}/{}".format(PGLOG['USRHOME'], specialist)
|
|
1445
|
+
shell = "tcsh"
|
|
1446
|
+
buf = pgsystem("grep ^{}: /etc/passwd".format(specialist), LOGWRN, 20)
|
|
1447
|
+
if buf:
|
|
1448
|
+
lines = buf.split('\n')
|
|
1449
|
+
for line in lines:
|
|
1450
|
+
ms = re.search(r':(/.+):(/.+)', line)
|
|
1451
|
+
if ms:
|
|
1452
|
+
home = ms.group(1)
|
|
1453
|
+
shell = op.basename(ms.group(2))
|
|
1454
|
+
break
|
|
1455
|
+
|
|
1456
|
+
if home != os.environ['HOME'] and op.exists(home):
|
|
1457
|
+
os.environ['HOME'] = home
|
|
1458
|
+
|
|
1459
|
+
return shell
|
|
1460
|
+
|
|
1461
|
+
#
|
|
1462
|
+
# set environments for a specified specialist
|
|
1463
|
+
#
|
|
1464
|
+
def set_specialist_environments(specialist):
|
|
1465
|
+
|
|
1466
|
+
shell = set_specialist_home(specialist)
|
|
1467
|
+
resource = os.environ['HOME'] + "/.tcshrc"
|
|
1468
|
+
checkif = 0 # 0 outside of if; 1 start if, 2 check envs, -1 checked already
|
|
1469
|
+
missthen = 0
|
|
1470
|
+
try:
|
|
1471
|
+
rf = open(resource, 'r')
|
|
1472
|
+
except:
|
|
1473
|
+
return # skip if cannot open
|
|
1474
|
+
|
|
1475
|
+
nline = rf.readline()
|
|
1476
|
+
while nline:
|
|
1477
|
+
line = pgtrim(nline)
|
|
1478
|
+
nline = rf.readline()
|
|
1479
|
+
if not line: continue
|
|
1480
|
+
if checkif == 0:
|
|
1481
|
+
ms = re.match(r'^if(\s|\()', line)
|
|
1482
|
+
if ms: checkif = 1 # start if
|
|
1483
|
+
elif missthen:
|
|
1484
|
+
missthen = 0
|
|
1485
|
+
if re.match(r'^then$', line): continue # then on next line
|
|
1486
|
+
checkif = 0 # end of inline if
|
|
1487
|
+
elif re.match(r'^endif', line):
|
|
1488
|
+
checkif = 0 # end of if
|
|
1489
|
+
continue
|
|
1490
|
+
elif checkif == -1: # skip the line
|
|
1491
|
+
continue
|
|
1492
|
+
elif checkif == 2 and re.match(r'^else', line):
|
|
1493
|
+
checkif = -1 # done check envs in if
|
|
1494
|
+
continue
|
|
1495
|
+
|
|
1496
|
+
if checkif == 1:
|
|
1497
|
+
if line == 'else':
|
|
1498
|
+
checkif = 2
|
|
1499
|
+
continue
|
|
1500
|
+
elif re.search(r'if\W', line):
|
|
1501
|
+
if(re.search(r'host.*!', line, re.I) and not re.search(PGLOG['HOSTNAME'], line) or
|
|
1502
|
+
re.search(r'host.*=', line, re.I) and re.search(PGLOG['HOSTNAME'], line)):
|
|
1503
|
+
checkif = 2
|
|
1504
|
+
if re.search(r'\sthen$', line):
|
|
1505
|
+
continue
|
|
1506
|
+
else:
|
|
1507
|
+
missthen = 1
|
|
1508
|
+
if checkif == 1: continue
|
|
1509
|
+
else:
|
|
1510
|
+
continue
|
|
1511
|
+
|
|
1512
|
+
ms = re.match(r'^setenv\s+(.*)', line)
|
|
1513
|
+
if ms: one_specialist_environment(ms.group(1))
|
|
1514
|
+
|
|
1515
|
+
rf.close()
|
|
1516
|
+
|
|
1517
|
+
SETPGLOG("HOMEBIN", PGLOG['PGBINDIR'])
|
|
1518
|
+
os.environ['PATH'] = add_local_path(PGLOG['HOMEBIN'], os.environ['PATH'], 0)
|
|
1519
|
+
|
|
1520
|
+
#
|
|
1521
|
+
# set one environment for specialist
|
|
1522
|
+
#
|
|
1523
|
+
def one_specialist_environment(line):
|
|
1524
|
+
|
|
1525
|
+
ms = re.match(r'^(\w+)[=\s]+(.+)$', line)
|
|
1526
|
+
if not ms: return
|
|
1527
|
+
(var, val) = ms.groups()
|
|
1528
|
+
if re.match(r'^(PATH|SHELL|IFS|CDPATH|)$', var): return
|
|
1529
|
+
if val.find('$') > -1: val = replace_environments(val)
|
|
1530
|
+
ms = re.match(r'^(\"|\')(.*)(\"|\')$', val)
|
|
1531
|
+
if ms: val = ms.group(2) # remove quotes
|
|
1532
|
+
os.environ[var] = val
|
|
1533
|
+
|
|
1534
|
+
#
|
|
1535
|
+
# get and repalce environment variables in ginve string; defaults to the values in PGLOG
|
|
1536
|
+
#
|
|
1537
|
+
def replace_environments(envstr, default = '', logact = 0):
|
|
1538
|
+
|
|
1539
|
+
ishash = isinstance(default, dict)
|
|
1540
|
+
ms = re.search(r'(^|.)\$({*)(\w+)(}*)', envstr)
|
|
1541
|
+
if ms:
|
|
1542
|
+
lead = ms.group(1)
|
|
1543
|
+
name = ms.group(3)
|
|
1544
|
+
rep = ms.group(2) + name + ms.group(4)
|
|
1545
|
+
env = get_environment(name, (PGLOG[name] if name in PGLOG else (default[name] if ishash else default)), logact)
|
|
1546
|
+
pre = (lead if (env or lead != ":") else '')
|
|
1547
|
+
envstr = re.sub(r'{}\${}'.format(lead, rep), (pre+env), envstr)
|
|
1548
|
+
|
|
1549
|
+
return envstr
|
|
1550
|
+
|
|
1551
|
+
#
|
|
1552
|
+
# validate if the current host is a valid host to process
|
|
1553
|
+
#
|
|
1554
|
+
def check_process_host(hosts, chost = None, mflag = None, pinfo = None, logact = None):
|
|
1555
|
+
|
|
1556
|
+
ret = 1
|
|
1557
|
+
error = ''
|
|
1558
|
+
if not mflag: mflag = 'G'
|
|
1559
|
+
if not chost: chost = get_host(1)
|
|
1560
|
+
|
|
1561
|
+
if mflag == 'M': # exact match
|
|
1562
|
+
if not hosts or hosts != chost:
|
|
1563
|
+
ret = 0
|
|
1564
|
+
if pinfo: error = "not matched exactly"
|
|
1565
|
+
elif mflag == 'I': # inclusive match
|
|
1566
|
+
if not hosts or hosts.find('!') == 0 or hosts.find(chost) < 0:
|
|
1567
|
+
ret = 0
|
|
1568
|
+
if pinfo: error = "not matched inclusively"
|
|
1569
|
+
elif hosts:
|
|
1570
|
+
if hosts.find(chost) >= 0:
|
|
1571
|
+
if hosts.find('!') == 0:
|
|
1572
|
+
ret = 0
|
|
1573
|
+
if pinfo: error = "matched exclusively"
|
|
1574
|
+
elif hosts.find('!') != 0:
|
|
1575
|
+
ret = 0
|
|
1576
|
+
if pinfo: error = "not matched"
|
|
1577
|
+
|
|
1578
|
+
if error:
|
|
1579
|
+
if logact is None: logact = LOGERR
|
|
1580
|
+
pglog("{}: CANNOT be processed on {} for hosthame {}".format(pinfo, chost, error), logact)
|
|
1581
|
+
|
|
1582
|
+
return ret
|
|
1583
|
+
|
|
1584
|
+
#
|
|
1585
|
+
# convert special foreign characters into ascii characters
|
|
1586
|
+
#
|
|
1587
|
+
def convert_chars(name, default = 'X'):
|
|
1588
|
+
if not name: return default
|
|
1589
|
+
if re.match(r'^[a-zA-Z0-9]+$', name): return name # conversion not needed
|
|
1590
|
+
decoded_name = unidecode(name).strip()
|
|
1591
|
+
# remove any non-alphanumeric and non-underscore characters
|
|
1592
|
+
cleaned_name = re.sub(r'[^a-zA-Z0-9_]', '', decoded_name)
|
|
1593
|
+
if cleaned_name:
|
|
1594
|
+
return cleaned_name
|
|
1595
|
+
else:
|
|
1596
|
+
return default
|
|
1597
|
+
|
|
1598
|
+
#
|
|
1599
|
+
# Retrieve host and process id
|
|
1600
|
+
#
|
|
1601
|
+
def current_process_info(realpid = 0):
|
|
1602
|
+
|
|
1603
|
+
if realpid or PGLOG['CURBID'] < 1:
|
|
1604
|
+
return [PGLOG['HOSTNAME'], os.getpid()]
|
|
1605
|
+
else:
|
|
1606
|
+
return [PGLOG['PGBATCH'], PGLOG['CURBID']]
|
|
1607
|
+
|
|
1608
|
+
#
|
|
1609
|
+
# convert given @ARGV to string. quote the entries with spaces
|
|
1610
|
+
#
|
|
1611
|
+
def argv_to_string(argv = None, quote = 1, action = None):
|
|
1612
|
+
|
|
1613
|
+
argstr = ''
|
|
1614
|
+
if argv is None: argv = sys.argv[1:]
|
|
1615
|
+
for arg in argv:
|
|
1616
|
+
if argstr: argstr += ' '
|
|
1617
|
+
ms = re.search(r'([<>\|\s])', arg)
|
|
1618
|
+
if ms:
|
|
1619
|
+
if action:
|
|
1620
|
+
pglog("{}: Cannot {} for special character '{}' in argument value".format(arg, action, ms.group(1)), LGEREX)
|
|
1621
|
+
if quote:
|
|
1622
|
+
if re.search(r"\'", arg):
|
|
1623
|
+
arg = "\"{}\"".format(arg)
|
|
1624
|
+
else:
|
|
1625
|
+
arg = "'{}'".format(arg)
|
|
1626
|
+
argstr += arg
|
|
1627
|
+
|
|
1628
|
+
return argstr
|
|
1629
|
+
|
|
1630
|
+
#
|
|
1631
|
+
# convert an integer to non-10 based string
|
|
1632
|
+
#
|
|
1633
|
+
def int2base(x, base):
|
|
1634
|
+
|
|
1635
|
+
if x == 0: return '0'
|
|
1636
|
+
negative = 0
|
|
1637
|
+
if x < 0:
|
|
1638
|
+
negative = 1
|
|
1639
|
+
x = -x
|
|
1640
|
+
|
|
1641
|
+
dgts = []
|
|
1642
|
+
while x:
|
|
1643
|
+
dgts.append(str(int(x%base)))
|
|
1644
|
+
x = int(x/base)
|
|
1645
|
+
if negative: dgts.append('-')
|
|
1646
|
+
dgts.reverse()
|
|
1647
|
+
|
|
1648
|
+
return ''.join(dgts)
|
|
1649
|
+
|
|
1650
|
+
#
|
|
1651
|
+
# convert a non-10 based string to an integer
|
|
1652
|
+
#
|
|
1653
|
+
def base2int(x, base):
|
|
1654
|
+
|
|
1655
|
+
if not isinstance(x, int): x = int(x)
|
|
1656
|
+
if x == 0: return 0
|
|
1657
|
+
|
|
1658
|
+
negative = 0
|
|
1659
|
+
if x < 0:
|
|
1660
|
+
negative = 1
|
|
1661
|
+
x = -x
|
|
1662
|
+
|
|
1663
|
+
num = 0
|
|
1664
|
+
fact = 1
|
|
1665
|
+
while x:
|
|
1666
|
+
num += (x%10)*fact
|
|
1667
|
+
fact *= base
|
|
1668
|
+
x = int(x/10)
|
|
1669
|
+
if negative: num = -num
|
|
1670
|
+
|
|
1671
|
+
return num
|
|
1672
|
+
|
|
1673
|
+
#
|
|
1674
|
+
# convert integer to ordinal string
|
|
1675
|
+
#
|
|
1676
|
+
def int2order(num):
|
|
1677
|
+
|
|
1678
|
+
ordstr = ['th', 'st', 'nd', 'rd']
|
|
1679
|
+
snum = str(num)
|
|
1680
|
+
num %= 100
|
|
1681
|
+
if num > 19: num %= 10
|
|
1682
|
+
if num > 3: num = 0
|
|
1683
|
+
|
|
1684
|
+
return snum + ordstr[num]
|
|
1685
|
+
|
|
1686
|
+
#
|
|
1687
|
+
# Always call this function to initialize global variables for all applications
|
|
1688
|
+
#
|
|
1689
|
+
set_common_pglog()
|