|
2 | 2 |
|
3 | 3 | importio
|
4 | 4 | importos
|
5 |
| -importsix |
6 | 5 | importsubprocess
|
7 | 6 | importtime
|
8 | 7 |
|
9 |
| -try: |
10 |
| -importpsutil |
11 |
| -exceptImportError: |
12 |
| -psutil=None |
13 |
| - |
14 | 8 | fromshutilimportrmtree
|
15 |
| -fromsiximportraise_from |
| 9 | +fromsiximportraise_from,iteritems |
16 | 10 | fromtempfileimportmkstemp,mkdtemp
|
17 | 11 |
|
18 | 12 | from .enumsimportNodeStatus,ProcessType
|
|
70 | 64 | from .backupimportNodeBackup
|
71 | 65 |
|
72 | 66 |
|
| 67 | +classProcessProxy(object): |
| 68 | +""" |
| 69 | + Wrapper for psutil.Process |
| 70 | +
|
| 71 | + Attributes: |
| 72 | + process: wrapped psutill.Process object |
| 73 | + ptype: instance of ProcessType |
| 74 | + """ |
| 75 | + |
| 76 | +def__init__(self,process): |
| 77 | +self.process=process |
| 78 | +self.ptype=ProcessType.from_process(process) |
| 79 | + |
| 80 | +def__getattr__(self,name): |
| 81 | +returngetattr(self.process,name) |
| 82 | + |
| 83 | +def__str__(self): |
| 84 | +pid=self.process.pid |
| 85 | +cmdline=' '.join(self.process.cmdline()).strip() |
| 86 | +return'{} [{}]'.format(cmdline,pid) |
| 87 | + |
| 88 | + |
73 | 89 | classPostgresNode(object):
|
74 | 90 | def__init__(self,name=None,port=None,base_dir=None):
|
75 | 91 | """
|
@@ -122,11 +138,88 @@ def __exit__(self, type, value, traceback):
|
122 | 138 |
|
123 | 139 | @property
|
124 | 140 | defpid(self):
|
125 |
| -returnself.get_main_pid() |
| 141 | +""" |
| 142 | + Return postmaster's PID if node is running, else 0. |
| 143 | + """ |
| 144 | + |
| 145 | +ifself.status(): |
| 146 | +pid_file=os.path.join(self.data_dir,PG_PID_FILE) |
| 147 | +withio.open(pid_file)asf: |
| 148 | +returnint(f.readline()) |
| 149 | + |
| 150 | +# for clarity |
| 151 | +return0 |
126 | 152 |
|
127 | 153 | @property
|
128 | 154 | defauxiliary_pids(self):
|
129 |
| -returnself.get_auxiliary_pids() |
| 155 | +""" |
| 156 | + Returns a dict of { ProcessType : PID }. |
| 157 | + """ |
| 158 | + |
| 159 | +result= {} |
| 160 | + |
| 161 | +forprocessinself.auxiliary_processes: |
| 162 | +ifprocess.ptypenotinresult: |
| 163 | +result[process.ptype]= [] |
| 164 | + |
| 165 | +result[process.ptype].append(process.pid) |
| 166 | + |
| 167 | +returnresult |
| 168 | + |
| 169 | +@property |
| 170 | +defauxiliary_processes(self): |
| 171 | +""" |
| 172 | + Returns a list of auxiliary processes. |
| 173 | + Each process is represented by ProcessProxy object. |
| 174 | + """ |
| 175 | + |
| 176 | +defis_aux(process): |
| 177 | +returnprocess.ptype!=ProcessType.Unknown |
| 178 | + |
| 179 | +returnlist(filter(is_aux,self.child_processes)) |
| 180 | + |
| 181 | +@property |
| 182 | +defchild_processes(self): |
| 183 | +""" |
| 184 | + Returns a list of all child processes. |
| 185 | + Each process is represented by ProcessProxy object. |
| 186 | + """ |
| 187 | + |
| 188 | +try: |
| 189 | +importpsutil |
| 190 | +exceptImportError: |
| 191 | +raiseTestgresException("psutil module is not installed") |
| 192 | + |
| 193 | +# get a list of postmaster's children |
| 194 | +children=psutil.Process(self.pid).children() |
| 195 | + |
| 196 | +return [ProcessProxy(p)forpinchildren] |
| 197 | + |
| 198 | +@property |
| 199 | +defsource_walsender(self): |
| 200 | +""" |
| 201 | + Returns master's walsender feeding this replica. |
| 202 | + """ |
| 203 | + |
| 204 | +sql=""" |
| 205 | + select pid |
| 206 | + from pg_catalog.pg_stat_replication |
| 207 | + where application_name = $1 |
| 208 | + """ |
| 209 | + |
| 210 | +ifnotself.master: |
| 211 | +raiseTestgresException("Node doesn't have a master") |
| 212 | + |
| 213 | +# master should be on the same host |
| 214 | +assertself.master.host==self.host |
| 215 | + |
| 216 | +withself.master.connect()ascon: |
| 217 | +forrowincon.execute(sql,self.name): |
| 218 | +forchildinself.master.auxiliary_processes: |
| 219 | +ifchild.pid==int(row[0]): |
| 220 | +returnchild |
| 221 | + |
| 222 | +raiseQueryException("Master doesn't send WAL to {}",self.name) |
130 | 223 |
|
131 | 224 | @property
|
132 | 225 | defmaster(self):
|
@@ -427,98 +520,6 @@ def status(self):
|
427 | 520 | elife.exit_code==4:
|
428 | 521 | returnNodeStatus.Uninitialized
|
429 | 522 |
|
430 |
| -defget_main_pid(self): |
431 |
| -""" |
432 |
| - Return postmaster's PID if node is running, else 0. |
433 |
| - """ |
434 |
| - |
435 |
| -ifself.status(): |
436 |
| -pid_file=os.path.join(self.data_dir,PG_PID_FILE) |
437 |
| -withio.open(pid_file)asf: |
438 |
| -returnint(f.readline()) |
439 |
| - |
440 |
| -# for clarity |
441 |
| -return0 |
442 |
| - |
443 |
| -defget_child_processes(self): |
444 |
| -''' Returns child processes for this node ''' |
445 |
| - |
446 |
| -ifpsutilisNone: |
447 |
| -raiseTestgresException("psutil module is not installed") |
448 |
| - |
449 |
| -try: |
450 |
| -postmaster=psutil.Process(self.pid) |
451 |
| -exceptpsutil.NoSuchProcess: |
452 |
| -returnNone |
453 |
| - |
454 |
| -returnpostmaster.children(recursive=True) |
455 |
| - |
456 |
| -defget_auxiliary_pids(self): |
457 |
| -''' Returns dict with pids of auxiliary processes ''' |
458 |
| - |
459 |
| -alternative_names= { |
460 |
| -ProcessType.LogicalReplicationLauncher: [ |
461 |
| -'postgres: bgworker: logical replication launcher' |
462 |
| - ], |
463 |
| -ProcessType.BackgroundWriter: [ |
464 |
| -'postgres: writer', |
465 |
| - ], |
466 |
| -ProcessType.WalWriter: [ |
467 |
| -'postgres: wal writer', |
468 |
| - ], |
469 |
| -ProcessType.WalReceiver: [ |
470 |
| -'postgres: wal receiver', |
471 |
| - ], |
472 |
| - } |
473 |
| - |
474 |
| -children=self.get_child_processes() |
475 |
| -ifchildrenisNone: |
476 |
| -returnNone |
477 |
| - |
478 |
| -result= {} |
479 |
| -forchildinchildren: |
480 |
| -line=' '.join(child.cmdline()) |
481 |
| -forptypeinProcessType: |
482 |
| -ifptype==ProcessType.WalSender \ |
483 |
| -and (line.startswith(ptype.value)or |
484 |
| -line.startswith('postgres: wal sender')): |
485 |
| -result.setdefault(ptype, []) |
486 |
| -result[ptype].append(child.pid) |
487 |
| -break |
488 |
| -elifline.startswith(ptype.value): |
489 |
| -result[ptype]=child.pid |
490 |
| -break |
491 |
| -elifptypeinalternative_names: |
492 |
| -names=alternative_names[ptype] |
493 |
| -fornameinnames: |
494 |
| -ifline.startswith(name): |
495 |
| -result[ptype]=child.pid |
496 |
| -break |
497 |
| - |
498 |
| -returnresult |
499 |
| - |
500 |
| -defget_walsender_pid(self): |
501 |
| -''' Returns pid of according walsender for replica ''' |
502 |
| - |
503 |
| -ifnotself._master: |
504 |
| -raiseTestgresException("This node is not a replica") |
505 |
| - |
506 |
| -children=self._master.get_child_processes() |
507 |
| -ifchildrenisNone: |
508 |
| -returnNone |
509 |
| - |
510 |
| -sql='select application_name, client_port from pg_stat_replication' |
511 |
| -forname,client_portinself._master.execute(sql): |
512 |
| -ifname==self.name: |
513 |
| -forchildinchildren: |
514 |
| -line=' '.join(child.cmdline()) |
515 |
| -if (line.startswith(ProcessType.WalSender.value)or |
516 |
| -line.startswith('postgres: wal sender'))and \ |
517 |
| -str(client_port)inline: |
518 |
| -returnchild.pid |
519 |
| - |
520 |
| -returnNone |
521 |
| - |
522 | 523 | defget_control_data(self):
|
523 | 524 | """
|
524 | 525 | Return contents of pg_control file.
|
@@ -1079,7 +1080,7 @@ def pgbench_run(self,
|
1079 | 1080 | "-U",username,
|
1080 | 1081 | ]+options
|
1081 | 1082 |
|
1082 |
| -forkey,valueinsix.iteritems(kwargs): |
| 1083 | +forkey,valueiniteritems(kwargs): |
1083 | 1084 | # rename keys for pgbench
|
1084 | 1085 | key=key.replace('_','-')
|
1085 | 1086 |
|
|