Robot Framework + FtpLibraryで、FTPサーバと通信する

この記事は Robot Framework Advent Calendar 2017 - Qiita の14日目の記事です。

今回はRobot Frameworkを使ってFTPサーバと通信してみます。

 
目次

 

環境

  • Python 3.6.3
  • Robot Framework 3.0.2
  • FtpLibrary 1.4
    • 後述の通り、現時点のリリース版ではPython3系に対応していないため、GitHubからインストール
  • FTPサーバ
    • pyftpdlib 1.5.3

 

FTPサーバの用意

pyftpdlibによる実装

今回は、PythonFTPサーバである pyftpdlib を使います。
giampaolo/pyftpdlib: Extremely fast and scalable Python FTP server library

 
GitHubにあるREADMEに従い、実装します。

FTPサーバの設定は

とします。

import pyftpdlib.authorizers
import pyftpdlib.handlers
import pyftpdlib.servers
import pathlib
import shutil


def run_server():
    # FTPサーバのルートディレクトリを作る
    # __file__のパスを絶対パスに変換し、ルートディレクトリのパスも追加する
    ftp_server_root_path = pathlib.Path(__file__).resolve().parent.joinpath('serv')
    # ディレクトリがある場合は、一度削除しておく
    if ftp_server_root_path.exists():
        # pathlibのrmdir()ではファイルがあると削除できないので、shutilを使う
        shutil.rmtree(ftp_server_root_path.as_posix())
    # ディレクトリを作って、パーミッションは777にしておく
    ftp_server_root_path.mkdir()
    ftp_server_root_path.chmod(0o777)
    print(ftp_server_root_path)

    # 認証ユーザーを作る
    authorizer = pyftpdlib.authorizers.DummyAuthorizer()
    authorizer.add_user('user', 'password', ftp_server_root_path.as_posix(), perm='elradfmw')

    # 個々の接続を管理するハンドラを作る
    handler = pyftpdlib.handlers.FTPHandler
    handler.authorizer = authorizer

    # FTPサーバーを立ち上げる
    # Unix系だと、21番ポートはsuper userがbindできるポートなので、それ以外のポートにする
    # https://github.com/giampaolo/pyftpdlib/blob/master/docs/faqs.rst#why-do-i-get-socket-error-permission-denied-error-on-ftpd-starting
    server = pyftpdlib.servers.FTPServer(("127.0.0.1", 12345), handler)
    server.serve_forever()


if __name__ == '__main__':
    run_server()

 
ちなみに、ファイルパス操作は pathlib モジュールを使いました。Python3.4から使えます。

例えば、ディレクトリの絶対パスを取得する場合、

# os.pathを使う場合
import os
path_by_os = os.path.abspath(os.path.dirname(__file__))

# pathlibを使う場合
import pathlib
path_by_pathlib = pathlib.Path(__file__).resolve().parent

となります。個人的には、pathlibの方が見やすいと感じました。

 

pyftpdlibの起動

上記スクリプトftp_server.py として保存し、起動します。

$ python ftp_server.py 
/path/to/ftp_library_sample/serv
[I 2017-12-14 05:37:03] >>> starting FTP server on 127.0.0.1:12345, pid=34184 <<<
[I 2017-12-14 05:37:03] concurrency model: async
[I 2017-12-14 05:37:03] masquerade (NAT) address: None
[I 2017-12-14 05:37:03] passive ports: None

 

Robot FrameworkのFTPLibraryを使う

インストール

Robot FrameworkでFTPサーバと通信するためには、 Robot-Framework-FTP-Library を使います。
https://github.com/kowalpy/Robot-Framework-FTP-Library

 
このライブラリはpipでインストールできます。

ただ、2017/12/14現在、PyPIにあるものはPython3系に対応していません*1

そのため、GitHubからインストールします。
Please support python3+ · Issue #10 · kowalpy/Robot-Framework-FTP-Library

$ pip install git+https://github.com/kowalpy/Robot-Framework-FTP-Library

 

