55import six
66import subprocess
77import time
8+ import warnings
9+
10+ try :
11+ import psutil
12+ except ImportError :
13+ psutil = None
814
915from shutil import rmtree
1016from six import raise_from
4854 ExecUtilException , \
4955 QueryException , \
5056 StartNodeException , \
51- TimeoutException
57+ TimeoutException , \
58+ TestgresException
5259
5360from .logger import TestgresLogger
5461
@@ -116,7 +123,11 @@ def __exit__(self, type, value, traceback):
116123
117124 @property
118125 def pid (self ):
119- return self .get_pid ()
126+ return self .get_main_pid ()
127+
128+ @property
129+ def auxiliary_pids (self ):
130+ return self .get_auxiliary_pids ()
120131
121132 @property
122133 def master (self ):
@@ -417,7 +428,7 @@ def status(self):
417428 elif e .exit_code == 4 :
418429 return NodeStatus .Uninitialized
419430
420- def get_pid (self ):
431+ def get_main_pid (self ):
421432 """
422433 Return postmaster's PID if node is running, else 0.
423434 """
@@ -428,7 +439,73 @@ def get_pid(self):
428439 return int (f .readline ())
429440
430441 # for clarity
431- return 0
442+ return None
443+
444+ def get_child_processes (self ):
445+ ''' Returns child processes for this node '''
446+
447+ if psutil is None :
448+ warnings .warn ("psutil module is not installed" )
449+ return None
450+
451+ try :
452+ postmaster = psutil .Process (self .pid )
453+ except psutil .NoSuchProcess :
454+ return None
455+
456+ return postmaster .children (recursive = True )
457+
458+ def get_auxiliary_pids (self ):
459+ ''' Returns dict with pids of auxiliary processes '''
460+
461+ children = self .get_child_processes ()
462+ if children is None :
463+ return None
464+
465+ result = {}
466+ for child in children :
467+ line = child .cmdline ()[0 ]
468+ if line .startswith ('postgres: checkpointer' ):
469+ result ['checkpointer' ] = child .pid
470+ elif line .startswith ('postgres: background writer' ):
471+ result ['bgwriter' ] = child .pid
472+ elif line .startswith ('postgres: walwriter' ):
473+ result ['walwriter' ] = child .pid
474+ elif line .startswith ('postgres: autovacuum launcher' ):
475+ result ['autovacuum_launcher' ] = child .pid
476+ elif line .startswith ('postgres: stats collector' ):
477+ result ['stats' ] = child .pid
478+ elif line .startswith ('postgres: logical replication launcher' ):
479+ result ['logical_replication_launcher' ] = child .pid
480+ elif line .startswith ('postgres: walreceiver' ):
481+ result ['walreceiver' ] = child .pid
482+ elif line .startswith ('postgres: walsender' ):
483+ result .setdefault ('walsenders' , [])
484+ result ['walsenders' ].append (child .pid )
485+ elif line .startswith ('postgres: startup' ):
486+ result ['startup' ] = child .pid
487+
488+ return result
489+
490+ def get_walsender_pid (self ):
491+ ''' Returns pid of according walsender for replica '''
492+
493+ if not self ._master :
494+ raise TestgresException ("This node is not a replica" )
495+
496+ children = self ._master .get_child_processes ()
497+ if children is None :
498+ return None
499+
500+ sql = 'select application_name, client_port from pg_stat_replication'
501+ for name , client_port in self ._master .execute (sql ):
502+ if name == self .name :
503+ for child in children :
504+ line = child .cmdline ()[0 ]
505+ if line .startswith ('postgres: walsender' ) and str (client_port ) in line :
506+ return child .pid
507+
508+ return None
432509
433510 def get_control_data (self ):
434511 """
0 commit comments