Memo/Fabric

https://dexlab.net:443/pukiwiki/index.php?Memo/Fabric
 

Fabric

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

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

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

  • /.ssh/config

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

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

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

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

fabtools

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

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

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

gateway: 多段SSH

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

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

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

@runs_onceデコレータ

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

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

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

return_codeの取得

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

環境変数の設定

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

with構文

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

文字列をbool型に変換

  • fabricのコマンドライン引数は文字列(str)になるため、ifの分岐等で思った通りにいかない場合がある。
import distutils

print distutils.util.strtobool("true")  # 1: y, yes, t, true, on
print distutils.util.strtobool("false") # 0: n, no, f, false, off
var1 = distutils.util.strtobool(var1) if isinstance(var1, str) else var1

公開鍵認証での接続

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

Exception : エラーと例外

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

logging : ログの出力

  • コンソール(簡易)とファイル(詳細)に両方出力
    # -*- coding: utf8 -*-
    from fabric.api import *
    import logging
    
    # ~/.fabricrc に log_filename=/tmp/fab.log 等を追加
    def set_logging(filename=env.log_filename, level=logging.INFO):
        try:
            logger = logging.getLogger()
            if logger.handlers:
                for handler in logger.handlers:
                    logger.removeHandler(handler)
    
            if filename != "":
                logging.basicConfig(
                    filename=filename
                    , level=level
                    , format="%(asctime)s\t%(levelname)s\t%(message)s\t%(module)s\t%(funcName)s\t%(lineno)d\t%(pathname)s")
    
            console = logging.StreamHandler()
            console.setLevel(level)
            formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(message)s")
            console.setFormatter(formatter)
            logging.getLogger().addHandler(console)
        except Exception, e:
            sys.stderr.write(e.message)
    
    @task
    def test():
        set_logging()
        logging.debug('message') # level=logging.INFO なので出力されない
        logging.info('message')
        logging.warning('message')
        logging.error('message')
        logging.critical('message')

env.hosts : ホストの指定

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

fabricrc : 設定ファイル

  • default: ~/.fabricrc, または -c fabricrc
    vim ~/.fabricrc
    ----
    user = hoge
    ----
    
    fab show_env:user
    
    hoge

env : 環境変数

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

  • '#'で注釈を入れた。環境:CentOS6.3 x86_64, fablic 1.8.0
    vim fabfile.py
    ----
    # -*- coding: utf8 -*-
    from fabric.api import *
    from pprint import pprint
    
    @task
    def show_env(key = ""):
        if key == "":
            pprint(env)
        else:
            print(env[key])
    ----
    
    fab show_env
    
    {'': True,
     'abort_exception': None,
     'abort_on_prompts': False,
     'again_prompt': 'Sorry, try again.',
     'all_hosts': [],
     'always_use_pty': True,
     'colorize_errors': False,
     'combine_stderr': True,
     'command': 'show_env',
     'command_prefixes': [],
     'command_timeout': None,
     'connection_attempts': 1,
     'cwd': '',
     'dedupe_hosts': True,
     'default_port': '22',
     'disable_known_hosts': False,
     'eagerly_disconnect': False,
     'echo_stdin': True,
     'exclude_hosts': [],
     'fabfile': 'fabfile',
     'forward_agent': False,
     'gateway': None,
     'hide': None,
     'host': None, # hostname
     'host_string': None, # 1台だけホストを指定
     'hosts': [], # ["host1", "host2"] のように複数ホストを指定。コマンドの-H host1,host2 で指定可能。変数はなぜか代入しても機能しない
     'keepalive': 0,
     'key_filename': None,
     'lcwd': '',
     'linewise': False,
     'local_user': 'hoge',
     'new_style_tasks': True,
     'no_agent': False,
     'no_keys': False,
     'ok_ret_codes': [0],
     'output_prefix': True,
     'parallel': False,
     'password': None,
     'passwords': {},
     'path': '',
     'path_behavior': 'append',
     'pool_size': 0,
     'port': '22',
     'rcfile': '/home/hoge/.fabricrc',
     'real_fabfile': '/home/hoge/fabric/fabfile.py',
     'reject_unknown_hosts': False,
     'remote_interrupt': None,
     'roledefs': {},
     'roles': [],
     'shell': '/bin/bash -l -c', # run(), local() で実行される場合のshell
     'shell_env': {},
     'show': None,
     'skip_bad_hosts': False,
     'ssh_config_path': '~/.ssh/config',
     'sudo_prefix': "sudo -S -p '%(sudo_prompt)s' ",
     'sudo_prompt': 'sudo password:',
     'sudo_user': None,
     'system_known_hosts': None,
     'tasks': ['show_env'],
     'timeout': 10,
     'use_exceptions_for': {'network': False},
     'use_shell': True,
     'use_ssh_config': False, # True : ~/.ssh/config を使用する
     'user': 'hoge', # ssh接続する時に使うユーザ名
     'version': '1.8.0',
     'warn_only': False} # タスク実行中に異常が起きた場合、中断する
    
    Done.

