0

I would like timeout the function sftp.put(), I have tried with signal Module but the script doesn't die if the upload time is over 10s. I use that to transfer files by ssh (paramiko).

[...]
def handler(signum, frame):
        print 'Signal handler called with signal', signum
        raise IOError("Couldn't upload the fileeeeeeeeeeee!!!!")

[...]
raspi = paramiko.SSHClient()
raspi.set_missing_host_key_policy(paramiko.AutoAddPolicy())
raspi.connect(ip , username= "", password= "" , timeout=10)
sftp = raspi.open_sftp()        

[...]   
signal.signal(signal.SIGALRM, handler)
signal.alarm(10)

sftp.put(source, destination , callback=None, confirm=True)  
    
signal.alarm(0)
raspi.close()

[...]

Update 1:

I want to abort the transfer if the server stops responding for a while. Actually, my python script check (in loop) any files in a folder, and send it to this remote server. But in the problem here I want to leave this function in the case of the server become inaccessible suddenly during a transfer (ip server changing, no internet anymore,...). But when I simulate a disconnection, the script stays stuck at this function sftp.put anyway...)

Update 2:

When the server goes offline during a transfer, put() seems to be blocked forever. This happens with this line too:

sftp.get_channel().settimeout(xx)

How to do when we lose the Channel?

Update 3 & script goal

Ubuntu 18.04 and paramiko version 2.6.0

Hello, To follow your remarks and questions, I have to give more details about my very Ugly script, sorry about that :)

Actually, I don’t want to have to kill a thread manually and open a new one. For my application I want that the script run totally in autonomous, and if something wrong during the process, it can still go on. For that I use the Python exception handling. Everything does what I want except when the remote server going off during a transfer: The script stays blocked in the put() function, I think inside a loop. Below, the script contains in total 3 functions to timeout this thanks to your help, but apparently nothing can leave this damned sftp.put()! Do you have some new idea ?

Import […]
[...]

def handler(signum, frame):
        print 'Signal handler called with signal', signum
        raise IOError("Couldn't upload the fileeeeeeeeeeee!!!!")

def check_time(size, file_size):
    global start_time
        if (time.time() - start_time) > 10:
            raise Exception

i = 0
while i == 0:

    try:

        
        time.sleep(1) # CPU break
        print ("go!")

        #collect ip server
        fichierIplist = open("/home/robert/Documents/iplist.txt", "r")
        file_lines = fichierIplist.readlines()
        fichierIplist.close()
        last_line = file_lines [len (file_lines)-1]
        lastKnowip = last_line

        data = glob.glob("/home/robert/Documents/data/*")   
        items = len(data) 

        if items != 0: 
        time.sleep(60) #anyway
            print("some Files!:)")
            raspi = paramiko.SSHClient()
            raspi.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            raspi.connect(lastKnowip, username= "", password= "" , timeout=10)

            for source in data: #Upload file by file    
                filename = os.path.basename(source) #
                destination = '/home/pi/Documents/pest/'+ filename #p

                sftp = raspi.open_sftp()

                signal.signal(signal.SIGALRM, handler)
                signal.alarm(10)

                sftp.get_channel().settimeout(10)
                start_time = time.time()

                sftp.put(source, destination, callback=check_time)

                sftp.close()    
                signal.alarm(0) 
            raspi.close()

        else:

            print("noFile!")

    except:
        pass
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
pierre
  • 11
  • 3

3 Answers3

1

If you want to timeout, when the server stops responding:

  • set the timeout argument of SSHClient.connect (your doing that already),
  • and set sftp.get_channel().settimeout as already suggested by @EOhm

If you want to timeout even when the server is responding, but slowly, implement the callback argument to abort the transfer after certain time:

start_time = time.time()

def check_time(size, file_size):
    global start_time
    if (time.time() - start_time) > ...:
        raise Exception

sftp.put(source, destination, callback=check_time)  

This won't cancel the transfer immediately. To optimize transfer performance, Paramiko queues the write requests to the server. Once you attempt to cancel the transfer, Paramiko has to wait for the responses to those requests in SFTPFile.close() to clear the queue. You might solve that by using SFTPClient.putfo() and avoiding calling the SFTPFile.close() when the transfer is cancelled. But you won't be able to use the connection afterwards. Of course, you can also not use the optimization, then you can cancel the transfer without delays. But that kind of defies the point of all this, doesn't it?


Alternatively, you can run the transfer in a separate thread and kill the thread if it takes too long. Ugly but sure solution.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
0

Use sftp.get_channel().settimeout(s) for that instead.

EOhm
  • 626
  • 4
  • 11
  • Thanks. But If I add this line, the script doesn't stop when a server is not responding for 5 second for example. Why? – pierre Nov 13 '19 at 22:31
  • Do You run the script on Windows? I think it is not working there for some reasons.. Also signals etc. are very different on Windows so that would probabyl be any way very relevant. – EOhm Nov 14 '19 at 05:48
  • Hello, I use Ubuntu 18.04. – pierre Nov 14 '19 at 14:55
0

After trying a lot of things and with your help and advice, I have found a reliable solution for what I wanted. I execute sftp.put in a separate Thread and my script do what I want. Many thanks for your help

Now if the server shuts down during a transfer, after 60 sec, my script goes on using:

[...]
import threading
[...]
th = threading.Thread(target=sftp.put, args=(source,destination))
th.start()                  
h.join(60)
[...]
pierre
  • 11
  • 3
  • 1
    Ok this is of course a workaround, but the SFTP session and thread will keep running till timeout. As Python sadly has no means to kill a thread besides shutdown program and having threads daemonic and apparently SFTP.put cannot be interrupted by signal this is this way Your only choice. Sadly I did not find time to investigate further on my Ubuntu server about the channel timeout method. I'm quite certain there is a better and real way or at least a bug that should be fixed. – EOhm Nov 18 '19 at 07:31