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.
File without changes
@@ -0,0 +1,493 @@
1
+ #
2
+ ###############################################################################
3
+ #
4
+ # Title : pg_cmd.py
5
+ # Author : Zaihua Ji, zji@ucar.edu
6
+ # Date : 08/25/2020
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 for functions to record commands for delayed
10
+ # mode or command recovery
11
+ #
12
+ # Github : https://github.com/NCAR/rda-python-common.git
13
+ #
14
+ ###############################################################################
15
+ #
16
+ import os
17
+ import re
18
+ import sys
19
+ import time
20
+ from .pg_lock import PgLock
21
+
22
+ class PgCMD(PgLock):
23
+
24
+ def __init__(self):
25
+ super().__init__() # initialize parent class
26
+ # cached dscheck info
27
+ self.DSCHK = {}
28
+ self.BOPTIONS = {"hostname" : None, "qoptions" : None, "modules" : None, "environments" : None}
29
+ self.BFIELDS = ', '.join(self.BOPTIONS)
30
+ self.TRYLMTS = {
31
+ 'dsquasar' : 3,
32
+ 'dsarch' : 2,
33
+ 'default' : 1
34
+ }
35
+ self.DLYPTN = r'(^|\s)-(d|BP|BatchProcess|DelayedMode)(\s|$)'
36
+ self.DLYOPT = {
37
+ 'dsarch' : ' -d',
38
+ 'dsupdt' : ' -d',
39
+ 'dsrqst' : ' -d'
40
+ }
41
+
42
+ # params: dict array holding option values
43
+ # opt: 2 - each value of the dict array is a list; otherwise 1
44
+ # addhost: 1 to add host name too
45
+ # initial set Batch options passed in from command line
46
+ def set_batch_options(self, params, opt, addhost = 0):
47
+ if 'QS' in params: self.BOPTIONS['qoptions'] = (params['QS'][0] if opt == 2 else params['QS'])
48
+ if 'MO' in params: self.BOPTIONS['modules'] = (params['MO'][0] if opt == 2 else params['MO'])
49
+ if 'EV' in params: self.BOPTIONS['environments'] = (params['EV'][0] if opt == 2 else params['EV'])
50
+ if addhost and 'HN' in params: self.BOPTIONS['hostname'] = (params['HN'][0] if opt == 2 else params['HN'])
51
+
52
+ # boptions: dict array holding batch options
53
+ # refresh: 1 to clean the previous cached global batch options
54
+ # checkkey: 1 to check and valid pre-defined fields
55
+ # fill Batch options recorded in RDADB
56
+ def fill_batch_options(self, boptions, refresh = 0, checkkey = 0):
57
+ if refresh:
58
+ for bkey in self.BOPTIONS:
59
+ self.BOPTIONS[bkey] = None # clean the hash before filling it up
60
+ if not boptions: return
61
+ for bkey in boptions:
62
+ if not checkkey or bkey in self.BOPTIONS:
63
+ self.BOPTIONS[bkey] = boptions[bkey]
64
+
65
+ # bkey: batch option field name
66
+ # bval: batch option value
67
+ # override: 1 to override an existing option
68
+ # fill a single Batch option
69
+ def set_one_boption(self, bkey, bval, override = 0):
70
+ if bval:
71
+ if override or not ( bkey in self.BOPTIONS and self.BOPTIONS[bkey]): self.BOPTIONS[bkey] = bval
72
+ elif override and bkey in self.BOPTIONS and self.BOPTIONS[bkey]:
73
+ self.BOPTIONS[bkey] = None
74
+
75
+ # fill the passed in dict record with the pre-saved batch options
76
+ def get_batch_options(self, pgrec = None):
77
+ record = {}
78
+ for bkey in self.BOPTIONS:
79
+ if pgrec and bkey in pgrec and pgrec[bkey]:
80
+ record[bkey] = pgrec[bkey]
81
+ elif self.BOPTIONS[bkey]:
82
+ record[bkey] = self.BOPTIONS[bkey]
83
+ return record
84
+
85
+ # return delay mode option to append to argv string for a specified cmd
86
+ def append_delayed_mode(self, cmd, argv):
87
+ if cmd in self.DLYOPT and not re.search(self.DLYPTN, argv, re.I):
88
+ return self.DLYOPT[cmd]
89
+ else:
90
+ return ''
91
+
92
+ # check given doptions and cmd, and return the try limit and specified hosts
93
+ def get_delay_options(self, doptions, cmd):
94
+ mcount = 0
95
+ hosts = None
96
+ if doptions:
97
+ for bval in doptions:
98
+ if re.match(r'^(\d+)$', bval):
99
+ mcount = int(bval)
100
+ if mcount > 99: mcount = 99
101
+ else:
102
+ hosts = bval
103
+ if mcount == 0: mcount = self.get_try_limit(cmd)
104
+ if hosts: self.set_one_boption('hostname', hosts, 1)
105
+ return (mcount, hosts)
106
+
107
+ # find an existing dscheck record from the cached command argument; create and initialize one if not exist
108
+ def init_dscheck(self, oindex, otype, cmd, dsid, action, workdir = None, specialist = None, doptions = None, logact = 0):
109
+ cidx = 0
110
+ argv = self.argv_to_string(sys.argv[1:], 0, "Process in Delayed Mode")
111
+ argextra = None
112
+ if not logact: logact = self.LGEREX
113
+ if not workdir: workdir = os.getcwd()
114
+ if not specialist: specialist = self.PGLOG['CURUID']
115
+ (mcount, hosts) = self.get_delay_options(doptions, cmd)
116
+ if len(argv) > 100:
117
+ argextra = argv[100:]
118
+ argv = argv[0:100]
119
+ bck = self.PGLOG['BCKGRND']
120
+ self.PGLOG['BCKGRND'] = 0
121
+ cinfo = "{}-{}-Chk".format(self.PGLOG['HOSTNAME'], self.current_datetime())
122
+ pgrec = self.get_dscheck(cmd, argv, workdir, specialist, argextra, logact)
123
+ if pgrec: # found existing dscheck record
124
+ cidx = pgrec['cindex']
125
+ cmsg = "{}{}: {} batch process ".format(cinfo, cidx, self.get_command_info(pgrec))
126
+ cidx = self.lock_dscheck(cidx, 1, self.LOGWRN)
127
+ if cidx < 0:
128
+ self.pglog(cmsg + "is Running, No restart", self.LOGWRN)
129
+ sys.exit(0)
130
+ if cidx > 0:
131
+ if not hosts and pgrec['hostname']:
132
+ hosts = pgrec['hostname']
133
+ self.set_one_boption('hostname', hosts, 0)
134
+ if mcount: pgrec['mcount'] = mcount
135
+ self.DSCHK['chkcnd'] = "cindex = {}".format(cidx)
136
+ if(pgrec['status'] == 'D' or pgrec['fcount'] and pgrec['dcount'] >= pgrec['fcount'] or
137
+ pgrec['tcount'] > pgrec['mcount'] or not pgrec['pid'] and pgrec['tcount'] == pgrec['mcount']):
138
+ self.pglog("{}is {}".format(cmsg, ('Done' if pgrec['status'] == 'D' else 'Finished')), self.LOGWRN)
139
+ self.lock_dscheck(cidx, 0, logact)
140
+ sys.exit(0)
141
+ if not cidx: # add new dscheck record
142
+ record = {}
143
+ if hosts and re.match(r'^(ds\d|\d)\d\d.\d$', hosts):
144
+ self.pglog(hosts + ": Cannot pass DSID for hostname to submit batch process", self.LGEREX)
145
+ if oindex: self.set_command_control(oindex, otype, cmd, logact)
146
+ record['oindex'] = oindex
147
+ record['dsid'] = dsid
148
+ record['action'] = action
149
+ record['otype'] = otype
150
+ (record['date'], record['time']) = self.get_date_time()
151
+ record['command'] = cmd
152
+ record['argv'] = argv
153
+ if mcount > 0: record['mcount'] = mcount
154
+ record['specialist'] = specialist
155
+ record['workdir'] = workdir
156
+ if argextra: record['argextra'] = argextra
157
+ record.update(self.get_batch_options())
158
+ cidx = self.pgadd("dscheck", record, logact|self.AUTOID)
159
+ if cidx:
160
+ cmsg = "{}{}: {} Adds a new check".format(cinfo, cidx, self.get_command_info(record))
161
+ self.pglog(cmsg, self.LOGWRN)
162
+ sys.exit(0)
163
+
164
+ (chost, cpid) = self.current_process_info()
165
+ (rhost, rpid) = self.current_process_info(1)
166
+
167
+ if not self.check_command_specialist_host(hosts, chost, specialist, cmd, action, self.LOGERR):
168
+ self.lock_dscheck(cidx, 0, logact)
169
+ sys.exit(1)
170
+
171
+ record = {}
172
+ record['status'] = "R"
173
+ if mcount > 0: record['mcount'] = mcount
174
+ record['bid'] = (cpid if self.PGLOG['CURBID'] else 0)
175
+ if pgrec['stttime'] and pgrec['chktime'] > pgrec['stttime']:
176
+ (record['ttltime'], record['quetime']) = self.get_dscheck_runtime(pgrec)
177
+ record['chktime'] = record['stttime'] = int(time.time())
178
+ if not pgrec['subtime']: record['subtime'] = record['stttime']
179
+ if dsid and not pgrec['dsid']: record['dsid'] = dsid
180
+ if action and not pgrec['action']: record['action'] = action
181
+ if oindex and not pgrec['oindex']: record['oindex'] = oindex
182
+ if otype and not pgrec['otype']: record['otype'] = otype
183
+ if argv and not pgrec['argv']: record['argv'] = argv
184
+ record['runhost'] = rhost
185
+ if pgrec['command'] == "dsrqst" and pgrec['oindex']:
186
+ (record['fcount'], record['dcount'], record['size']) = self.get_dsrqst_counts(pgrec, logact)
187
+ self.pgupdt("dscheck", record, self.DSCHK['chkcnd'], logact)
188
+ self.DSCHK['dcount'] = pgrec['dcount']
189
+ self.DSCHK['fcount'] = pgrec['fcount']
190
+ self.DSCHK['size'] = pgrec['size']
191
+ self.DSCHK['cindex'] = cidx
192
+ self.DSCHK['dflags'] = pgrec['dflags']
193
+ self.PGLOG['DSCHECK'] = self.DSCHK # add global access link
194
+ if not self.PGLOG['BCKGRND']: self.PGLOG['BCKGRND'] = 1 # turn off screen output if not yet
195
+ tcnt = pgrec['tcount']
196
+ if not pgrec['pid']: tcnt += 1
197
+ tstr = "the {} run".format(self.int2order(tcnt)) if tcnt > 1 else "running"
198
+ pstr = "{}<{}>".format(chost, cpid)
199
+ if rhost != chost: pstr += "/{}<{}>".format(rhost, rpid)
200
+ self.pglog("{}Starts {} ({})".format(cmsg, tstr, pstr), self.LOGWRN)
201
+ self.PGLOG['BCKGRND'] = bck
202
+ return cidx
203
+
204
+ # check and validate if the current host is configured for the specialist
205
+ def check_command_specialist_host(self, hosts, chost, specialist, cmd, act = 0, logact = 0):
206
+ if cmd == 'dsrqst' and act == 'PR':
207
+ mflag = 'G'
208
+ else:
209
+ cnd = "command = '{}' AND specialist = '{}' AND hostname = '{}'".format(cmd, specialist, chost)
210
+ pgrec = self.pgget("dsdaemon", 'matchhost', cnd, logact)
211
+ mflag = (pgrec['matchhost'] if pgrec else 'G')
212
+ return self.check_process_host(hosts, chost, mflag, "{}-{}".format(specialist, cmd), logact)
213
+
214
+ # set command control info
215
+ def set_command_control(self, oindex, otype, cmd, logact = 0):
216
+ if not oindex: return
217
+ pgctl = None
218
+ if cmd == "dsrqst":
219
+ if otype == 'P':
220
+ pgrec = self.pgget("ptrqst", "rindex", "pindex = {}".format(oindex), logact)
221
+ if pgrec: pgctl = self.get_partition_control(pgrec, None, None, logact)
222
+ else:
223
+ pgrec = self.pgget("dsrqst", "dsid, gindex, cindex, rqsttype", "rindex = {}".format(oindex), logact)
224
+ if pgrec: pgctl = self.get_dsrqst_control(pgrec, logact)
225
+ elif cmd == "dsupdt":
226
+ if otype == 'L':
227
+ pgrec = self.pgget("dlupdt", "cindex", "lindex = {}".format(oindex), logact)
228
+ if not (pgrec and pgrec['cindex']): return
229
+ oindex = pgrec['cindex']
230
+ pgctl = self.pgget("dcupdt", self.BFIELDS, "cindex = {}".format(oindex), logact)
231
+ if pgctl:
232
+ for bkey in pgctl:
233
+ self.set_one_boption(bkey, pgctl[bkey], 0)
234
+
235
+ # get dsrqst control info
236
+ def get_dsrqst_control(self, pgrqst, logact = 0):
237
+ cflds = self.BFIELDS
238
+ if 'ptcount' in pgrqst and pgrqst['ptcount'] == 0: cflds += ", ptlimit, ptsize"
239
+ if pgrqst['cindex']:
240
+ pgctl = self.pgget("rcrqst", cflds, "cindex = {}".format(pgrqst['cindex']), logact)
241
+ else:
242
+ pgctl = None
243
+ if not pgctl:
244
+ gcnd = "dsid = '{}' AND gindex = ".format(pgrqst['dsid'])
245
+ if pgrqst['rqsttype'] in "ST":
246
+ tcnd = " AND (rqsttype = 'T' OR rqsttype = 'S')"
247
+ else:
248
+ tcnd = " AND rqsttype = '{}'".format(pgrqst['rqsttype'])
249
+ gindex = pgrqst['gindex']
250
+ while True:
251
+ pgctl = self.pgget("rcrqst", cflds, "{}{}{}".format(gcnd, gindex, tcnd), logact)
252
+ if pgctl or not gindex: break
253
+ pgctl = self.pgget("dsgroup", "pindex", "{}{}".format(gcnd, gindex), logact)
254
+ if not pgctl: break
255
+ gindex = pgctl['pindex']
256
+ return pgctl
257
+
258
+ # get dsrqst partition control info
259
+ def get_partition_control(self, pgpart, pgrqst = None, pgctl = None, logact = 0):
260
+ if not pgctl:
261
+ if not pgrqst and pgpart['rindex']:
262
+ pgrqst = self.pgget("dsrqst", "dsid, gindex, cindex, rqsttype", "rindex = {}".format(pgpart['rindex']), logact)
263
+ if pgrqst: pgctl = self.get_dsrqst_control(pgrqst, logact)
264
+ return pgctl
265
+
266
+ # build the dynamic options
267
+ def get_dynamic_options(self, cmd, oindex, otype):
268
+
269
+ if oindex: cmd += " {}".format(oindex)
270
+ if otype: cmd += ' ' + otype
271
+ ret = options = ''
272
+ for loop in range(3):
273
+ ret = self.pgsystem(cmd, self.LOGWRN, 279) # 1+2+4+16+256
274
+ if loop < 2 and self.PGLOG['SYSERR'] and 'Connection timed out' in self.PGLOG['SYSERR']:
275
+ time.sleep(self.PGSIG['ETIME'])
276
+ else:
277
+ break
278
+ if ret:
279
+ ret = ret.strip()
280
+ ms = re.match(r'^(-.+)/(-.+)$', ret)
281
+ if ms:
282
+ options = ms.group(1) if otype == 'R' else ms.group(2)
283
+ elif re.match(r'^(-.+)$', ret):
284
+ options = ret
285
+ if not options:
286
+ if ret: self.PGLOG['SYSERR'] += ret
287
+ self.PGLOG['SYSERR'] += " for {}".format(cmd)
288
+
289
+ return options
290
+
291
+ # retrieve a dscheck record for provided cmd, argv and other conditions
292
+ def get_dscheck(self, cmd, argv, workdir, specialist, argextra = None, logact = 0):
293
+ cnd = "command = '{}' AND specialist = '{}' AND argv = '{}'".format(cmd, specialist, argv)
294
+ pgrecs = self.pgmget("dscheck", "*", cnd, logact)
295
+ cnt = len(pgrecs['cindex']) if pgrecs else 0
296
+ if cnt == 0 and cmd in self.DLYOPT:
297
+ ms = re.match(r'^(.+){}$'.format(self.DLYOPT[cmd]), argv)
298
+ if ms:
299
+ argv = ms.group(1)
300
+ cnt = 1
301
+ elif not argextra:
302
+ dopt = self.append_delayed_mode(cmd, argv)
303
+ if dopt:
304
+ argv += dopt
305
+ cnt = 1
306
+ if cnt:
307
+ cnd = "command = '{}' AND specialist = '{}' AND argv = '{}'".format(cmd, specialist, argv)
308
+ pgrecs = self.pgmget("dscheck", "*", cnd, logact)
309
+ cnt = len(pgrecs['cindex']) if pgrecs else 0
310
+ for i in range(cnt):
311
+ pgrec = self.onerecord(pgrecs, i)
312
+ if pgrec['workdir'] and self.pgcmp(workdir, pgrec['workdir']): continue
313
+ if self.pgcmp(argextra, pgrec['argextra']): continue
314
+ return pgrec
315
+ return None
316
+
317
+ # delete one dsceck record fo given cindex
318
+ def delete_dscheck(self, pgrec, chkcnd, logact = 0):
319
+ if not chkcnd:
320
+ if pgrec:
321
+ chkcnd = "cindex = {}".format(pgrec['cindex'])
322
+ elif 'chkcnd' in self.DSCHK:
323
+ chkcnd = self.DSCHK['chkcnd']
324
+ else:
325
+ return 0 # nothing to delete
326
+ if not pgrec:
327
+ pgrec = self.pgget("dscheck", "*", chkcnd, logact)
328
+ if not pgrec: return 0 # dscheck record is gone
329
+ record = {}
330
+ record['cindex'] = pgrec['cindex']
331
+ record['command'] = pgrec['command']
332
+ record['dsid'] = (pgrec['dsid'] if pgrec['dsid'] else self.PGLOG['DEFDSID'])
333
+ record['action'] = (pgrec['action'] if pgrec['action'] else "UN")
334
+ record['specialist'] = pgrec['specialist']
335
+ record['hostname'] = pgrec['runhost']
336
+ if pgrec['bid']: record['bid'] = pgrec['bid']
337
+ if pgrec['command'] == "dsrqst" and pgrec['oindex']:
338
+ (record['fcount'], record['dcount'], record['size']) = self.get_dsrqst_counts(pgrec, logact)
339
+ else:
340
+ record['fcount'] = pgrec['fcount']
341
+ record['dcount'] = pgrec['dcount']
342
+ record['size'] = pgrec['size']
343
+ record['tcount'] = pgrec['tcount']
344
+ record['date'] = pgrec['date']
345
+ record['time'] = pgrec['time']
346
+ record['closetime'] = self.curtime(1)
347
+ (record['ttltime'], record['quetime']) = self.get_dscheck_runtime(pgrec)
348
+ record['argv'] = pgrec['argv']
349
+ if pgrec['argextra']:
350
+ record['argv'] += pgrec['argextra']
351
+ if len(record['argv']) > 255: record['argv'] = record['argv'][0:255]
352
+ if pgrec['errmsg']: record['errmsg'] = pgrec['errmsg']
353
+ record['status'] = ('F' if pgrec['status'] == "R" else pgrec['status'])
354
+ if self.pgget("dschkhist", "", chkcnd):
355
+ stat = self.pgupdt("dschkhist", record, chkcnd, logact)
356
+ else:
357
+ stat = self.pgadd("dschkhist", record, logact)
358
+ if stat:
359
+ cmsg = "{} cleaned as '{}' at {} on {}".format(self.get_command_info(pgrec), record['status'], self.current_datetime(), self.PGLOG['HOSTNAME'])
360
+ self.pglog("Chk{}: {}".format(pgrec['cindex'], cmsg), self.LOGWRN|self.FRCLOG)
361
+ stat = self.pgdel("dscheck", chkcnd, logact)
362
+ if record['status'] == "E" and 'errmsg' in record:
363
+ self.pglog("Chk{}: {} Exits with Error\n{}".format(pgrec['cindex'], self.get_command_info(pgrec), record['errmsg']), logact)
364
+ return stat
365
+
366
+ # get dsrqst fcount and dcount
367
+ def get_dsrqst_counts(self, pgchk, logact = 0):
368
+ fcount = pgchk['fcount']
369
+ dcount = pgchk['dcount']
370
+ size = pgchk['size']
371
+ if pgchk['otype'] == 'P':
372
+ table = 'ptrqst'
373
+ cnd = "pindex = {}".format(pgchk['oindex'])
374
+ fields = "fcount"
375
+ else:
376
+ table = 'dsrqst'
377
+ cnd = "rindex = {}".format(pgchk['oindex'])
378
+ fields = "fcount, pcount, size_input, size_request"
379
+ pgrec = self.pgget(table, fields, cnd, logact)
380
+ if pgrec:
381
+ fcnt = pgrec['fcount']
382
+ else:
383
+ fcnt = 0
384
+ pgrec = {'fcount' : 0}
385
+ if not fcnt: fcnt = self.pgget("wfrqst", "", cnd, logact)
386
+ if fcnt and fcount != fcnt: fcount = fcnt
387
+ if fcount:
388
+ if 'pcount' in pgrec and pgrec['pcount']:
389
+ dcnt = pgrec['pcount']
390
+ else:
391
+ dcnt = self.pgget("wfrqst", "", cnd + " AND status = 'O'", logact)
392
+ if dcnt and dcnt != dcount: dcount = dcnt
393
+ if not size:
394
+ if 'size_input' in pgrec and pgrec['size_input']:
395
+ if size != pgrec['size_input']: size = pgrec['size_input']
396
+ elif 'size_request' in pgrec and pgrec['size_request']:
397
+ if size != pgrec['size_request']: size = pgrec['size_request']
398
+ elif fcnt: # evaluate total size only if file count is set in request/partition record
399
+ pgrec = self.pgget("wfrqst", "sum(size) data_size", cnd, logact)
400
+ if pgrec and pgrec['data_size']: size = pgrec['data_size']
401
+ return (fcount, dcount, size)
402
+
403
+ # set dscheck fcount
404
+ def set_dscheck_fcount(self, count, logact = 0):
405
+ record = {'fcount' : count, 'chktime' : int(time.time())}
406
+ self.pgupdt("dscheck", record, self.DSCHK['chkcnd'], logact)
407
+ self.DSCHK['fcount'] = count
408
+ return self.DSCHK['dcount'] # return Done count
409
+
410
+ # set dscheck dcount
411
+ def set_dscheck_dcount(self, count, size, logact = 0):
412
+ record = {'dcount' : count, 'size' : size, 'chktime' : int(time.time())}
413
+ self.pgupdt("dscheck", record, self.DSCHK['chkcnd'], logact)
414
+ self.DSCHK['dcount'] = count
415
+ self.DSCHK['size'] = size
416
+ return self.DSCHK['dcount'] # return Done count
417
+
418
+ # add dscheck dcount
419
+ def add_dscheck_dcount(self, count, size, logact = 0):
420
+ record = {}
421
+ if count:
422
+ self.DSCHK['dcount'] += count
423
+ record['dcount'] = self.DSCHK['dcount']
424
+ if size:
425
+ self.DSCHK['size'] += size
426
+ record['size'] = self.DSCHK['size']
427
+ record['chktime'] = int(time.time())
428
+ self.pgupdt("dscheck", record, self.DSCHK['chkcnd'], logact)
429
+ return self.DSCHK['dcount'] # return Done count
430
+
431
+ # set dscheck source information
432
+ def set_dscheck_attribute(self, fname, value, logact = 0):
433
+ record = {}
434
+ if value: record[fname] = value
435
+ record['chktime'] = int(time.time())
436
+ self.pgupdt("dscheck", record, self.DSCHK['chkcnd'], logact)
437
+
438
+ # update dscheck status
439
+ def record_dscheck_status(self, stat, logact = 0):
440
+ pgrec = self.pgget("dscheck", "lockhost, pid", self.DSCHK['chkcnd'], logact)
441
+ if not pgrec: return 0
442
+ if not (pgrec['pid'] and pgrec['lockhost']): return 0
443
+ (chost, cpid) = self.current_process_info()
444
+ if pgrec['pid'] != cpid or pgrec['lockhost'] != chost: return 0
445
+
446
+ # update dscheck status only if it is still locked by the current process
447
+ record = {'status' : stat, 'chktime' : int(time.time()), 'pid' : 0}
448
+ return self.pgupdt("dscheck", record, self.DSCHK['chkcnd'], logact)
449
+
450
+ # get the number of tries to execute for a given cmd under dscheck control
451
+ def get_try_limit(self, cmd):
452
+ return (self.TRYLMTS[cmd] if cmd in self.TRYLMTS else self.TRYLMTS['default'])
453
+
454
+ # get the execution time for a dscheck command
455
+ @staticmethod
456
+ def get_dscheck_runtime(pgrec, current = 0):
457
+ ttime = (0 if current else pgrec['ttltime'])
458
+ qtime = (0 if current else pgrec['quetime'])
459
+ if pgrec['subtime']:
460
+ ttime += (pgrec['chktime'] - pgrec['subtime'])
461
+ if pgrec['stttime']: qtime += (pgrec['stttime'] - pgrec['subtime'])
462
+ return (ttime, qtime)
463
+
464
+ # retrieve a command string from a given dscheck record
465
+ @staticmethod
466
+ def get_command_info(pgrec):
467
+ if pgrec['oindex']:
468
+ if pgrec['command'] == "dsupdt":
469
+ cinfo = "UC{}".format(pgrec['oindex'])
470
+ elif pgrec['command'] == "dsrqst":
471
+ if pgrec['otype'] == "P":
472
+ cinfo = "RPT{}".format(pgrec['oindex'])
473
+ else:
474
+ cinfo = "Rqst{}".format(pgrec['oindex'])
475
+ else:
476
+ cinfo ="{}-{}".format(pgrec['command'], pgrec['oindex'])
477
+ else:
478
+ cinfo = pgrec['command']
479
+ if pgrec['dsid']: cinfo += " " + pgrec['dsid']
480
+ if pgrec['action']: cinfo += " " + pgrec['action']
481
+ cinfo += " of " + pgrec['specialist']
482
+ return cinfo
483
+
484
+ # change the dscheck original command information
485
+ def change_dscheck_oinfo(self, oidx, otype, nidx, ntype):
486
+ cnd = "oindex = {} AND otype = '{}'".format(oidx, otype)
487
+ pgchk = self.pgget('dscheck', 'cindex, oindex, otype', cnd, self.LGEREX)
488
+ if not pgchk: return 0 # miss dscheck record to change
489
+ record = {}
490
+ self.DSCHK['oindex'] = record['oindex'] = nidx
491
+ self.DSCHK['otype'] = record['otype'] = ntype
492
+ cnd = "cindex = {}".format(pgchk['cindex'])
493
+ return self.pgupdt('dscheck', record, cnd, self.LGEREX)