AssertionError

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

confirm: Yes/No確認

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

prompt: ユーザ入力待ち

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

rootユーザで実行

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

関数と引数

  • 実行時に関数と引数を指定: fab command:arg1=val1,arg2=val2,...
  • source
    # -*- coding: utf-8 -*-
    from fabric.api import *
    
    @task
    def hello_world(message=""):
        localhost('echo "Hello, world! %s"' % message);
    
    # vim: ts=4 sw=4 expandtab
  • run
    fab hello_world:message=hoge
    
    Hello, world! hoge
  • パラメータに",","="を含む場合はエスケープが必要
    fab hello_world:"bar\,foo"
    
    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

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

boto:A Python interface to Amazon Web Services

  • インストール
    sudo pip install boto

@taskデコレータ

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

git/svn無視ファイル

  • svn
    svn propedit svn:ignore .
    ----
    *.pem
    *.pyc
    *.pyo
    ----
  • git
    vim .gitignore
    ----
    *.pem
    *.pyc
    *.pyo
    ----

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

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

インストール


Raspbian 7.x

cat /etc/debian_version 
7.1

python -V
Python 2.7.3

sudo aptitude update
sudo aptitude install python-pip python-dev gcc
sudo pip-2.7 install fabric fabtools fexpect
sudo pip-2.7 install -U distribute

CentOS 6.x

  • CentOS 6.4 x86_64
    yum install python python-devel python-setuptools --enablerepo=epel
    easy_install pip
    pip install fabric fabtools fexpect
    
    pip list | grep -i fabric
    Fabric (1.6.1)
    
    # pip list で以下のエラーが出たが、fabricは動いている。
    pip list
    Traceback (most recent call last):
      File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/basecommand.py", line 134, in main
        status = self.run(options, args)
      File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 80, in run
        self.run_listing(options)
      File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 127, in run_listing
        self.output_package_listing(installed_packages)
      File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/commands/list.py", line 136, in output_package_listing
        if dist_is_editable(dist):
      File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/util.py", line 347, in dist_is_editable
        req = FrozenRequirement.from_dist(dist, [])
      File "/usr/lib/python2.6/site-packages/pip-1.4-py2.6.egg/pip/__init__.py", line 194, in from_dist
        assert len(specs) == 1 and specs[0][0] == '=='
    AssertionError
    
    # 解決するには
    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
vim fabfile.py
----
# -*- coding: utf-8 -*-
from fabric.api import *

@task
def host_type() :
    """ usage """
    run('uname -s');
# vim: ts=4 sw=4 expandtab
----

fab -H localhost host_type
out: Linux

fab list
Available commands:
    host_type              usage

CentOS 5.x

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

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

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-09-15 (土) 07:31:37 (94d)