Memo/Fabric

http://dexlab.net/pukiwiki/index.php?Memo/Fabric
 

Fabric

  • 類似ツール
    • Puppet(ruby製) マニフェストを定義。状態を定義する。
    • Chef(ruby製)レシピを定義。

~/.ssh/configで多段sshの設定と、use_ssh_configを組み合わせると切断されない

/.ssh/configで多段sshの設定と、use_ssh_configを組み合わせると処理が終わっても切断されない。
バグっぽい

  • /.ssh/config

    1. Host gateway.example.com
    2.    ProxyCommand none
    3.    User gateway-user01
    4.    IdentityFile ~/.ssh/gateway.pem
    5.  
    6. Host target01.example.com
    7.   User user01
    8.   ProxyCommand ssh gateway.example.com nc %h %p 2> /dev/null
    9.   IdentityFile ~/.ssh/id_rsa
  • fabric側 v1.8.0
    1. # -*- coding: utf-8 -*-
    2. from fabric.api import *
    3. from fabric.contrib.console import *
    4. from pprint import pprint
    5.  
    6. @task
    7. def multistage_ssh_test():
    8.     env.use_ssh_config = True
    9.     run('whoami')
  • 実行
    1. # localは大丈夫
    2. fab multistage_ssh_test
    3.  
    4. # 多段sshは処理が終了しない
    5. fab -u user01 -H target01.example.com multistage_ssh_test
  • 対処
    • -gで明示的にgatewayを指定すると切断された
      1. fab -u user01 -H target01.example.com -g gateway-user01@gateway.example.com multistage_ssh_test

fabricからスクリプトが起動しない場合

fabricからシェルスクリプト等を実行する時、起動しないものがある

  • nohupを付ける
    1. run("nohup my_script.sh")
  • 仮想端末を割り当てる(pty=True)
    1. run("my_script.sh", pty=True)

fabtools

  • インストール
    1. sudo pip install fabtools
  • yumパッケージのインストール
    1. fabtools.rpm.install(['ntp'],options=['--quiet'])

実行時に処理が進まない場合

  • gateway側で操作ログをファイルに記録するために"script"コマンドを使っている場合に処理が進まなかった
    • 対処: ptyをデフォルトでは使わないようにする。(gateway側で@ptyが無い場合は記録しないように変更する必要あり)
      1. env.always_use_pty=False

gateway: 多段SSH

  • env.gatewayを使うサンプル。
    • localhost => gateway host => other host とssh接続して、hostnameコマンドを実行。other hostのhostnameが取得できれば成功
    • filegateway_test.py
      1. fab -f gateway_test.py gateway_test

env.roledefs/@roles : 環境別設定

  • fabricのバージョンによって仕様が変わったのか、blog等の情報は実行してもエラーが出たりと錯綜している
  • -R rolename オプションは、env.roledefs = {}を定義していないとエラーになる
  • 複数のサーバに特定の処理を実行(fabric 1.8.0)
    • fabfile.py
      1. # -*- coding: utf-8 -*-
      2. from fabric.api import *
      3.  
      4. env.roledefs = {
      5.     'web' : ['web01.example.com','web02.example.com'],
      6.     'db'  : ['db01.example.com','db02.example.com'],
      7. }
      8.  
      9. @task
      10. def deploy():
      11.     pass
    • webとdbにdeploy()を実行
      1. $ fab -R web,db deploy
      2. [web01.example.com] Executing task 'deploy'
      3. [web02.example.com] Executing task 'deploy'
      4. [db01.example.com] Executing task 'deploy'
      5. [db02.example.com] Executing task 'deploy'
      6.  
      7. Done.
    • webにだけdeploy()を実行
      1. fab -R web deploy
      2. [web01.example.com] Executing task 'deploy'
      3. [web02.example.com] Executing task 'deploy'
      4.  
      5. Done.
  • 関数毎に環境を指定する(fabric 1.8.0)
    • fabfile.py
      1. ...
      2. @task
      3. @roles('web')
      4. def deploy():
      5.     pass
    • roleを指定しないで実行するとwebホストにだけ実行される
      1. fab deploy
      2. [web01.example.com] Executing task 'deploy'
      3. [web02.example.com] Executing task 'deploy'
      4.  
      5. Done.
    • -R dbを指定しても、webホストに実行される
      1. fab -R db deploy
      2. [web01.example.com] Executing task 'deploy'
      3. [web02.example.com] Executing task 'deploy'
      4.  
      5. Done.

