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.
@@ -0,0 +1,533 @@
1
+ #
2
+ ###############################################################################
3
+ #
4
+ # Title : pg_lock.py
5
+ # Author : Zaihua Ji, zji@ucar.edu
6
+ # Date : 08/118/2020
7
+ # 2025-01-10 transferred to package rda_python_common from
8
+ # https://github.com/NCAR/rda-shared-libraries.git
9
+ # 2025-12-01 convert to class PgLock
10
+ # Purpose : python library module for functions to lock RDADB records
11
+ #
12
+ # Github : https://github.com/NCAR/rda-python-common.git
13
+ #
14
+ ###############################################################################
15
+ #
16
+ import re
17
+ import time
18
+ from .pg_file import PgFile
19
+
20
+ class PgLock(PgFile):
21
+
22
+ def __init__(self):
23
+
24
+ super().__init__() # initialize parent class
25
+ self.DOLOCKS = {-2 : 'Force Unlock', -1 : 'Unlock', 0 : 'Unlock', 1 : 'Relock', 2 : 'Force Relock'}
26
+
27
+ def end_db_transaction(self, idx):
28
+ if idx > 0:
29
+ self.endtran()
30
+ else:
31
+ self.aborttran()
32
+ return idx
33
+
34
+ # check and return running process status: 1-running/uncheckable,0-stopped
35
+ def check_process_running_status(self, host, pid, dolock, lmsg, logact):
36
+ if not self.local_host_action(host, self.DOLOCKS[dolock], lmsg, logact): return 1
37
+ stat = self.check_host_pid(host, pid)
38
+ if stat > 0:
39
+ if logact: self.pglog("{}: Cannot {}".format(lmsg, self.DOLOCKS[dolock]), logact)
40
+ return 1
41
+ if stat < 0 and dolock > -2 and dolock < 2:
42
+ if logact: self.pglog("{}: Fail checking lock info to {}".format(lmsg, self.DOLOCKS[dolock]), logact)
43
+ return 1
44
+ return 0
45
+
46
+ # lock/unlock dscheck record
47
+ # lock if dolock > 0, unlock if <= 0, skip for locked on different host if 0 or 1
48
+ # force unlock if < -1 or force lock if 2
49
+ def lock_dscheck(self, cidx, dolock, logact = 0):
50
+ if not cidx: return 0
51
+ if logact:
52
+ logerr = logact|self.ERRLOG
53
+ logout = logact&(~self.EXITLG)
54
+ else:
55
+ logerr = self.LOGERR
56
+ logout = self.LOGWRN if dolock > 1 or dolock < 0 else 0
57
+ table = "dscheck"
58
+ cnd = "cindex = {}".format(cidx)
59
+ fields = "command, pid, lockhost, lockcmd"
60
+ pgrec = self.pgget(table, fields, cnd, logerr)
61
+ if not pgrec: return 0 # dscheck is gone or db error
62
+ pid = pgrec['pid']
63
+ host = pgrec['lockhost']
64
+ lockcmd = pgrec['lockcmd']
65
+ (chost, cpid) = self.current_process_info()
66
+ clockcmd = self.get_command()
67
+ if pid == 0 and dolock <= 0: return cidx # no need unlock
68
+ lckpid = -pid if pid > 0 and pid == cpid and not self.pgcmp(host, chost, 1) else pid
69
+ if dolock > 0 and lckpid < 0: return cidx # no need lock again
70
+ cinfo = "{}-{}-Chk{}({})".format(self.PGLOG['HOSTNAME'], self.current_datetime(), cidx, pgrec['command'])
71
+ if lckpid > 0 and (clockcmd == "dscheck" or lockcmd != "dscheck"):
72
+ lmsg = "{} Locked by {}/{}/{}".format(cinfo, pid, host, lockcmd)
73
+ if self.check_process_running_status(host, pid, dolock, lmsg, logout): return -cidx
74
+ record = {}
75
+ if dolock > 0:
76
+ if pid != cpid: record['pid'] = cpid
77
+ if host != chost: record['lockhost'] = chost
78
+ if lockcmd != clockcmd: record['lockcmd'] = clockcmd
79
+ else:
80
+ if pid: record['pid'] = 0
81
+ if not record: return cidx
82
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
83
+ if not lkrec: return self.end_db_transaction(0) # dscheck is gone or db error
84
+ if (not lkrec['pid'] or
85
+ lkrec['pid'] == pid and self.pgcmp(lkrec['lockhost'], host, 1) == 0 or
86
+ lkrec['pid'] == cpid and self.pgcmp(lkrec['lockhost'], chost, 1) == 0):
87
+ if not self.pgupdt(table, record, cnd, logerr):
88
+ if logout: self.pglog(cinfo + ": Error update lock", logout)
89
+ cidx = -cidx
90
+ else:
91
+ if logout: self.pglog("{}: Relocked {}/{}".format(cinfo, lkrec['pid'], lkrec['lockhost']), logout)
92
+ cidx = -cidx
93
+ return self.end_db_transaction(cidx)
94
+
95
+ # lock dscheck record for given cidx, pid and host
96
+ def lock_host_dscheck(self, cidx, pid, host, logact = 0):
97
+ if not (cidx and pid): return 0
98
+ if logact:
99
+ logerr = logact|self.ERRLOG
100
+ logout = logact&(~self.EXITLG)
101
+ else:
102
+ logerr = self.LOGERR
103
+ logout = 0
104
+ table = "dscheck"
105
+ cnd = "cindex = {}".format(cidx)
106
+ fields = "command, pid, lockhost, lockcmd"
107
+ pgrec = self.pgget(table, fields, cnd, logerr)
108
+ if not pgrec: return 0 # dscheck is gone or db error
109
+ (chost, cpid) = self.current_process_info()
110
+ cinfo = "{}-{}-Chk{}({})".format(self.PGLOG['HOSTNAME'], self.current_datetime(), cidx, pgrec['command'])
111
+ if pgrec['pid']:
112
+ if pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0:
113
+ return -cidx # locked by the real process already
114
+ elif cpid != pgrec['pid'] or self.pgcmp(pgrec['lockhost'], chost, 1):
115
+ if logout:
116
+ lmsg = "{} Locked by {}/{}/{}".format(cinfo, pid, host, pgrec['lockcmd'])
117
+ self.pglog(lmsg +": Cannot Lock", logout)
118
+ return -cidx # locked by other process
119
+ record = {}
120
+ record['pid'] = pid
121
+ record['lockhost'] = host
122
+ record['lockcmd'] = self.get_command(pgrec['command'])
123
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
124
+ if not lkrec: return self.end_db_transaction(0)
125
+ if (not lkrec['pid'] or
126
+ lkrec['pid'] == pid and self.pgcmp(lkrec['lockhost'], host, 1) == 0 or
127
+ lkrec['pid'] == cpid and self.pgcmp(lkrec['lockhost'], chost, 1) == 0):
128
+ if not self.pgupdt(table, record, cnd, logerr):
129
+ if logout: self.pglog(cinfo + ": Error update lock", logout)
130
+ cidx = -cidx
131
+ else:
132
+ if logout: self.pglog("{}: Relocked {}/{}".format(cinfo, lkrec['pid'], lkrec['lockhost']), logout)
133
+ cidx = -cidx
134
+ return self.end_db_transaction(cidx)
135
+
136
+ # lock/unlock data request record
137
+ # lock if dolock > 0, unlock if <= 0, skip for locked on different host if 0 or 1
138
+ # force unlock if < -1 or 2
139
+ def lock_request(self, ridx, dolock, logact = 0):
140
+ if not ridx: return 0
141
+ if logact:
142
+ logerr = logact|self.ERRLOG
143
+ logout = logact&(~self.EXITLG)
144
+ else:
145
+ logerr = self.LOGERR
146
+ logout = self.LOGWRN if dolock > 1 or dolock < 0 else 0
147
+ table = "dsrqst"
148
+ cnd = "rindex = {}".format(ridx)
149
+ fields = "pid, lockhost"
150
+ pgrec = self.pgget(table, fields, cnd, logerr)
151
+ if not pgrec: return 0 # request is gone or db error
152
+ pid = pgrec['pid']
153
+ host = pgrec['lockhost']
154
+ (chost, cpid) = self.current_process_info()
155
+ if pid == 0 and dolock <= 0: return ridx # no need unlock
156
+ lckpid = -pid if pid > 0 and pid == cpid and not self.pgcmp(host, chost, 1) else pid
157
+ if dolock > 0 and lckpid < 0: return ridx # no need lock again
158
+ rinfo = "{}-{}-Rqst{}".format(self.PGLOG['HOSTNAME'], self.current_datetime(), ridx)
159
+ if lckpid > 0:
160
+ lmsg = "{} Locked by {}/{}".format(rinfo, pid, host)
161
+ if self.check_process_running_status(host, pid, dolock, lmsg, logout): return -ridx
162
+ record = {}
163
+ if dolock > 0:
164
+ if pid != cpid: record['pid'] = cpid
165
+ if host != chost: record['lockhost'] = chost
166
+ if record: record['locktime'] = int(time.time())
167
+ else:
168
+ if pid: record['pid'] = 0
169
+ if host: record['lockhost'] = ""
170
+ if not record: return ridx
171
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
172
+ if not lkrec: return self.end_db_transaction(0) # request is gone or db error
173
+ if (not lkrec['pid'] or
174
+ lkrec['pid'] == pid and self.pgcmp(lkrec['lockhost'], host, 1) == 0 or
175
+ lkrec['pid'] == cpid and self.pgcmp(lkrec['lockhost'], chost, 1) == 0):
176
+ if not self.pgupdt(table, record, cnd, logerr):
177
+ if logout: self.pglog(rinfo + ": Error update lock", logout)
178
+ ridx = -ridx
179
+ else:
180
+ if logout: self.pglog("{}: Relocked {}/{}".format(rinfo, lkrec['pid'], lkrec['lockhost']), logout)
181
+ ridx = -ridx
182
+ return self.end_db_transaction(ridx)
183
+
184
+ # lock dsrqst record for given cidx, pid and host
185
+ def lock_host_request(self, ridx, pid, host, logact = 0):
186
+ if not (ridx and pid): return 0
187
+ if logact:
188
+ logerr = logact|self.ERRLOG
189
+ logout = logact&(~self.EXITLG)
190
+ else:
191
+ logerr = self.LOGERR
192
+ logout = 0
193
+ table = "dsrqst"
194
+ cnd = "rindex = {}".format(ridx)
195
+ fields = "pid, lockhost"
196
+ pgrec = self.pgget(table, fields, cnd, logerr)
197
+ if not pgrec: return 0 # dscheck is gone or db error
198
+ rinfo = "{}-{}-Rqst{}".format(self.PGLOG['HOSTNAME'], self.current_datetime(), ridx)
199
+ if pgrec['pid']:
200
+ if pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0: return ridx
201
+ if logout:
202
+ lmsg = "{} Locked by {}/{}".format(rinfo, pid, host)
203
+ self.pglog(lmsg +": Cannot Lock", logout)
204
+ return -ridx
205
+ record = {}
206
+ record['pid'] = pid
207
+ record['lockhost'] = host
208
+ record['locktime'] = int(time.time())
209
+ pgrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
210
+ if not pgrec: return self.end_db_transaction(0)
211
+ if not pgrec['pid'] or pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0:
212
+ if not self.pgupdt(table, record, cnd, logerr):
213
+ if logout: self.pglog(rinfo + ": Error update lock", logout)
214
+ ridx = -ridx
215
+ else:
216
+ if logout: self.pglog("{}: Relocked {}/{}".format(rinfo, pgrec['pid'], pgrec['lockhost']), logout)
217
+ ridx = -ridx
218
+ return self.end_db_transaction(ridx)
219
+
220
+ # lock/unlock dataset update record
221
+ # lock if dolock > 0, unlock if <= 0, skip for locked on different host if 0 or 1
222
+ # force unlock if < -1 or 2
223
+ def lock_update(self, lidx, linfo, dolock, logact = 0):
224
+ if not lidx: return 0
225
+ if logact:
226
+ logerr = logact|self.ERRLOG
227
+ logout = logact&(~self.EXITLG)
228
+ else:
229
+ logerr = self.LOGERR
230
+ logout = self.LOGWRN if dolock > 1 or dolock < 0 else 0
231
+ table = "dlupdt"
232
+ cnd = "lindex = {}".format(lidx)
233
+ fields = "pid, hostname"
234
+ pgrec = self.pgget(table, fields, cnd, logerr)
235
+ if not pgrec: return 0 # update record is deleted
236
+ pid = pgrec['pid']
237
+ host = pgrec['hostname']
238
+ (chost, cpid) = self.current_process_info()
239
+ if pid == 0 and dolock <= 0: return lidx # no need unlock
240
+ lckpid = -pid if pid > 0 and pid == cpid and not self.pgcmp(host, chost, 1) else pid
241
+ if dolock > 0 and lckpid < 0: return lidx # no need lock again
242
+ if not linfo: linfo = "{}-{}-Updt{}".format(self.PGLOG['HOSTNAME'], self.current_datetime(), lidx)
243
+ if lckpid > 0:
244
+ lmsg = "{} Locked by {}/{}".format(linfo, pid, host)
245
+ if self.check_process_running_status(host, pid, dolock, lmsg, logout): return -lidx
246
+ record = {}
247
+ if dolock > 0:
248
+ if pid != cpid: record['pid'] = cpid
249
+ if host != chost: record['hostname'] = chost
250
+ if record: record['locktime'] = int(time.time())
251
+ else:
252
+ if pid: record['pid'] = 0
253
+ if host: record['hostname'] = ''
254
+ if not record: return lidx
255
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
256
+ if not lkrec: return self.end_db_transaction(0) # update record is deleted
257
+ if not lkrec['pid'] or lkrec['pid'] == pid and self.pgcmp(lkrec['hostname'], host, 1) == 0:
258
+ if not self.pgupdt(table, record, cnd, logerr):
259
+ if logout: self.pglog(linfo + ": Error update lock", logout)
260
+ lidx = -lidx
261
+ else:
262
+ if logout: self.pglog("{}: Relocked {}/{}".format(linfo, lkrec['pid'], lkrec['hostname']), logout)
263
+ lidx = -lidx
264
+ return self.end_db_transaction(lidx)
265
+
266
+ # lock/unlock dataset update control record
267
+ # lock if dolock > 0, unlock if <= 0, skip for locked on different host if 0 or 1,
268
+ # unlock dead process if < -1 or 2, force unlock if -2
269
+ def lock_update_control(self, cidx, dolock, logact = 0):
270
+ if not cidx: return 0
271
+ if logact:
272
+ logerr = logact|self.ERRLOG
273
+ logout = logact&(~self.EXITLG)
274
+ else:
275
+ logerr = self.LOGERR
276
+ logout = self.LOGWRN if dolock > 1 or dolock < 0 else 0
277
+ table = "dcupdt"
278
+ cnd = "cindex = {}".format(cidx)
279
+ fields = "pid, lockhost"
280
+ pgrec = self.pgget(table, fields, cnd, logerr)
281
+ if not pgrec: return 0 # update control record is deleted
282
+ pid = pgrec['pid']
283
+ host = pgrec['lockhost']
284
+ (chost, cpid) = self.current_process_info()
285
+ if pid == 0 and dolock <= 0: return cidx # no need unlock
286
+ lckpid = -pid if pid > 0 and pid == cpid and not self.pgcmp(host, chost, 1) else pid
287
+ if dolock > 0 and lckpid < 0: return cidx # no need lock again
288
+ cinfo = "{}-{}-UC{}".format(self.PGLOG['HOSTNAME'], self.current_datetime(), cidx)
289
+ if lckpid > 0:
290
+ lmsg = "{} Locked by {}/{}".format(cinfo, pid, host)
291
+ if self.check_process_running_status(host, pid, dolock, lmsg, logout): return -cidx
292
+ record = {}
293
+ if dolock > 0:
294
+ if pid != cpid: record['pid'] = cpid
295
+ if host != chost: record['lockhost'] = chost
296
+ if record: record['chktime'] = int(time.time())
297
+ else:
298
+ if pid: record['pid'] = 0
299
+ if host: record['lockhost'] = ''
300
+ if not record: return cidx
301
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
302
+ if not lkrec: return self.end_db_transaction(0) # update control record is deleted
303
+ if (not lkrec['pid'] or
304
+ lkrec['pid'] == pid and self.pgcmp(lkrec['lockhost'], host, 1) == 0 or
305
+ lkrec['pid'] == cpid and self.pgcmp(lkrec['lockhost'], chost, 1) == 0):
306
+ if not self.pgupdt(table, record, cnd, logerr):
307
+ if logout: self.pglog(cinfo + ": Error update lock", logout)
308
+ cidx = -cidx
309
+ else:
310
+ if logout: self.pglog("{}: Relocked {}/{}".format(cinfo, lkrec['pid'], lkrec['lockhost']), logout)
311
+ cidx = -cidx
312
+ return self.end_db_transaction(cidx)
313
+
314
+ # lock dscheck record for given cidx, pid and host
315
+ def lock_host_update_control(self, cidx, pid, host, logact = 0):
316
+ if not (cidx and pid): return 0
317
+ if logact:
318
+ logerr = logact|self.ERRLOG
319
+ logout = logact&(~self.EXITLG)
320
+ else:
321
+ logerr = self.LOGERR
322
+ logout = 0
323
+ table = "dcupdt"
324
+ cnd = "cindex = {}".format(cidx)
325
+ fields = "pid, lockhost"
326
+ pgrec = self.pgget(table, fields, cnd, logerr)
327
+ if not pgrec: return 0 # dscheck is gone or db error
328
+ cinfo = "{}-{}-UC{}".format(self.PGLOG['HOSTNAME'], self.current_datetime(), cidx)
329
+ if pgrec['pid']:
330
+ if pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0: return cidx
331
+ if logout:
332
+ lmsg = "{} Locked by {}/{}".format(cinfo, pid, host)
333
+ self.pglog(lmsg +": Cannot Lock", logout)
334
+ return -cidx
335
+ record = {}
336
+ record['pid'] = pid
337
+ record['lockhost'] = host
338
+ record['chktime'] = int(time.time())
339
+ pgrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
340
+ if not pgrec: return self.end_db_transaction(0)
341
+ if not pgrec['pid'] or pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0:
342
+ if not self.pgupdt(table, record, cnd, logerr):
343
+ if logout: self.pglog(cinfo + ": Error update lock", logout)
344
+ cidx = -cidx
345
+ else:
346
+ if logout: self.pglog("{}: Relocked {}/{}".format(cinfo, pgrec['pid'], pgrec['lockhost']), logout)
347
+ cidx = -cidx
348
+ return self.end_db_transaction(cidx)
349
+
350
+ # return lock information of a locked process
351
+ @staticmethod
352
+ def lock_process_info(pid, lockhost, runhost = None, pcnt = 0):
353
+ retstr = " {}<{}".format(lockhost, pid)
354
+ if pcnt: retstr += "/{}".format(pcnt)
355
+ retstr += ">"
356
+ if runhost and runhost != lockhost: retstr += '/' + runhost
357
+ return retstr
358
+
359
+ # lock/unlock data request partition record
360
+ # lock if dolock > 0, unlock if <= 0, skip for locked on different host if 0 or 1
361
+ # force unlock if < -1 or 2
362
+ def lock_partition(self, pidx, dolock, logact = 0):
363
+ if not pidx: return 0
364
+ if logact:
365
+ logerr = logact|self.ERRLOG
366
+ logout = logact&(~self.EXITLG)
367
+ else:
368
+ logerr = self.LOGERR
369
+ logout = self.LOGWRN if dolock > 1 or dolock < 0 else 0
370
+ table = "ptrqst"
371
+ cnd = "pindex = {}".format(pidx)
372
+ fields = "pid, lockhost"
373
+ pgrec = self.pgget(table, "rindex, ptorder, " + fields, cnd, logerr)
374
+ if not pgrec: return 0 # request is gone or db error
375
+ ridx = pgrec['rindex']
376
+ pid = pgrec['pid']
377
+ host = pgrec['lockhost']
378
+ (chost, cpid) = self.current_process_info()
379
+ if pid == 0 and dolock <= 0: return pidx # no need unlock
380
+ lckpid = -pid if pid > 0 and pid == cpid and not self.pgcmp(host, chost, 1) else pid
381
+ if dolock > 0 and lckpid < 0: return pidx # no need lock again
382
+ pinfo = "{}-{}-RPT{}(Rqst{}/PTO{})".format(self.PGLOG['HOSTNAME'], self.current_datetime(), pidx, ridx, pgrec['ptorder'])
383
+ if lckpid > 0:
384
+ lmsg = "{} Locked by {}/{}".format(pinfo, pid, host)
385
+ if self.check_process_running_status(host, pid, dolock, lmsg, logout): return -pidx
386
+ record = {}
387
+ if dolock > 0:
388
+ if pid != cpid: record['pid'] = cpid
389
+ if host != chost: record['lockhost'] = chost
390
+ if record: record['locktime'] = int(time.time())
391
+ else:
392
+ if pid: record['pid'] = 0
393
+ if host: record['lockhost'] = ""
394
+ if not record: return pidx
395
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
396
+ if not lkrec: return self.end_db_transaction(0) # request partition is gone or db error
397
+ if (not lkrec['pid'] or
398
+ lkrec['pid'] == pid and self.pgcmp(lkrec['lockhost'], host, 1) == 0 or
399
+ lkrec['pid'] == cpid and self.pgcmp(lkrec['lockhost'], chost, 1) == 0):
400
+ lmsg = self.update_partition_lock(ridx, record, logout)
401
+ if lmsg:
402
+ if logout: self.pglog("{}: {}".format(pinfo, lmsg), logout)
403
+ pidx = -pidx
404
+ elif not self.pgupdt(table, record, cnd, logerr):
405
+ if logout: self.pglog(pinfo + ": error update lock", logout)
406
+ pidx = -pidx
407
+ else:
408
+ self.pglog("{}: Relocked {}/{}".format(pinfo, lkrec['pid'], lkrec['lockhost']), logout)
409
+ pidx = -pidx
410
+ return self.end_db_transaction(pidx)
411
+
412
+ # lock dsrqst partition record for given cidx, pid and host
413
+ def lock_host_partition(self, pidx, pid, host, logact = 0):
414
+ if not (pidx and pid): return 0
415
+ if logact:
416
+ logerr = logact|self.ERRLOG
417
+ logout = logact&(~self.EXITLG)
418
+ else:
419
+ logerr = self.LOGERR
420
+ logout = 0
421
+ table = "ptrqst"
422
+ cnd = "pindex = {}".format(pidx)
423
+ fields = "pid, lockhost"
424
+ pgrec = self.pgget(table, "rindex, ptorder, " + fields, cnd, logerr)
425
+ if not pgrec: return 0 # dscheck is gone or db error
426
+ ridx = pgrec['rindex']
427
+ pinfo = "{}-{}-RPT{}(Rqst{}/PTO{})".format(self.PGLOG['HOSTNAME'], self.current_datetime(), pidx, ridx, pgrec['ptorder'])
428
+ if pgrec['pid']:
429
+ if pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0: return pidx
430
+ if logout:
431
+ lmsg = "{} Locked by {}/{}".format(pinfo, pid, host)
432
+ self.pglog(lmsg +": Cannot Lock", logout)
433
+ return -pidx
434
+ record = {}
435
+ record['pid'] = pid
436
+ record['lockhost'] = host
437
+ record['locktime'] = int(time.time())
438
+ pgrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
439
+ if not pgrec: return self.end_db_transaction(0)
440
+ if not pgrec['pid'] or pid == pgrec['pid'] and self.pgcmp(pgrec['lockhost'], host, 1) == 0:
441
+ lmsg = self.update_partition_lock(ridx, record, logout)
442
+ if lmsg:
443
+ if logout: self.pglog("{}: {}".format(pinfo, lmsg), logout)
444
+ pidx = -pidx
445
+ elif not self.pgupdt(table, record, cnd, logerr):
446
+ if logout: self.pglog(pinfo + ": error update lock", logout)
447
+ pidx = -pidx
448
+ else:
449
+ if logout: self.pglog("{}: Relocked {}/{}".format(pinfo, pgrec['pid'], pgrec['lockhost']), logout)
450
+ pidx = -pidx
451
+ return self.end_db_transaction(pidx)
452
+
453
+ # update dsrqst lock info for given partition lock status
454
+ # Return None if all is fine; error message otherwise
455
+ def update_partition_lock(self, ridx, ptrec, logact = 0):
456
+ if not ridx: return 0
457
+ if logact:
458
+ logerr = logact|self.ERRLOG
459
+ logout = logact&(~self.EXITLG)
460
+ else:
461
+ logerr = self.LOGERR
462
+ logout = self.LOGWRN
463
+ table = "dsrqst"
464
+ lockhost = "partition"
465
+ cnd = "rindex = {}".format(ridx)
466
+ pgrec = self.pgget(table, "pid, lockhost", cnd, logact|self.DOLOCK)
467
+ if not pgrec: return "Error get Rqst{} record".format(ridx) # should not happen
468
+ if pgrec['pid'] > 0 and pgrec['lockhost'] != lockhost:
469
+ return "Rqst{} locked by non-lockhost process ({}/{})".format(ridx, pgrec['pid'], pgrec['lockhost'])
470
+ record = {}
471
+ if ptrec['pid'] > 0:
472
+ record['pid'] = pgrec['pid'] + 1
473
+ record['lockhost'] = lockhost
474
+ record['locktime'] = ptrec['locktime']
475
+ else:
476
+ if pgrec['pid'] > 1:
477
+ pcnt = self.pgget('ptrqst', '', cnd + " AND pid > 0")
478
+ if pgrec['pid'] > pcnt: pgrec['pid'] = pcnt
479
+ record['pid'] = pgrec['pid'] - 1
480
+ record['lockhost'] = lockhost
481
+ else:
482
+ record['pid'] = 0
483
+ record['lockhost'] = ''
484
+ if not self.pgupdt(table, record, cnd, logact):
485
+ return "Error update Rqst{} lock".format(ridx)
486
+ return None
487
+
488
+ # lock/unlock dataset record for Quasar Backup
489
+ # lock if dolock > 0, unlock if <= 0, skip for locked on different host if 0 or 1,
490
+ # unlock dead process if < -1 or 2, force unlock if -2
491
+ def lock_dataset(self, dsid, dolock, logact = 0):
492
+ if not dsid: return 0
493
+ if logact:
494
+ logerr = logact|self.ERRLOG
495
+ logout = logact&(~self.EXITLG)
496
+ else:
497
+ logerr = self.LOGERR
498
+ logout = self.LOGWRN if dolock > 1 or dolock < 0 else 0
499
+ table = "dataset"
500
+ cnd = "dsid = '{}'".format(dsid)
501
+ fields = "pid, lockhost"
502
+ pgrec = self.pgget(table, fields, cnd, logerr)
503
+ if not pgrec: return 0 # dataset not exists
504
+ pid = pgrec['pid']
505
+ host = pgrec['lockhost']
506
+ (chost, cpid) = self.current_process_info()
507
+ if pid == 0 and dolock <= 0: return 1 # no need unlock
508
+ lckpid = -pid if pid > 0 and pid == cpid and not self.pgcmp(host, chost, 1) else pid
509
+ if dolock > 0 and lckpid < 0: return 1 # no need lock again
510
+ dinfo = "{}-{}-{}".format(self.PGLOG['HOSTNAME'], self.current_datetime(), dsid)
511
+ if lckpid > 0:
512
+ lmsg = "{} Locked by {}/{}".format(dinfo, pid, host)
513
+ if self.check_process_running_status(host, pid, dolock, lmsg, logout): return -1
514
+ record = {}
515
+ if dolock > 0:
516
+ if pid != cpid: record['pid'] = cpid
517
+ if host != chost: record['lockhost'] = chost
518
+ else:
519
+ if pid: record['pid'] = 0
520
+ if not record: return 1
521
+ lkrec = self.pgget(table, fields, cnd, logerr|self.DOLOCK)
522
+ if not lkrec: return self.end_db_transaction(0) # dscheck is gone or db error
523
+ lstat = 1
524
+ if (not lkrec['pid'] or
525
+ lkrec['pid'] == pid and self.pgcmp(lkrec['lockhost'], host, 1) == 0 or
526
+ lkrec['pid'] == cpid and self.pgcmp(lkrec['lockhost'], chost, 1) == 0):
527
+ if not self.pgupdt(table, record, cnd, logerr):
528
+ if logout: self.pglog(dinfo + ": Error update lock", logout)
529
+ lstat = -1
530
+ else:
531
+ if logout: self.pglog("{}: Relocked {}/{}".format(dinfo, lkrec['pid'], lkrec['lockhost']), logout)
532
+ lstat = -1
533
+ return self.end_db_transaction(lstat)