2012-11-09 30 views
5

शीर्षक के हिस्से पर छूने वाले बहुत सारे विषय हैं, लेकिन कुछ भी नहीं जो पूरी चीज को पूरा करता है। मैं रिमोट सर्वर पर कमांड दबा रहा हूं और लंबे निष्पादन समय के बाद पूर्ण आउटपुट की आवश्यकता है, 5 मिनट या उससे भी ज्यादा कहें। चैनल का उपयोग करके मैं एक टाइमआउट सेट करने में सक्षम था, लेकिन जब मैंने स्टडआउट वापस पढ़ा तो मुझे आउटपुट का केवल एक छोटा सा हिस्सा मिला। समाधान channel.exit_status_ready() के लिए प्रतीक्षा करना प्रतीत होता था। यह एक सफल कॉल पर काम करता था, लेकिन एक असफल कॉल चैनल टाइमआउट को कभी भी ट्रिगर नहीं करेगा। दस्तावेज़ों की समीक्षा करने के बाद, मैं सिद्धांतित करता हूं क्योंकि टाइमआउट केवल पढ़ने के लिए काम करता है, और बाहर निकलने की स्थिति की प्रतीक्षा करने योग्य नहीं है। यहां वह प्रयास है:लंबे निष्पादन के साथ पायथन पैरामीको टाइमआउट, पूर्ण आउटपुट

channel = ssh.get_transport().open_session() 
channel.settimeout(timeout) 
channel.exec_command(cmd) # return on this is not reliable 
while True: 
    try: 
     if channel.exit_status_ready(): 
      if channel.recv_ready(): # so use recv instead... 
       output = channel.recv(1048576) 
       break 
     if channel.recv_stderr_ready(): # then check error 
      error = channel.recv_stderr(1048576) 
      break 
    except socket.timeout: 
     print("SSH channel timeout exceeded.") 
     break 
    except Exception: 
     traceback.print_exc() 
     break 

सुंदर, है ना? इच्छा है कि यह काम किया।

समाधान पर मेरा पहला प्रयास टाइमटाइम() शुरू करने के लिए था, फिर स्टार्ट-टाइम.टाइम()> टाइमआउट देखें। यह सीधा लगता है, लेकिन मेरे वर्तमान संस्करण में, मैं एक निश्चित टाइमआउट के साथ आउटपुट - टाइम.टाइम() शुरू करता हूं जो ब्रेक को ट्रिगर करना चाहिए ... और अंतर को देखें जो बिना किसी ब्रेक के टाइमआउट को दोगुना और तीन गुना करें। अंतरिक्ष को बचाने के लिए, मैं अपने तीसरे प्रयास का उल्लेख करूंगा, जिसे मैंने इस के साथ बढ़ाया है। मैंने यहां आउटपुट का इंतजार करने के लिए select.select का उपयोग करने के बारे में पढ़ा है, और दस्तावेज में नोट किया है कि वहां एक टाइमआउट भी है। जैसा कि आप नीचे दिए गए कोड से देखेंगे, मैंने सभी तीन तरीकों को मिश्रित किया है - चैनल टाइमआउट, टाइम.टाइम टाइमआउट, और टाइमआउट का चयन करें - फिर भी प्रक्रिया को मारना है।

channel = ssh.get_transport().open_session() 
channel.settimeout(timeout) 
channel.exec_command(cmd) # return on this is not reliable 
print("{0}".format(cmd)) 
start = time.time() 
while True: 
    try: 
     rlist, wlist, elist = select([channel], [], [], 
      float(timeout)) 
     print("{0}, {1}, {2}".format(rlist, wlist, elist)) 
     if rlist is not None and len(rlist) > 0: 
      if channel.exit_status_ready(): 
       if channel.recv_ready(): # so use recv instead... 
        output = channel.recv(1048576) 
        break 
     elif elist is not None and len(elist) > 0: 
      if channel.recv_stderr_ready(): # then check error 
       error = channel.recv_stderr(1048576) 
       break 
     print("{0} - {1} = {2}".format(
      time.time(), start, time.time() - start)) 
     if time.time() - start > timeout: 
      break 
    except socket.timeout: 
     print("SSH channel timeout exceeded.") 
     break 
    except Exception: 
     traceback.print_exc() 
     break 