@runs_onceデコレータ

  • スクリプト全体で1回実行される

quiet() : エラーが発生した場合でも表示しない

  • 通常、local(), run(), sudo()を実行するとコンソールに表示され、エラーが発生すると中断するが、quiet()を使うと画面に何も表示されなくなる
  1. @task
  2. def test_quiet():
  3.     with quiet():
  4.         print local("test -e /tmp/build").succeeded # False
  5.         print local("test -e /tmp/build").failed # True

return_codeの取得

  • local(), run(), sudo() で使える
  1. @task
  2. def test_return():
  3.     print local('test -d /tmp', quiet=True, warn_only=True).succeeded   # True
  4.     print local('test -d /tmp', quiet=True, warn_only=True).return_code # 0

環境変数の設定

  • shell_env(name=value)で設定できる
    1. @task
    2. def test_shell():
    3.     with shell_env(VAR1='hoge'):
    4.         print local('echo $VAR1') # hoge
    5.         print run('echo $VAR1')   # hoge

with構文

  • python 2.5以上
  • with構文に対応しているcd(), lcd(), shell_env()等は、環境を引き継いで次のコマンドを実行できる
  1. @task
  2. def test_cd():
  3.     with cd('/tmp'):
  4.         run('pwd') # /tmp

文字列をbool型に変換

  • fabricのコマンドライン引数は文字列(str)になるため、ifの分岐等で思った通りにいかない場合がある。
  1. import distutils
  2.  
  3. print distutils.util.strtobool("true")  # 1: y, yes, t, true, on
  4. print distutils.util.strtobool("false") # 0: n, no, f, false, off
  5. var1 = distutils.util.strtobool(var1) if isinstance(var1, str) else var1

公開鍵認証での接続

  1. def test():
  2.     env.use_ssh_config = True # ~/.ssh/configを参照する場合
  3.     env.user           = "hoge"
  4.     env.key_filename   = "/home/hoge/.ssh/id_rsa"
  5.     run("hostname -s")

Exception : エラーと例外

  • 8. エラーと例外 Python 2.7ja1 documentation
    1. # -*- coding: utf8 -*-
    2. from fabric.api import *
    3. import logging
    4.  
    5. @task
    6. def test():
    7.     try:
    8.         run("hostname -s")
    9.         raise Exception('foo bar')
    10.     except Exception, e:
    11.         logging.error("%s" % ( e.message ))
    12.         sys.exit(1)

logging : ログの出力

  • コンソール(簡易)とファイル(詳細)に両方出力
    1. # -*- coding: utf8 -*-
    2. from fabric.api import *
    3. import logging
    4.  
    5. # ~/.fabricrc に log_filename=/tmp/fab.log 等を追加
    6. def set_logging(filename=env.log_filename, level=logging.INFO):
    7.     try:
    8.         logger = logging.getLogger()
    9.         if logger.handlers:
    10.             for handler in logger.handlers:
    11.                 logger.removeHandler(handler)
    12.  
    13.         if filename != "":
    14.             logging.basicConfig(
    15.                 filename=filename
    16.                 , level=level
    17.                 , format="%(asctime)s\t%(levelname)s\t%(message)s\t%(module)s\t%(funcName)s\t%(lineno)d\t%(pathname)s")
    18.  
    19.         console = logging.StreamHandler()
    20.         console.setLevel(level)
    21.         formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s")
    22.         console.setFormatter(formatter)
    23.         logging.getLogger().addHandler(console)
    24.     except Exception, e:
    25.         sys.stderr.write(e.message)
    26.  
    27. @task
    28. def test():
    29.     set_logging()
    30.     logging.debug('message') # level=logging.INFO なので出力されない
    31.     logging.info('message')
    32.     logging.warning('message')
    33.     logging.error('message')
    34.     logging.critical('message')

