2011-03-04 16 views
10

मैं टिड्ड शंख का उपयोग कर अजगर में एक बहुत ही सरल फ़ाइल स्थानांतरण क्लाइंट को लागू करने की कोशिश कर रहा हूं। क्लाइंट को कुछ फ़ाइलों को एक प्रोग्रामिक तरीके से रिमोट एसएसएच/एसएफटीपी सर्वर में स्थानांतरित करना चाहिए। फ़ंक्शन को उपयोगकर्ता नाम, पासवर्ड, फ़ाइल सूची, गंतव्य सर्वर दिया गया है: निर्देशिका और केवल क्रॉस-प्लेटफ़ॉर्म तरीके से प्रमाणीकरण और प्रतिलिपि बनाने की आवश्यकता है।घुमावदार शंख फ़ाइल ट्रांसफर

मैंने ट्विस्ट पर कुछ प्रारंभिक सामग्री पढ़ी है और अपना स्वयं का एसएसएच क्लाइंट बनाने में कामयाब रहा है जो रिमोट सर्वर पर cat निष्पादित करता है। मुझे फ़ाइलों को स्थानांतरित करने के लिए इसे विस्तारित करने में एक वास्तविक कठिन समय है। मैंने cftp.py और filetransfer परीक्षणों पर एक नज़र डाली है लेकिन मुड़कर पूरी तरह से रहस्यमय हूं।

क्या किसी के पास कोई सुझाव या संदर्भ हैं जो मुझे सही दिशा में इंगित कर सकता है? एसएसएच क्लाइंट जो मैंने पहले ही बनाया है this one पर आधारित है।

+0

क्या आप समझा सकते हैं कि आप अधिक विशेष रूप से कैसे फंस गए हैं? जैसा कि आपका प्रश्न अब है, मैं इसका जवाब देने का एकमात्र तरीका एक पूर्ण कॉंच/एसएफटीपी ट्यूटोरियल लिखना चाहता हूं, जो एसओ पर 15 अंकों से अधिक काम करता है (कम से कम इस समय)। ;) लेकिन एक और विशिष्ट प्रश्न का एक आसान जवाब हो सकता है। –

+0

@ जीन-पॉल अभी मैं _think_ मुझे t.c.s.f.FileTransferClient subclass करने की आवश्यकता है। मैं भी _think_ कि मुझे ऊपर से जुड़े उदाहरण के समान एक एसएसएच कनेक्शन खोलने की जरूरत है। मैं t.c.s.f.FileTransferClient और वास्तव में फ़ाइलों को कैसे स्थानांतरित करने के लिए ठीक से subclass करने के साथ अटक गया हूँ। एक पूर्ण उड़ा हुआ ट्यूटोरियल जरूरी नहीं है क्योंकि मुझे मुड़ने में दिलचस्पी है (यह मेरी पहली छोटी परियोजना थी) लेकिन दस्तावेज़ों के बारे में मुझे किस तरीके और कक्षाओं का उपयोग करना चाहिए या पढ़ना चाहिए या यहां तक ​​कि एक साधारण-इश उदाहरण भी हो सकता है (मुझे मिला पढ़ने के लिए कड़ी मेहनत करने के लिए cftp.py) बहुत सराहना की जाएगी। – rymurr

उत्तर

32

ट्विस्टेड कॉंच के साथ एक एसएफटीपी फ़ाइल स्थानांतरण करना कुछ अलग चरणों में शामिल है (ठीक है, यदि आप स्क्वांट करते हैं तो वे अलग हैं)। असल में, सबसे पहले आपको उस पर चल रहे एक एसएफटीपी उपप्रणाली के साथ एक चैनल के साथ एक कनेक्शन स्थापित करने की आवश्यकता है। वाह। फिर आप उस चैनल से जुड़े FileTransferClient इंस्टेंस के तरीकों का उपयोग कर सकते हैं जो आप जो भी एसएफटीपी ऑपरेशंस करना चाहते हैं उसे करने के लिए।

