|
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 |
|
|