env.hosts : ホストの指定

    • 設定
      1. vim ~/.fabricrc
      2. ----
      3. servers=host1,host2
      4. ----
    • タスク
      1. # -*- coding: utf8 -*-
      2. from fabric.api import *
      3.  
      4. @task
      5. def test():
      6.     run("hostname -s")
      7.  
      8. @task
      9. def test2():
      10.     """ パスワード指定 """
      11.     env.use_ssh_config = True
      12.     env.host_string="ssh.example.com"
      13.     env.user="foo"
      14.     env.password="bar"
      15.     run("hostname -s")
  • fab -H host1,host2 # env.hostsに値が入る
    1. fab test -H host1,host2
  • env.hosts = ["host1","host2"] # 固定値のみで変数は使えない
    1. env.hosts = ["host1","host2"]
    2. #env.hosts = env.servers.split(",") # ERROR: No hosts found.
  • env.host_string # 1台だけ指定
    1. env.host_string="ssh.example.com"
  • @hosts : デコレータでは変数も使える
    1. @hosts(env.servers.split(","))
    2. @task
    3. def test():
    4.     run("hostname -s")
  • Using execute with dynamically-set host lists env.hosts = [var1,var2]と同等の事がしたい場合
    1. def _test(arg1, arg2):
    2.     run("hostname -s")
    3.  
    4. @task
    5. def test():
    6.     host_list=env.servers.split(",")
    7.     execute(_test, arg1="arg1", arg2="arg2", hosts=host_list)

fabricrc : 設定ファイル

  • default: ~/.fabricrc, または -c fabricrc
    1. vim ~/.fabricrc
    2. ----
    3. user = hoge
    4. ----
    5.  
    6. fab show_env:user
    7.  
    8. hoge

env : 環境変数

env.varname に対して、読み書き可能。省略時のデフォルトとして使われる。
env.newvar="value" のように新しい値も作成できる。
envは辞書型

  • '#'で注釈を入れた。環境:CentOS6.3 x86_64, fablic 1.8.0
    1. vim fabfile.py
    2. ----
    3. # -*- coding: utf8 -*-
    4. from fabric.api import *
    5. from pprint import pprint
    6.  
    7. @task
    8. def show_env(key = ""):
    9.     if key == "":
    10.         pprint(env)
    11.     else:
    12.         print(env[key])
    13. ----
    14.  
    15. fab show_env
    16.  
    17. {'': True,
    18.  'abort_exception': None,
    19.  'abort_on_prompts': False,
    20.  'again_prompt': 'Sorry, try again.',
    21.  'all_hosts': [],
    22.  'always_use_pty': True,
    23.  'colorize_errors': False,
    24.  'combine_stderr': True,
    25.  'command': 'show_env',
    26.  'command_prefixes': [],
    27.  'command_timeout': None,
    28.  'connection_attempts': 1,
    29.  'cwd': '',
    30.  'dedupe_hosts': True,
    31.  'default_port': '22',
    32.  'disable_known_hosts': False,
    33.  'eagerly_disconnect': False,
    34.  'echo_stdin': True,
    35.  'exclude_hosts': [],
    36.  'fabfile': 'fabfile',
    37.  'forward_agent': False,
    38.  'gateway': None,
    39.  'hide': None,
    40.  'host': None, # hostname
    41.  'host_string': None, # 1台だけホストを指定
    42.  'hosts': [], # ["host1", "host2"] のように複数ホストを指定。コマンドの-H host1,host2 で指定可能。変数はなぜか代入しても機能しない
    43.  'keepalive': 0,
    44.  'key_filename': None,
    45.  'lcwd': '',
    46.  'linewise': False,
    47.  'local_user': 'hoge',
    48.  'new_style_tasks': True,
    49.  'no_agent': False,
    50.  'no_keys': False,
    51.  'ok_ret_codes': [0],
    52.  'output_prefix': True,
    53.  'parallel': False,
    54.  'password': None,
    55.  'passwords': {},
    56.  'path': '',
    57.  'path_behavior': 'append',
    58.  'pool_size': 0,
    59.  'port': '22',
    60.  'rcfile': '/home/hoge/.fabricrc',
    61.  'real_fabfile': '/home/hoge/fabric/fabfile.py',
    62.  'reject_unknown_hosts': False,
    63.  'remote_interrupt': None,
    64.  'roledefs': {},
    65.  'roles': [],
    66.  'shell': '/bin/bash -l -c', # run(), local() で実行される場合のshell
    67.  'shell_env': {},
    68.  'show': None,
    69.  'skip_bad_hosts': False,
    70.  'ssh_config_path': '~/.ssh/config',
    71.  'sudo_prefix': "sudo -S -p '%(sudo_prompt)s' ",
    72.  'sudo_prompt': 'sudo password:',
    73.  'sudo_user': None,
    74.  'system_known_hosts': None,
    75.  'tasks': ['show_env'],
    76.  'timeout': 10,
    77.  'use_exceptions_for': {'network': False},
    78.  'use_shell': True,
    79.  'use_ssh_config': False, # True : ~/.ssh/config を使用する
    80.  'user': 'hoge', # ssh接続する時に使うユーザ名
    81.  'version': '1.8.0',
    82.  'warn_only': False} # タスク実行中に異常が起きた場合、中断する
    83.  
    84. Done.