यहां कुछ सामान्य उत्पादन है: - शुरू = (

[<paramiko.Channel 3 (open) window=515488 -> <paramiko.Transport at 0x888414cL (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>], [], [] 
1352494558.42 - 1352494554.69 = 3.73274183273 

शीर्ष पंक्ति [rlist, wlist, elist] चयन से, लब्बोलुआब यह time.time() है यहाँ frankencode है time.time() - शुरू)। मुझे इस दौड़ को पुनरावृत्ति की गिनती करके और 1000 बार लूप करने के बाद प्रयास के नीचे तोड़ने के लिए मिला। नमूना चलाने पर टाइमआउट 3 पर सेट किया गया था। जो साबित करता है कि हम कोशिश करते हैं, लेकिन जाहिर है, तीन तरीकों में से कोई भी काम नहीं करना चाहिए।

अगर मैंने मूल रूप से कुछ गलत समझा है तो कोड में चिपकने के लिए स्वतंत्र महसूस करें। मैं इसके लिए उबेर-पायथनिक होना चाहता हूं और अभी भी सीख रहा हूं।

उत्तर

2

मुझे एक ही तरह का मुद्दा है। मुझे लगता है कि हम इसे सिग्नलिंग के साथ संभाल सकते हैं। http://docs.python.org/2/library/signal.html

यह दिखाने के लिए यहां एक सादा गूंगा उदाहरण है कि यह कैसे काम करता है।

import signal, time       

def handler(signum, frame):     
    pass          

# Set the signal handler and a 2-second alarm 
signal.signal(signal.SIGALRM, handler)  
signal.alarm(2)        

# This is where your operation that might hang goes 
time.sleep(10)        

# Disable the alarm       
signal.alarm(0)        

तो यहां, अलार्म 2 सेकंड पर सेट है। Time.sleep को 10 सेकंड के साथ बुलाया जाता है। बेशक, नींद खत्म होने से पहले अलार्म ट्रिगर हो जाएगा। यदि आप time.sleep के बाद कुछ आउटपुट डालते हैं, तो आप देखेंगे कि प्रोग्राम निष्पादन वहां फिर से शुरू होता है।

यदि आप नियंत्रण कहीं और जारी रखना चाहते हैं, तो अपने हैंडलिंग कॉल को एक कोशिश/छोड़कर लपेटें और अपने हैंडलर फ़ंक्शन को अपवाद बढ़ाएं।

हालांकि मुझे पूरा यकीन है कि यह काम करेगा, मैंने अभी तक paramiko कॉल पर इसका परीक्षण नहीं किया है।

+0

मेरा शोध एक ही दिशा में भाग गया, लेकिन मुझे "ValueError: सिग्नल केवल मुख्य धागे में काम करता है", हालांकि मैं जानबूझ कर अपने कोड में धागे का उपयोग नहीं कर रहा हूं। या तो कुछ मॉड्यूल प्रक्रिया को फोर्क कर रहा है या यह एक बग है। विचार? – user1772459

+0

हाँ मुझे एहसास हुआ कि बहुत पाइथन केवल मुख्य धागे में सिग्नल का समर्थन करता है। अगर आपको वह संदेश मिलता है, तो मुझे लगता है कि कुछ बिंदु पर धागे के कुछ स्पंज होते हैं। –

2

यहां कुछ ऐसा है जो मदद कर सकता है, हालांकि मैं अभी भी परीक्षण के बीच में हूं।

chan = ssh.get_transport().open_session() 

cmd = "timeout {0} {1}\n".format(timeouttime, cmd) 

chan.exec_command(cmd) 

सर्वर बार: एक कैच-ऑल अजगर के लिए समय समाप्ति सहित विभिन्न प्रकार के समय समाप्ति के साथ संघर्ष कर, और साकार असली समस्या यह है कि सर्वर प्रक्रिया समाप्त करने के लिए भरोसा नहीं किया जा सकता है कि बाद, मैं ऐसा किया timeouttime के बाद बाहर cmd जितनी जल्दी चाहें उतनी जल्दी बाहर नहीं निकलता है, और समाप्त आदेश कमांड चैनल को मारता है। एकमात्र पकड़ यह है कि सर्वर पर जीएनयू कोर्यूटिल्स मौजूद होना चाहिए। विफल रहे कि विकल्प हैं।

+0

मेरे लिए क्या काम किया गया उपर्युक्त पर भिन्नता थी: 'टाइमआउट-सिगकिल <टाइमआउट वैल्यू>', अन्यथा कार्यक्रम नहीं मारा गया था। – Lidia

0

मैं चैनल से exec_command बुला समस्या का एक बहुत था, बजाय मैं सीधे ssh कनेक्शन से exec_command का उपयोग करें और एसटीडी उत्पादन के चैनल, कोड है कि मेरे लिए काम करता फोन myexec की तरह है:

#!/usr/bin/env python 
import paramiko 
import select 

def myexec(ssh, cmd, timeout): 
    stdin, stdout, stderr = ssh.exec_command(cmd) 
    channel = stdout.channel 
    stdin.close() #As I don't need stdin 
    channel.shutdown_write() #As I will not write to this channel 

    stdout_chunks = [] 
    stdout_chunks.append(stdout.channel.recv(len(stdout.channel.in_buffer))) 

    # chunked read to prevent stalls 
    while not channel.closed or channel.recv_ready() 
     or channel.recv_stderr_ready(): 

    # stop if channel was closed prematurely, 
    # and there is no data in the buffers. 
    got_chunk = False 
    readq, _, _ = select.select([stdout.channel], [], [], timeout) 
    for c in readq: 
     if c.recv_ready(): 
     stdout_chunks.append(stdout.channel.recv(len(c.in_buffer))) 
     got_chunk = True 
     if c.recv_stderr_ready(): 
     # make sure to read stderr to prevent stall 
     stderr.channel.recv_stderr(len(c.in_stderr_buffer)) 
     got_chunk = True 
    if not got_chunk \ 
      and stdout.channel.exit_status_ready() \ 
      and not stderr.channel.recv_stderr_ready() \ 
      and not stdout.channel.recv_ready(): 
     # indicate that we're not going to read from this channel anymore 
     stdout.channel.shutdown_read() # close the channel 
     stdout.channel.close() 
     break # exit as remote side is finished and our bufferes are empty 

    # close all the pseudofiles 
    stdout.close() 
    stderr.close() 
    return (''.join(stdout_chunks), stdout.channel.recv_exit_status()) 

ssh = paramiko.SSHClient() 
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect('remotehost', username='remoteuser', password='remotepassword') 
rtrval = myexec(ssh, 'remotecomand', 5*60) 
ssh.close() 
print rtrval 

मैं डेबियन 8 और पायथन 2.7.13 का उपयोग करता हूं, शुभकामनाएं।