एसएसएच कनेक्शन सेट अप करने के लिए आवश्यक अनिवार्यता twisted.conch.client पैकेज में मॉड्यूल द्वारा प्रदान की गई API द्वारा आपके लिए देखभाल की जा सकती है। यहाँ एक समारोह है कि एक थोड़ा कम आश्चर्य की बात इंटरफ़ेस में twisted.conch.client.default.connect की मामूली weirdness ऊपर लपेटता है:

from twisted.internet.defer import Deferred 
from twisted.conch.scripts.cftp import ClientOptions 
from twisted.conch.client.connect import connect 
from twisted.conch.client.default import SSHUserAuthClient, verifyHostKey 

def sftp(user, host, port): 
    options = ClientOptions() 
    options['host'] = host 
    options['port'] = port 
    conn = SFTPConnection() 
    conn._sftp = Deferred() 
    auth = SSHUserAuthClient(user, options, conn) 
    connect(host, port, options, verifyHostKey, auth) 
    return conn._sftp 

यह समारोह एक उपयोगकर्ता नाम, होस्ट नाम (या IP पता), और पोर्ट नंबर लेता है और करने के लिए एक प्रमाणीकृत SSH कनेक्शन सेट दिए गए उपयोगकर्ता नाम से जुड़े खाते का उपयोग कर उस पते पर सर्वर।

दरअसल, यह उससे थोड़ा अधिक करता है, क्योंकि एसएफटीपी सेटअप यहां थोड़ा मिश्रित है। इस पल के लिए, SFTPConnection और _sftp को अनदेखा करें।

ClientOptions मूल रूप से केवल एक फैंसी डिक्शनरी है जो connect यह देखने में सक्षम होना चाहता है कि यह किससे कनेक्ट हो रहा है ताकि यह मेजबान कुंजी को सत्यापित कर सके।

SSHUserAuthClient वह वस्तु है जो परिभाषित करती है कि प्रमाणीकरण कैसे होगा। यह वर्ग जानता है कि ~/.ssh को देखने और स्थानीय एसएसएच एजेंट से बात करने जैसी सामान्य चीज़ों को कैसे आजमाएं। यदि आप बदलना चाहते हैं कि प्रमाणीकरण कैसे किया जाता है, तो यह ऑब्जेक्ट के साथ खेलने के लिए है। आप SSHUserAuthClient उपखंड कर सकते हैं और getPassword, getPublicKey, getPrivateKey, और/या signData विधियों को ओवरराइड कर सकते हैं, या आप अपनी पूरी तरह से अलग वर्ग लिख सकते हैं जिसमें आपके पास जो भी प्रमाणीकरण तर्क है, वह है। यह देखने के लिए कार्यान्वयन पर एक नज़र डालें कि प्रमाणीकरण प्राप्त करने के लिए एसएसएच प्रोटोकॉल कार्यान्वयन किस तरीके पर कॉल करता है।

तो यह फ़ंक्शन एक एसएसएच कनेक्शन स्थापित करेगा और इसे प्रमाणित करेगा। ऐसा करने के बाद, SFTPConnection उदाहरण खेल में आता है। ध्यान दें कि SSHUserAuthClient एक तर्क के रूप में SFTPConnection उदाहरण लेता है।एक बार प्रमाणीकरण सफल हो जाने पर, यह उस उदाहरण के कनेक्शन के नियंत्रण को बंद कर देता है। विशेष रूप से, उस उदाहरण में serviceStarted कहा जाता है।

class SFTPConnection(SSHConnection): 
    def serviceStarted(self): 
     self.openChannel(SFTPSession()) 

बहुत ही सरल:: यहाँ SFTPConnection वर्ग का पूरा कार्यान्वयन सब यह होता है खुला एक नया चैनल है। SFTPSession उदाहरण यह उस नए चैनल के साथ बातचीत करने के लिए गुजरता है। यहाँ कैसे मैं SFTPSession परिभाषित है:

class SFTPSession(SSHChannel): 
    name = 'session' 

    def channelOpen(self, whatever): 
     d = self.conn.sendRequest(
      self, 'subsystem', NS('sftp'), wantReply=True) 
     d.addCallbacks(self._cbSFTP) 


    def _cbSFTP(self, result): 
     client = FileTransferClient() 
     client.makeConnection(self) 
     self.dataReceived = client.dataReceived 
     self.conn._sftp.callback(client) 