AssertionError?

  • assert len(specs) == 1 and specs[0][0] == '=='
    1. pip list
    2.  
    3. ...
    4. Exception:
    5. Traceback (most recent call last):
    6.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/basecommand.py", line 134, in main
    7.     status = self.run(options, args)
    8.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 80, in run
    9.     self.run_listing(options)
    10.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 127, in run_listing
    11.     self.output_package_listing(installed_packages)
    12.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 136, in output_package_listing
    13.     if dist_is_editable(dist):
    14.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/util.py", line 347, in dist_is_editable
    15.     req = FrozenRequirement.from_dist(dist, [])
    16.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/__init__.py", line 194, in from_dist
    17.     assert len(specs) == 1 and specs[0][0] == '=='
    18. AssertionError

confirm: Yes/No確認

  • Yes/No確認を行う
    1. # -*- coding: utf-8 -*-
    2. from fabric.api import *
    3. from fabric.contrib.console import *
    4.  
    5. @task
    6. def test_confirm():
    7.     if confirm("hoge", False):
    8.         print("yes")
    9.     else:
    10.         print("no")
  • 実行
    1. fab test_confirm
    2. hoge [y/N] y
    3. yes

prompt: ユーザ入力待ち

  • var1=prompt("message") でユーザ入力待ちになる
  • source
    1. # -*- coding: utf-8 -*-
    2. from fabric.api import *
    3.  
    4. @task
    5. def create_file_in_prompt():
    6.     filename=prompt("Which file:");
    7.     run("touch /tmp/%s" % filename)
    8.  
    9. # vim: ts=4 sw=4 expandtab
  • run
    1. fab create_file_in_prompt -H localhost
    2. Which file: hoge

rootユーザで実行

  • 事前に接続するユーザがsudoできるようにしておく必要がある
  • sudo("command") : リモートでsudoしてcommandを実行
  • from fabric.api import sudo が必要
  • 別ユーザ/グループで実行するには sudo("command", user='apache',group='apache')
  • source
    1. # -*- coding: utf-8 -*-
    2. from fabric.api import *
    3.  
    4. @task
    5. def create_file_in_tmp(filename):
    6.     sudo("touch /tmp/%s" % filename)
    7.  
    8. # vim: ts=4 sw=4 expandtab
  • run
    1. fab create_file_in_tmp:filename=hoge -H localhost

関数と引数

  • 実行時に関数と引数を指定: fab command:arg1=val1,arg2=val2,...
  • source
    1. # -*- coding: utf-8 -*-
    2. from fabric.api import *
    3.  
    4. @task
    5. def hello_world(message=""):
    6.     localhost('echo "Hello, world! %s"' % message);
    7.  
    8. # vim: ts=4 sw=4 expandtab
  • run
    1. fab hello_world:message=hoge
    2.  
    3. Hello, world! hoge
  • パラメータに",","="を含む場合はエスケープが必要
    1. fab hello_world:"bar\,foo"
    2.  
    3. Hello, world! bar,foo
  • python
    • print("message %s %s" % (arg1, arg2)) : メッセージを表示
  • Core API > Operations
    • local("command") : ローカルで実行
    • run("command") : リモートで実行
    • put("local path", "remote path") : ローカルファイルをリモートへコピー。SFTP使用
    • get("remote path", "local paht") : リモートファイルをローカルへコピー。SFTP使用
    • var1=prompt("message") : ユーザ入力待ち
    • sudo("command") : root または別ユーザで実行
  • Core API > Utils
    • abort(message)
    • error(message)
    • puts(message) : printのエイリアス
    • warn(message)
  • Core API > Context Managers
    • quiet() エラーが発生した場合でも表示しない
    • shell_env(**kw) :環境変数を設定する。shell_env(HOGE="hoge")
    • cd(path) :ローカルカレントディレクトリの変更。 with cd(path): 構文
    • lcd(path) :リモートカレントディレクトリの変更。 with lcd(path): 構文
  • Contrib API > File and Directory Management
    • append(filename, text)
    • comment(filename, regex) : regexにマッチした行をコメントアウト
    • uncomment(filename, regex)
    • contains(filename, text) :ファイル中から文字列を探し、存在すればtrueを返す
    • exists(path)