Robot Frameworkのテストケースを実装

ドキュメントに従って実装します。
https://kowalpy.github.io/Robot-Framework-FTP-Library/FtpLibrary.html

今回は、

  • FTPサーバへの接続
  • FTPサーバへのアップロード
  • FTPサーバからのダウンロード

を実装してみます。

*** Settings ***
Library  FtpLibrary


*** Variables ***
${USER}  user
${PASSWORD}  password


*** TestCases ***
FTPサーバに接続する
    ftp connect  127.0.0.1  ${USER}  ${PASSWORD}  12345
    Mkd  foo
    # Welcome Messageを表示
    ${welcome} =  Get Welcome
    # 改行して見やすくする
    Log To Console  ${\n} ${welcome}
    # カレントディレクトリを表示
    ${path} =  Dir
    Log To Console  ${\n} ${path}
    FTP CLOSE

FTPサーバにファイルをアップロードする
    ftp connect  127.0.0.1  ${USER}  ${PASSWORD}  12345
    Mkd  upload
    Cwd  upload
    Upload File  result_google_python.png  uploaded.png
    FTP CLOSE

FTPサーバからファイルをダウンロードする
    ftp connect  127.0.0.1  ${USER}  ${PASSWORD}  12345
    # uploadディレクトリとupload.pngは前のテストで作成済なので、それを利用する
    Cwd  upload
    Download File  uploaded.png  downloaded.png
    FTP CLOSE

 

テストの実行

実行前のディレクトリの様子です。

f:id:thinkAmi:20171214210533p:plain:w250

 
テストを実行します。問題なく終了しました。

$ robot test_ftp.robot 
=========================
Test Ftp                 
=========================
FTPサーバに接続する      ...
 220 pyftpdlib 1.5.3 ready.
..
 ['drwxr-xr-x   2 you group  68 Dec 14 06:00 foo']
FTPサーバに接続する    | PASS |
-------------------------
FTPサーバにファイルをアップロードする    | PASS |
-------------------------
FTPサーバからファイルをダウンロードする    | PASS |
-------------------------
Test Ftp    | PASS |
3 critical tests, 3 passed, 0 failed
3 tests total, 3 passed, 0 failed
=========================

 
実行後のディレクトリの様子です。

アップロードとダウンロードがうまくいっているようです。

f:id:thinkAmi:20171214210601p:plain:w250

 
FTPサーバ側にもログが残っていました。

[I 2017-12-14 06:00:00] >>> starting FTP server on 127.0.0.1:12345, pid=34184 <<<
[I 2017-12-14 06:00:00] concurrency model: async
[I 2017-12-14 06:00:00] masquerade (NAT) address: None
[I 2017-12-14 06:00:00] passive ports: None
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[] FTP session opened (connect)
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] USER 'user' logged in.
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] MKD /path/to/ftp_library_sample/serv/foo 257
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] FTP session closed (disconnect).
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[] FTP session opened (connect)
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] USER 'user' logged in.
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] MKD /path/to/ftp_library_sample/serv/upload 257
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] CWD /path/to/ftp_library_sample/serv/upload 250
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] STOR /path/to/ftp_library_sample/serv/upload/uploaded.png completed=1 bytes=213754 seconds=0.014
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] FTP session closed (disconnect).
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[] FTP session opened (connect)
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] USER 'user' logged in.
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] CWD /path/to/ftp_library_sample/serv/upload 250
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] RETR /path/to/ftp_library_sample/serv/upload/uploaded.png completed=1 bytes=213754 seconds=0.004
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] FTP session closed (disconnect).

 

ソースコード

GitHubに上げました。ftp_library_sample ディレクトリの中が今回のファイルです。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples

*1:pip installできますが、参照して実行すると「ailed: SyntaxError: Missing parentheses in call to 'print'. Did you mean print(int "Starting Robot Framework Ftp Library as a remote server ...")? (FtpLibrary.py, line 451) 」が発生します