SFTPConnection साथ जैसा

, इस वर्ग के लिए एक विधि कहा जाता हो जाता है कि जब कनेक्शन इसके लिए तैयार है। इस मामले में, जब चैनल सफलतापूर्वक खोला जाता है, तो यह कहा जाता है, और विधि channelOpen है।

अंत में, एसएफटीपी सबसिस्टम लॉन्च करने की आवश्यकताएं मौजूद हैं। तो channelOpen उस उपप्रणाली को लॉन्च करने के लिए चैनल पर एक अनुरोध भेजता है। यह एक उत्तर मांगता है ताकि यह बता सके कि वह सफल रहा है (या असफल)। यह Deferred पर कॉलबैक जोड़ता है, यह FileTransferClient को अपने आप में हुक करने के लिए मिलता है।

FileTransferClient उदाहरण वास्तव में कनेक्शन के इस चैनल पर चलने वाले बाइट्स को प्रारूपित और पार्स करेगा। दूसरे शब्दों में, यह का कार्यान्वयन केवल एसएफटीपी प्रोटोकॉल है। यह एसएसएच प्रोटोकॉल पर चल रहा है, जो इस उदाहरण के अन्य ऑब्जेक्ट्स का ख्याल रखता है। लेकिन जहां तक ​​यह चिंतित है, यह dataReceived विधि में बाइट प्राप्त करता है, उन्हें पार्स करता है और कॉलबैक के लिए डेटा भेजता है, और यह उन तरीकों की पेशकश करता है जो संरचित पायथन वस्तुओं को स्वीकार करते हैं, उन वस्तुओं को सही बाइट्स के रूप में स्वरूपित करते हैं, और उन्हें अपने परिवहन में लिखते हैं।

इनमें से कोई भी इसका उपयोग करने के लिए सीधे महत्वपूर्ण नहीं है, हालांकि। हालांकि, इसके साथ एसएफटीपी कार्यों को निष्पादित करने का उदाहरण देने से पहले, _sftp विशेषता को कवर करें। यह इस नए जुड़े FileTransferClient उदाहरण को किसी अन्य कोड के लिए उपलब्ध कराने के लिए मेरा कच्चा दृष्टिकोण है जो वास्तव में जानता है कि इसके साथ क्या किया जाए। एसएफटीपी सेटअप कोड को उस कोड से अलग करना जो वास्तव में एसएफटीपी कनेक्शन का उपयोग करता है, बाद वाले को बदलने के दौरान पूर्व का पुन: उपयोग करना आसान बनाता है।

तो Deferred मैं sftp में सेट FileTransferClient_cbSFTP में जुड़े हुए के साथ निकाल दिया जाता। और sftp के फोन करने वाले मिल गया है कि Deferred उन्हें लौटा ताकि कोड इस तरह कर सकते हैं: एक बाइट के लिए एक स्थानीय FileTransferClient इंस्टेंस को जोड़ने के लिए सभी तरह से

def transfer(client): 
    d = client.makeDirectory('foobarbaz', {}) 
    def cbDir(ignored): 
     print 'Made directory' 
    d.addCallback(cbDir) 
    return d 


def main(): 
    ... 
    d = sftp(user, host, port) 
    d.addCallback(transfer) 

तो सबसे पहले sftp पूरे कनेक्शन सेट, स्ट्रीम जिसमें दूसरे छोर पर कुछ एसएसएच सर्वर का एसएफटीपी उपप्रणाली है, और फिर transfer उस उदाहरण को लेता है और कुछ एसएफटीपी ऑपरेशन करने के लिए FileTransferClient के तरीकों का उपयोग करके निर्देशिका बनाने के लिए इसका उपयोग करता है।

from sys import stdout 

from twisted.python.log import startLogging, err 

from twisted.internet import reactor 
from twisted.internet.defer import Deferred 

from twisted.conch.ssh.common import NS 
from twisted.conch.scripts.cftp import ClientOptions 
from twisted.conch.ssh.filetransfer import FileTransferClient 
from twisted.conch.client.connect import connect 
from twisted.conch.client.default import SSHUserAuthClient, verifyHostKey 
from twisted.conch.ssh.connection import SSHConnection 
from twisted.conch.ssh.channel import SSHChannel 