clouddns:Python API binding to Rackspace Cloud DNS

  • インストール
    1. sudo pip install git+git://github.com/rackerlabs/python-clouddns

boto:A Python interface to Amazon Web Services

  • インストール
    1. sudo pip install boto

@taskデコレータ

  • 関数の前に @task を付ける事でfab -lでタスク一覧として表示される
  • 関数内の1行目のコメントがlistにも表示される
    1. vim fabfile.py
    2. ----
    3. # -*- coding: utf-8 -*-
    4. from fabric.api import *
    5.  
    6. @task
    7. def host_type() :
    8.     """ usage """
    9.     run('uname -s');
    10. # vim: ts=4 sw=4 expandtab
    11. ----
    12.  
    13. fab -l
    14.  
    15. Available commands:
    16.     host_type              usage

git/svn無視ファイル

  • svn
    1. svn propedit svn:ignore .
    2. ----
    3. *.pem
    4. *.pyc
    5. *.pyo
    6. ----
  • git
    1. vim .gitignore
    2. ----
    3. *.pem
    4. *.pyc
    5. *.pyo
    6. ----

再インストール/アップグレード

  • アップグレード
    1. sudo pip install fabric -U
    2.  
    3. # 1.7.0 => 1.8.0の場合
    4. sudo pip install fabric paramiko pycrypto -U
  • 再インストール
    1. sudo pip install fabric -I

インストール


Raspbian 7.x

  1. cat /etc/debian_version
  2. 7.1
  3.  
  4. python -V
  5. Python 2.7.3
  6.  
  7. sudo aptitude update
  8. sudo aptitude install python-pip python-dev gcc
  9. sudo pip-2.7 install fabric fabtools fexpect
  10. sudo pip-2.7 install -U distribute

CentOS 6.x

  • CentOS 6.4 x86_64
    1. yum install python python-devel python-setuptools --enablerepo=epel
    2. easy_install pip
    3. pip install fabric fabtools fexpect
    4.  
    5. pip list | grep -i fabric
    6. Fabric (1.6.1)
    7.  
    8. # pip list で以下のエラーが出たが、fabricは動いている。
    9. pip list
    10. Traceback (most recent call last):
    11.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/basecommand.py", line 134, in main
    12.     status = self.run(options, args)
    13.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 80, in run
    14.     self.run_listing(options)
    15.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 127, in run_listing
    16.     self.output_package_listing(installed_packages)
    17.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 136, in output_package_listing
    18.     if dist_is_editable(dist):
    19.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/util.py", line 347, in dist_is_editable
    20.     req = FrozenRequirement.from_dist(dist, [])
    21.   File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/__init__.py", line 194, in from_dist
    22.     assert len(specs) == 1 and specs[0][0] == '=='
    23. AssertionError
    24.  
    25. # 解決するには
    26. sudo pip install -U distribute
  • テスト
    • localhostにsshでログインしてuname -sを実行
    • デフォルトファイルは fabfile.py
    • -f PATH: 別のpyファイルを指定する場合。
    • -H HOSTS : ホスト指定。カンマ区切りで複数ホストを指定。指定しなければプロンプトが出る
    • -u USER : 接続ユーザ指定。指定しなければプロンプトが出る
    • -p PASSWORD : 接続パスワード
    • -i PATH : SSH private key file
  1. vim fabfile.py
  2. ----
  3. # -*- coding: utf-8 -*-
  4. from fabric.api import *
  5.  
  6. @task
  7. def host_type() :
  8.     """ usage """
  9.     run('uname -s');
  10. # vim: ts=4 sw=4 expandtab
  11. ----
  12.  
  13. fab -H localhost host_type
  14. out: Linux
  15.  
  16. fab list
  17. Available commands:
  18.     host_type              usage

CentOS 5.x

  • Memo/Python#a1abf210 でpipを使えるようにする
  • fabricのインストール
    1. sudo yum install python26-paramiko
    2. sudo pip2.6 install paramiko==1.10
    3. sudo pip install fabric==1.8.1

添付ファイル: filegateway_test.py 337件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-11-30 (日) 21:16:19 (1360d)