Основы использования Fabric
Оглавление
- Введение
- Установка
- Использование Fabric
- Файл fabfile.py
- Использование Fabric на локальном компьютере
- Использование Fabric для взаимодействия с удаленным сервером
- Обработка ошибок
- Расширенные возможности запуска задач. Декоратор task
- Заключение
- Коментарии
Введение
Как известно, то, что обычный пользователь делает мышкой, повторяя одни и те же действия по 10 раз, программисты и, особенно, админы, делают скриптами. Инструментов и языков для написания скриптов существует огромное количество. Некоторые из них предназначены для определенных задач, например, на сборку и компиляцию программ, как Makefile и его последователи, другие являются более низкоуровневыми, например тот же bash. Промежуточное место я бы отвел для таких скриптовых языков как Python, Perl, Ruby. Преимущество последних в первую очередь в читаемости кода, когда нужно писать что-то большое, но в то же время, например, для работы с файлами, при работе с этими языками приходится писать больше кода, чем на том же bash.
В этой статье мы поговорим о Fabric - библиотеке для Python (в данный момент поддерживаются версии 2.5 - 2.7), которую можно использовать для многих задач автоматизации, при этом она позволяет легко вызывать команды того же bash, где это необходимо или оправдано.
Если мельком взглянуть на документацию Fabric, то складывается впечатление, что эта библиотека предназначена в первую очередь для работы на удаленном сервере при подключении к нему через SSH, однако, ее можно использовать и для локальных задач.
Лично я на данный момент Fabric использую для трех типов задач:
- В качестве замены Makefile для сборки программы OutWiker.
- Для создания резервных копий на сетевом хранилище.
- Для управления сайтами через SSH (развертывание, создание резервных копий, перезапуск сервера и т.д.)
Разбираться с Fabric мы начнем с самого начала, т.е. с установки.
Установка
Установка под Linux не должна вызывать никаких затруднений. Достаточно написать в консоли
или
кому что больше нравится. И Fabric будет установлен вместе с необходимыми зависимостями.
А вот под Windows с установкой зависимостей могут возникнуть затруднения. Fabric использует библиотеку шифрования PyCrypto, которая, как правило, распространяется в исходных кодах и компилируется на месте при установке. Компиляция ее под Linux проблем не возникает, потому что редкая Linux-система (особенно система админа или программиста) не имеет компилятора C/C++.
Под Windows его может и не быть. Но, к счастью, можно найти готовые сборки PyCrypto под Windows. Доверять им или нет - ваше дело. Однако, если PyCrypto установить, то затем установка Fabric завершится без проблем.
Чтобы убедиться, что Fabric установлен, наберем в консоли команду
Если вы получите сообщение вида:
Remember that -f can be used to specify fabfile path, and use -h for help.
Aborting.
Couldn't find any fabfiles!
Remember that -f can be used to specify fabfile path, and use -h for help.
То значит, что все нормально и можно двигаться дальше.
Если вы пользуетесь Windows и вы получите сообщение о том, что команда fab не найдена, то убедитесь, что у вас добавлен путь до папки C:\Python27\Scripts\ или ее аналога для той версии Python, который вы используете, в переменную окружения PATH. В крайнем случае, вы всегда можете писать команду в виде
но просто fab писать удобнее.
Использование Fabric
Использование Fabric во многом напоминает использование Makefile, т.е. вы создаете в какой-то папке файл fabfile.py, в котором описываете задачи (как они описываются, мы рассмотрим чуть позже). Например, там может быть задача backup (для создания резервной копии) или deploy (для развертывания сайта) и т.д.
Затем в консоли вы запускаете команду
или
И Fabric выполняет нужные задачи. Можно указать несколько задач, тогда они будут выполнены последовательно, например, сначала создать резервную копию, а потом развернуть приложение:
В задачи можно передавать параметры. Параметры передаются после символа ":" после имени задачи. Пусть, например, мы можем указать путь до папки, куда делать резервную копию:
Если в параметрах нет пробелов, то кавычки можно не писать. Если параметров несколько, то они перечисляются через запятую. Но про передачу параметров мы еще поговорим.
Файл fabfile.py
Структура файла
Библиотека Fabric замечательна тем, что для ее использования не нужно разбираться с новым языком описания задач или каким-то новым синтаксисом, все задачи описываются на языке Python, возможно, с использованием вспомогательных средств Fabric. Так, например, для создания двух задач, которые были приведены выше в качестве примера, достаточно создать файл с именем fabfile.py (имя важно, хотя при желании его можно изменить, о чем будет сказано ниже) со следующим содержимым:
def backup ():
print u'Run backup...'
print u'Ok'
def deploy ():
print u'Run deploy...'
print u'Ok'
В данном случае у нас задачи backup и deploy не принимают никаких параметров. Как вы видите, описание задачи - это просто функция на языке Python. Мы можем выполнить описанные выше команды в консоли (в качестве рабочей папки у нас должна быть папка с файлом fabfile.py):
Run backup...
Ok
Done.
$ fab deploy
Run deploy...
Ok
Done.
По умолчанию Fabric ищет задачи в файле с именем fabfile.py, но при желании вы можете назвать этот файл по-другому, но в этом случае вам надо будет явно указывать имя файла с задачами с помощью с помощью одного из параметров -f или --fabfile. Например, если наш файл с задачами имеет имя my_fabfile.py, то мы можем запустить задачи так, как показано ниже:
или
Кроме того, в файле профиля пользователя можно создать файл .fabricrc (под Linux путь будет выглядеть как ~/.fabricrc), в котором задаются различные настройки по умолчанию. Если в этот файл добавить строку
то по умолчанию Fabric будет искать не файл fabfile.py, а файл my_fabfile.py.
Документирование задач
Команда fab имеет множество параметров, один из самых полезных параметров - это параметр -l или --list. Этот параметр показывает, какие задачи есть в fabfile.py. Вот, например, результат работы параметра --list для приведенного выше fabfile.py.
Available commands:
backup
deploy
В таком виде подсказка уже полезна, но лучше все-таки еще добавить комментарии к каждой задаче. Чтобы их добавить, достаточно для каждой функции добавить описание в виде docstring:
def backup ():
"""
Создание резервной копии
"""
print u'Run backup...'
print u'Ok'
def deploy ():
"""
Развертывание сайта mycoolsite.com
"""
print u'Run deploy...'
print u'Ok'
В этом случае параметр --list выведет список задач следующим образом:
Available commands:
backup Создание резервной копии
deploy Развертывание сайта mycoolsite.com
На самом деле лучше не использовать в описаниях русские символы, поскольку в консоли под Windows они превратятся в кракозяблы (под Linux все будет работать нормально). Кроме того, не стоит писать очень длинные описания, поскольку они будут обрезаны до чуть больше чем 70 символов.
Понятно, что одни задачи могут вызывать другие задачи, кроме того внутри fabfile.py могут быть функции для внутреннего использования, попадание которых в список задач, выводимых с помощью параметра --list, нежелательно. Такие "внутренние" функции должны начинаться с символа "_". Так, например, функция _internal() из следующего прмиера fabfile.py не будет видна как задача:
def backup ():
"""
Создание резервной копии
"""
print u'Run backup...'
print u'Ok'
def deploy ():
"""
Развертывание сайта mycoolsite.com
"""
print u'Run deploy...'
print u'Ok'
def _internal ():
pass
Available commands:
backup Создание резервной копии
deploy Развертывание сайта mycoolsite.com
Передача параметров
Выше мы уже говорили о том, что в задачи для Fabric мы можем передавать параметры, приведем несколько примеров. Для демонстрации мы будем использовать простую функцию, которая просто выводит переданный параметр:
def task (param):
print param
Если мы забудем передать параметр, то вызов команды fab task приведет к ошибке:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/Fabric-1.10.1-py2.7.egg/fabric/main.py", line 743, in main
*args, **kwargs
File "/usr/local/lib/python2.7/dist-packages/Fabric-1.10.1-py2.7.egg/fabric/tasks.py", line 424, in execute
results['<local-only>'] = task.run(*args, **new_kwargs)
File "/usr/local/lib/python2.7/dist-packages/Fabric-1.10.1-py2.7.egg/fabric/tasks.py", line 174, in run
return self.wrapped(*args, **kwargs)
TypeError: task() takes exactly 1 argument (0 given)
Если мы укажем значение параметра, то все будет работать, как положено:
Hello world!
Done.
Аналогичного результата мы добьемся, если будем указывать имя параметра:
Hello world!
Done.
Как и везде в языке Python, можно использовать значения параметров по умолчанию:
def task (param=False):
print param
В этом случае значение параметра можно не передавать:
False
Done.
Если же задача функция принимает два и более значений, то параметры передаются через запятую. Например:
def task (param1, param2):
print param1
print param2
Hello
World
Done.
Главное тут не ошибиться и не добавить после запятой пробел, иначе второй параметр будет воспринят, как имя другой команды:
Warning: Command(s) not found:
World
Available commands:
task
Как видно из предыдущей ошибки, Fabric позволяет одной командой запускать сразу несколько задач, которые будут выполняться последовательно.
def task1 ():
print "Task 1"
def task2 ():
print "Task 2"
Task 1
Task 2
Done.
Но если одна из них завершится с ошибкой, то следующая задача выполнена не будет.
def task1 ():
raise Exception
def task2 ():
print "Task 2"
Task 1
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/Fabric-1.10.1-py2.7.egg/fabric/main.py", line 743, in main
*args, **kwargs
File "/usr/local/lib/python2.7/dist-packages/Fabric-1.10.1-py2.7.egg/fabric/tasks.py", line 424, in execute
results['<local-only>'] = task.run(*args, **new_kwargs)
File "/usr/local/lib/python2.7/dist-packages/Fabric-1.10.1-py2.7.egg/fabric/tasks.py", line 174, in run
return self.wrapped(*args, **kwargs)
File "/media/Data/Workdir/Черновики/В процессе/Fabric/__attach/examples/03/fabfile.py", line 5, in task1
raise Exception
Exception
Теперь, когда мы рассмотрели способы запуска задач, перейдем к рассмотрению того, какие возможности нам предоставляет библиотека Fabric и ее модули. Внутри функций-задач мы можем использовать любые функции Python, в том числе и для копирования, удаления, переименования файлов и т.д. Также в Python есть средства для запуска программ. Но давайте посмотрим, какие функции нам предоставляет Fabric для облегчения работы на локальной компьютере и на удаленном сервере.
Использование Fabric на локальном компьютере
Знакомство с Fabric у меня началось с того, что я начал использовать эту библиотеку в качестве замены команды make и Makefile. Когда-то давно для сборки OutWiker использовался обычный Makefile. Например, команда make win собирала версию под Windows, а команда make deb делала deb-пакет под Linux'ы на основе Debian.
В целом такая система работала, но была одна вещь, которая ужасно раздражала - это плохая переносимость Makefile между Windows и Linux. Как известно, под Windows команды make могут выполнены различными версиями интерпретатора (например, той версией, что прилагается к cygwin или отдельно скачанной сборкой make или make, идущий вместе с Visual Studio). Я уже даже не помню, какие версии у меня стояли, но внезапно после установки какого-то софта make по-другому стал воспринимать переводы строк и то ли вместо написания команд на разных строках пришлось писать команды через &&, то ли наоборот, не суть важно. Главное, что Makefile не работал под разными сборками make. Тогда я понял, что надо искать замену, причем желательно, чтобы сборщик описывал бы задачи на любимом языке, т.е. на Python. Путем поверхностного осмотра кандидатов (одним из них был waf), я остановился на Fabric. В первую очередь на это решение повлияло то, что Fabric позволяет писать не только на Python, но и где нужно, вставлять команды bash, благодаря чему makefile был переписан под Fabric буквально за час. После этого уже можно было его доводить до ума заменой некоторых команд bash на Python, где это улучшало читаемость кода.
В результате Makefile по-прежнему используется, но только как часть сборки deb-пакетов под Linux, в остальном везде используется Fabric.
Многие полезные функции из Fabric содержатся в модуле fabric api. Первые две функции, которые мы рассмотрим будут lcd и local. Первая из них - аналог команды cd консоли, вторая позволяет выполнять произвольные команды, как будто мы их запускаем в консоли. Главное удобство команды lcd перед стандартной для Python функции os.chdir(), которая выполняет, на первый взгляд, аналогичные действия (т.е. устанавливает рабочую директорию) в том, что функцию lcd можно использовать вместе с оператором with, при выходе из которого рабочая директория будет изменена на прежнее значение. Если рассмотреть работу функции lcd подробнее, то окажется, что она на самом деле не изменяет текущую рабочую директорию с точки зрения скрипта Python.
Рассмотрим пример, в котором будут выводиться рабочие директории с помощью функции стандартной библиотеки os.getpwd() и с помощью команды pwd консоли (для выполнения команды pwd будет использоваться вышеупомянутая функция local:
import os
from fabric.api import lcd, local
def task ():
# Выведем исходные рабочие директории
print "**** Before 'with' operator"
print os.getcwd()
local ("pwd")
with lcd ("~"):
print "**** Inside 'with' operator"
# Выведем рабочие директории внутри оператора with
print os.getcwd()
local ("pwd")
# Выведем рабочие директории после оператора with
print "**** After 'with' operator"
print os.getcwd()
local ("pwd")
Результат в моем случае выглядит так (понятно, что у вас директории будут называться иначе):
**** Before 'with' operator
/media/Data/Workdir/Черновики/В процессе/Fabric/__attach/examples/04
[localhost] local: pwd
/media/Data/Workdir/Черновики/В процессе/Fabric/__attach/examples/04
**** Inside 'with' operator
/media/Data/Workdir/Черновики/В процессе/Fabric/__attach/examples/04
[localhost] local: pwd
/home/jenyay
**** After 'with' operator
/media/Data/Workdir/Черновики/В процессе/Fabric/__attach/examples/04
[localhost] local: pwd
/media/Data/Workdir/Черновики/В процессе/Fabric/__attach/examples/04
Done.
Разумеется, внутри функции local можно выполнять что-то более полезное. Например, в fabfile проекта OutWiker в задаче win, с помощью которой создается сборка OutWiker под Windows, можно найти такие строки:
...
with lcd ("src"):
local ("python setup_win.py build")
...
with lcd ("build/outwiker_win"):
local ("7z a ..\outwiker_win_unstable.zip .\* .\plugins -r -aoa")
...
На момент написания этих строк fabfile проекта OutWiker из модуля fabric.api используются только функции lcd и local.
Использование Fabric для взаимодействия с удаленным сервером
Настройка удаленного интерпретатора
Fabric удобно использовать на локальном компьютере, но больше всего возможностей эта библиотека предоставляет для работы по сети на удаленных серверах. Все возможности библиотеки в этой статье мы не рассмотрим, но, чтобы понять принцип работы, поговорим о некоторых из них.
Для работы на удаленном сервере существуют аналоги локальных функций lcd и local, это функции cd и run соответственно. Чтобы научиться подключаться к серверу, сделаем fabfile с одной задачей, которая просто выполняет команду ls на сервере, чтобы вывести список файлов в домашней директории.
В простейшем случае скрипт будет такой, но нужно заранее предупредить, что он может не сработать:
from fabric.api import run
def runls ():
run ("ls")
Как видите, в скрипте мы не задаем никакого адреса (хотя мы может это сделать, и воспользуемся этой возможностью позже). Если мы теперь выполним команду fab runls, то Fabric сначала попросит ввести адрес, куда мы должны подключиться, а затем результат уже будет зависеть от настроек удаленного сервера. Далее приведен вывод команды fab runls при подключении к удаленному хранилищу по локальной сети:
No hosts found. Please specify (single) host string for connection: 192.168.100.10
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Disconnecting from 192.168.100.10... done.
Но вполне может случиться и такая ошибка:
No hosts found. Please specify (single) host string for connection: 192.168.100.10
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] Login password for 'jenyay':
[192.168.100.10] out: sh: /bin/bash: not found
[192.168.100.10] out:
Fatal error: run() received nonzero return code 127 while executing!
Requested: ls
Executed: /bin/bash -l -c "ls"
Aborting.
Disconnecting from 192.168.100.10... done.
run() received nonzero return code 127 while executing!
Requested: ls
Executed: /bin/bash -l -c "ls"
Здесь нет ничего страшного, это довольно частая ошибка, возникающая из-за того, что по умолчанию команда run на удаленном сервере пытается выполнить команду с помощью строки "/bin/bash -l -c "<Команда>"". Здесь можно обратить внимание на то, что явно указан путь до интерпретатора bash и то, что используется параметр -c, говорящий о том, что команды передаются далее в качестве строкового параметра. На удаленном сервере может не быть интерпретатора bash или он может быть расположен в другом месте.
Указать путь до интерпретатора можно разными способами. Во-первых, можно воспользоваться параметром командной строки --shell:
No hosts found. Please specify (single) host string for connection: 192.168.100.10
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Disconnecting from 192.168.100.10... done.
Поскольку на данном сервере не установлен интерпретатор bash, я воспользовался интерпретатором sh.
Второй способ задания параметров подключения состоит в том, чтобы воспользоваться классом env из fabric.api. Класс env содержит различные настройки, в том числе и для подключения к удаленному серверу. Для этого нам надо изменить скрипт:
from fabric.api import run, env
env.shell = "sh -c"
def runls ():
run ("ls")
Теперь нам не нужно использовать параметр --shell:
No hosts found. Please specify (single) host string for connection: 192.168.100.10
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Disconnecting from 192.168.100.10... done.
Способы ввода адресов удаленных хостов
Теперь сделаем так, чтобы нам не нужно было каждый раз вводить адрес сервера. Эту проблему можно решить также двумя способами: воспользоваться параметром командной строки -H или --hosts или добавить хосты с помощью свойства env.hosts. Для начала воспользуемся параметром командной строки. Обратите внимание, что параметр (как и свойство в классе env) называется hosts во множественном числе. Дело в том, что одну и ту же задачу можно одновременно запускать сразу на нескольких серверах (последовательно или параллельно). Поэтому при использовании параметра --hosts мы можем задать список хостов, разделенных запятой, а свойство env.hosts - это список строк.
Сначала воспользуемся предыдущим примером с использованием параметра --hosts:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Disconnecting from 192.168.100.10... done.
Теперь изменим скрипт, чтобы адрес хоста задавался в нем:
from fabric.api import run, env
env.shell = 'sh -c'
env.hosts = ['192.168.100.10']
def runls ():
run ('ls')
Теперь мы можем запускать эту задачу без дополнительных параметров:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Disconnecting from 192.168.100.10... done.
Чтобы продемонстировать работу сразу с несколькими хостами, добавлю в env.hosts адрес еще одного сервера:
from fabric.api import run, env
env.shell = 'sh -c'
env.hosts = ['192.168.100.10',
'192.168.100.20']
def runls ():
run ('ls')
Результат работы будет выглядеть следующим образом:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls
[192.168.100.10] Passphrase for private key:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
[192.168.100.20] Executing task 'runls'
[192.168.100.20] run: ls
[192.168.100.20] Passphrase for private key:
[192.168.100.20] out: Maildir backups domains imap public_html tmp
[192.168.100.20] out:
Done.
Disconnecting from 192.168.100.20... done.
Disconnecting from 192.168.100.10... done.
Избавляемся от ввода пароля
В приведенных выше примерах при каждом запуске приходилось вводить пароли. Это сравнительно безопасно, но не удобно, особенно если вы используете Fabric для запуска скриптов сразу на нескольких серверах. С помощью класса env мы можем записать пароли в скрипт, воспользовавшись словарем env.passwords. В этом словаре ключ - полный адрес сервера, обязательно включающий имя пользователя и порт, а значение - пароль. Например, предыдущий скрипт может выглядеть следующим образом (разумеется, все пароли вымышленные, любые совпадения случайны :) ):
from fabric.api import run, env
env.shell = 'sh -c'
env.hosts = ['192.168.100.10',
'192.168.100.20']
env.passwords = {
'jenyay@192.168.100.10:22': '123456',
'jenyay@192.168.100.20:22': '111111',
}
def runls ():
run ('ls')
Теперь, если все задано правильно, то вводить пароль просить не будут:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
[192.168.100.20] Executing task 'runls'
[192.168.100.20] run: ls
[192.168.100.20] out: Maildir backups domains imap public_html tmp
[192.168.100.20] out:
Done.
Disconnecting from 192.168.100.20... done.
Disconnecting from 192.168.100.10... done.
Параллельное выполнение задач
Поскольку мы полностью избавились от ввода текста в консоль, теперь мы можем воспользоваться еще одной особенностью Fabric - мы можем запустить задачу на двух серверах параллельно. Для этого достаточно добавить параметр --parallel или -P. Результат будет выглядеть примерно таким образом:
[192.168.100.10] Executing task 'runls'
[192.168.100.20] Executing task 'runls'
[192.168.100.10] run: ls
[192.168.100.20] run: ls
[192.168.100.20] out: Maildir backups domains imap public_html tmp
[192.168.100.20] out:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Подобного результата мы можем добиться без использования параметров командной строки, для этого нужно обернуть функцию runls в fabfile.py декоратором paralell из fabric.api:
from fabric.api import run, env, parallel
env.shell = 'sh -c'
env.hosts = ['192.168.100.10',
'192.168.100.20']
env.passwords = {
'jenyay@192.168.100.10:22': '123456',
'jenyay@192.168.100.20:22': '111111',
}
@parallel
def runls ():
run ('ls')
Запускаем и проверяем результат:
[192.168.100.10] Executing task 'runls'
[192.168.100.20] Executing task 'runls'
[192.168.100.10] run: ls
[192.168.100.20] run: ls
[192.168.100.20] out: Maildir backups domains imap public_html tmp
[192.168.100.20] out:
[192.168.100.10] out: Distrib VirtualBox VMs music projects Кино Фото
[192.168.100.10] out: Downloads backup photo temp Книги
[192.168.100.10] out:
Done.
Еще раз отмечу, что задачи можно запускать параллельно только если они не требуют ввода текста в консоли. В противном случае возникнет ошибка.
Обработка ошибок
Теперь посмотрим, как мы можем отслеживать ошибки, возникающие на удаленном сервере во время выполнения команды. Для демонстрации мы изменим предыдущий пример, чтобы команда ls выводила содержимое не домашней директории, а директории, имя которой будет передаваться в качестве параметра:
from fabric.api import run, env
env.shell = 'sh -c'
env.hosts = ['192.168.100.10']
env.passwords = {
'jenyay@192.168.100.10:22': '12345',
}
def runls (dirname):
command = 'ls {}'.format (dirname)
run (command)
print "**** Task is completed"
Если мы передадим существующую на удаленном хосте директорию, то все будет работать хорошо, и мы увидим сообщение о том, что выполнение задачи доведено до конца:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls projects
[192.168.100.10] out: LJournalist_old drawspectrum ljournalist ljwatcher training_site
[192.168.100.10] out: Scripts exercise_maker ljournalist_web outwiker
[192.168.100.10] out:
**** Task is completed
Done.
Disconnecting from 192.168.100.10... done.
Если же возникнет какая-то ошибка, то сразу после нее выполнение скрипта прервется, даже если мы могли бы обработать возникшую ошибку:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls abyrvalg
[192.168.100.10] out: ls: abyrvalg: No such file or directory
[192.168.100.10] out:
Fatal error: run() received nonzero return code 1 while executing!
Requested: ls abyrvalg
Executed: sh -c "ls abyrvalg"
Aborting.
Disconnecting from 192.168.100.10... done.
run() received nonzero return code 1 while executing!
Requested: ls abyrvalg
Executed: sh -c "ls abyrvalg"
Как быть? Мы можем указать Fabric, чтобы при возникновении ошибки, выполнение скрипта не прерывалось, а, например, только выводился текст ошибки, после чего выполнение скрипта продолжалось. Это можно сделать глобально, установив env.warn_only=True, но можно сделать более красиво, обернув команду, которая может ожидаемо вызвать ошибку, в блок with. Для этого мы воспользуемся функцией settings. Эта функция может принимать большое количество параметров, мы пока воспользуемся только одним - warn_only. Также мы воспользуемся тем, что функция run, возвращает объект, из которого можно извлечь код ошибки и текст, который команда выводила в консоль:
from fabric.api import run, env, settings
env.shell = 'sh -c'
env.hosts = ['192.168.100.10']
env.passwords = {
'jenyay@192.168.100.10:22': '12345',
}
def runls (dirname):
with settings (warn_only=True):
command = 'ls {}'.format (dirname)
result = run (command)
print 'result: {}'.format (result.return_code)
print "**** Task is completed"
Если мы передадим в задачу существующую директорию, то результат будет напоминать предыдущий, с той лишь разницей, что еще будет выведен нулевой код ошибки:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls projects
[192.168.100.10] out: LJournalist_old drawspectrum ljournalist ljwatcher training_site
[192.168.100.10] out: Scripts exercise_maker ljournalist_web outwiker
[192.168.100.10] out:
result: 0
**** Task is completed
Done.
Disconnecting from 192.168.100.10... done.
Если же переданной директории не существует, то выполнение задачи все равно будет доведено до конца. Мы могли бы как-то обработать ошибку, но в данном случае мы ее просто проигнорируем:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls abyrvalg
[192.168.100.10] out: ls: abyrvalg: No such file or directory
[192.168.100.10] out:
Warning: run() received nonzero return code 1 while executing 'ls abyrvalg'!
result: 1
**** Task is completed
Done.
Disconnecting from 192.168.100.10... done.
Мы могли бы указать, что не нужно писать даже предупреждения. Для этого в качестве первого параметра функции settings нужно передать менеджер контекста, который можно создать с помощью функции hide. Эта функция принимает текстовые параметры, описывающие, что именно нужно скрыть, это могут быть строки вида 'running', 'stdout', 'stderr', 'warnings'. Мы скроем только предупреждения:
from fabric.api import run, env, settings, hide
env.shell = 'sh -c'
env.hosts = ['192.168.100.10']
env.passwords = {
'jenyay@192.168.100.10:22': '12345',
}
def runls (dirname):
with settings (hide('warnings'), warn_only=True):
command = 'ls {}'.format (dirname)
result = run (command)
print 'result: {}'.format (result.return_code)
print "**** Task is completed"
Теперь в случае ошибки вывод будет таким:
[192.168.100.10] Executing task 'runls'
[192.168.100.10] run: ls abyrvalg
[192.168.100.10] out: ls: abyrvalg: No such file or directory
[192.168.100.10] out:
result: 1
**** Task is completed
Done.
Disconnecting from 192.168.100.10... done.
Расширенные возможности запуска задач. Декоратор task
Новый стиль задач
До сих пор мы создавали задачи, используя просто функции верхнего уровня в файле fabfile.py (или его аналоге), однако, начиная с Fabric 1.1 появились так называемые задачи нового стиля (new-style tasks), позволяющие еще более гибко их настраивать. Главное нововведение Fabric 1.1 - это декоратор task и класс Task, наследуя который можно создавать новые задачи. В этой статье мы рассмотрим только декоратор. Ранее все функции, не начинающиеся с символа "_", считались задачей. С новым стилем мы можем создавать функции с любыми именами, но снаружи видны будут только те из них, кто помечен декоратором task. Это правило работает, если есть хотя бы одна функция, отмеченная этим декоратором.
Например, пусть у нас есть такой fabfile.py:
from fabric.api import task
@task
def task1 ():
print "Task 1"
@task
def task2 ():
print "Task 2"
def internal_func ():
print "internal"
С помощью параметра --list посмотрим список всех задач в этом файле:
Available commands:
task1
task2
Как видно, функция internal_func не отображается, хотя она и не начинается с символа "_".
Задачи по умолчанию
С помощью декоратора task можно указать задачу, которая будет выполняться в случае, если команде fab не будет передано имя задачи. Для этого в качестве параметра декоратора нужно задать default=True:
from fabric.api import task
@task (default=True)
def task1 ():
print "Task 1"
@task
def task2 ():
print "Task 2"
Теперь нам не нужно использовать команду fab task1, чтобы выполнить первую задачу, достаточно просто запустить команду fab:
Task 1
Done.
Разумеется, если мы выполним только команду fab task2, то будет выполнена только она:
Task 2
Done.
Псевдонимы
С помощью декоратора task можно создавать псевдонимы для задач. В этом случае у задачи будет два имени, вызывать ее можно по любому из них. Чтобы создать псевдоним, в декоратор task нужно добавить параметр alias="псевдоним". Изменим предыдущий пример:
from fabric.api import task
@task (alias="mytask")
def task1 ():
print "Task 1"
@task
def task2 ():
print "Task 2"
Если мы теперь выполним команду fab --list, то в списке будет показаны три задачи:
Available commands:
mytask
task1
task2
Если мы выполним команду fab mytask, то это будет равносильно выполнению команды fab task1:
Task 1
Done.
Заключение
В этой статье мы рассмотрели основные, но далеко не все возможности библиотеки Fabric. Эта библиотека позволяет легко автоматизировать рутинные задачи как при работе на локальном компьютере, так и на удаленном сервере. С помощью Fabric можно создавать резервные копии, развертывать веб-приложения и даже использовать его для сборки программ. Преимущество Fabric в том, что эта библиотека не будет требовать от вас изучения нового синтаксиса, все скрипты пишутся на Python.
Спасибо за внимание, жду ваших отзывов и комментариев. Интересно, многие ли из вас дочитают эту большую статью до конца. :)
Другие статьи на тему программирования на Python
Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.