class SFTPSession(SSHChannel): 
    name = 'session' 

    def channelOpen(self, whatever): 
     d = self.conn.sendRequest(
      self, 'subsystem', NS('sftp'), wantReply=True) 
     d.addCallbacks(self._cbSFTP) 


    def _cbSFTP(self, result): 
     client = FileTransferClient() 
     client.makeConnection(self) 
     self.dataReceived = client.dataReceived 
     self.conn._sftp.callback(client) 



class SFTPConnection(SSHConnection): 
    def serviceStarted(self): 
     self.openChannel(SFTPSession()) 


def sftp(user, host, port): 
    options = ClientOptions() 
    options['host'] = host 
    options['port'] = port 
    conn = SFTPConnection() 
    conn._sftp = Deferred() 
    auth = SSHUserAuthClient(user, options, conn) 
    connect(host, port, options, verifyHostKey, auth) 
    return conn._sftp 


def transfer(client): 
    d = client.makeDirectory('foobarbaz', {}) 
    def cbDir(ignored): 
     print 'Made directory' 
    d.addCallback(cbDir) 
    return d 


def main(): 
    startLogging(stdout) 

    user = 'exarkun' 
    host = 'localhost' 
    port = 22 
    d = sftp(user, host, port) 
    d.addCallback(transfer) 
    d.addErrback(err, "Problem with SFTP transfer") 
    d.addCallback(lambda ignored: reactor.stop()) 
    reactor.run() 


if __name__ == '__main__': 
    main() 

makeDirectory एक काफी सरल ऑपरेशन है:

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

यदि आप FileTransferClient के तरीकों के लिए दस्तावेज़ों को पढ़ते हैं, हालांकि, आपको यह देखना चाहिए कि वास्तविक फाइल स्थानांतरण के लिए, openFile मुख्य रूप से ब्याज की है। यह आपको Deferred देता है जो ISFTPFile प्रदाता के साथ आग लगती है। इस ऑब्जेक्ट में फ़ाइल सामग्री को पढ़ने और लिखने के तरीके हैं।

+0

ट्यूटोरियल के लिए बहुत बहुत धन्यवाद। इससे बहुत मदद मिली और चीजें अब बहुत स्पष्ट हैं। अब मेरे पास सबकुछ काम कर रहा है! मैं भविष्य में – rymurr

+0

ग्रेट स्पष्टीकरण में मुड़ने के साथ और अधिक सामान करने की उम्मीद कर रहा हूं, लेकिन क्या आप 'self.dataReceived = client.dataReceived' पर विस्तार कर सकते हैं? – daf

0

एसएसएच क्लाइंट अन्य ओएस सेवाओं से कुछ स्वतंत्र नहीं है। क्या आप वास्तव में .ssh फ़ोल्डर्स, कीचेन इत्यादि में समर्थन जोड़ना चाहते हैं? विंडोज के तहत एसपीपी (लिनक्स, ओएसएक्स) और pscp के आसपास रैपर बनाने के लिए और अधिक तेज़ और मजबूत तरीका हो सकता है। और इस तरह से अधिक "लिनक्स रास्ता" दिखता है (चेन मौजूदा जटिल टुकड़े कुछ जटिल में)।

+1

मुड़ और शंख की मेरी समझ यह है कि आप ओएस से स्वतंत्र एसएसएच सेवाओं को लागू कर सकते हैं। 'एसएसएच' फ़ोल्डर्स और जो कुछ मैं करना चाहता हूं उसके लिए महत्वपूर्ण नहीं हैं। एक रिमोट गुई सिर्फ एक सुरक्षित नेटवर्क में क्लस्टर को एक स्क्रिप्ट और कुछ पैरामीटर भेज रहा है, इसलिए इसे अत्यधिक सुरक्षित होने की आवश्यकता नहीं है। हालांकि क्लस्टर का एकमात्र तरीका एसएसएच के माध्यम से है। – rymurr

संबंधित मुद्दे