Feb 10, 2013

Paramiko object with Process (RNG must be re-initialized after fork)

When you create an object in Python which will be used later by an object of type Process, that works correctly. Let's see an example where an object of type Process prints an attribute provided by another object belonging to the class A.

from multiprocessing import Process

class A():
    def __init__(self):
        self.my_dict = {"day": "Tuesday"}

class B():
    def __init__(self):
        self.a = A()
        self.__process = None

    def start(self):
        self.__process = Process(target=self.__run_process)
        self.__process.start()
        
    def __run_process(self):
        print self.a.my_dict
        
if __name__ == "__main__":
    b = B()
    b.start()


$ python test.py 
{'day': 'Tuesday'}


But when that process has to handle a Paramiko object where the connection has been initialized before, will not work.

import paramiko
from multiprocessing import Process

class B():
    def __init__(self):
        self.__process = None
        self.__ssh = paramiko.SSHClient()
        self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.__ssh.connect('127.0.0.1', username='javi', password='xxxxxx')
        
    def start(self):
        self.__process = Process(target=self.__run_process)
        self.__process.start()
        
    def __run_process(self):
        try:
            _, stdout, _ = self.__ssh.exec_command("hostname")
        except:
            print "Failed the execution"
        else:
            print stdout.read()
        
if __name__ == "__main__":
    b = B()
    b.start()


$ python test.py 
Failed the execution


If you take a look at the output generated by the execution without catching it through an exception, you could observe an error as follows.

$ python test.py 
Process Process-1:
Traceback (most recent call last):
...
    raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()


This is a well-known issue in Paramiko that has been apparently fixed in recent versions (for my tests, I am using the version provided by Ubuntu 12.10 by default: 1.7.7.1-3). A possible workaround will be for example to open a new SSH connection for that process. Let's grab an example.

import paramiko
from multiprocessing import Process

class B():
    def __init__(self):
        self.__process = None
        self.__ssh = paramiko.SSHClient()
        self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
    def start(self):
        self.__process = Process(target=self.__run_process)
        self.__process.start()
        
    def __run_process(self):
        self.__ssh.connect('127.0.0.1', username='javi', password='xxxxxx')
        try:
            _, stdout, _ = self.__ssh.exec_command("hostname")
        except:
            print "Failed the execution"
        else:
            print stdout.read()
        
if __name__ == "__main__":
    b = B()
    b.start()


$ python test.py 
javi-pc


As you can see above, the definition of the Paramiko object takes place within the constructor of the class B, but the connection is eventually initialized by the forked process.


No comments:

Post a Comment