Подключаемые модули сети
Каждый плагин подключения к сети имеет набор собственных плагинов, которые предоставляют спецификацию подключения для определённого набора устройств. Конкретный используемый плагин выбирается во время выполнения на основе значения переменной ansible_network_os для хоста. Эта переменная должна быть установлена в такое же значение, как имя загружаемого плагина. Таким образом, ansible_network_os=nxos попытается загрузить плагин в файле с именем nxos.py, поэтому важно давать плагинам осмысленные имена.
Общедоступные методы этих плагинов могут вызываться из модуля или module_utils с объектом прокси-подключения, как и другие методы подключения. Ниже приведён очень простой пример использования такого вызова в файле module_utils, чтобы его можно было использовать другими модулями.
from ansible.module_utils.connection import Connection
def get_config(module):
# module is your AnsibleModule instance.
connection = Connection(module._socket_path)
# You can now call any method (that doesn't start with '_') of the connection
# plugin or its platform-specific plugin
return connection.get_config()
Разработка плагинов httpapi
Плагины httpapi служат адаптерами для различных API HTTP(S) для использования с плагином подключения httpapi. Они должны реализовывать минимальный набор удобных методов, адаптированных к API, который вы пытаетесь использовать.
В частности, есть несколько методов, которые ожидаются плагином подключения httpapi.
Выполнение запросов
Плагин подключения httpapi имеет метод send(), но плагин httpapi требует метод send_request(self, data, **message_kwargs), как оболочку более высокого уровня над send(). Этот метод должен подготавливать запросы, добавляя фиксированные значения, такие как общие заголовки или корневые пути URL. Этот метод может выполнять более сложную работу, например, преобразовывать данные в отформатированные полезные данные или определять путь и метод запроса. Затем он также может распаковывать ответы для более удобного использования вызывающей стороной.
from ansible.module_utils.six.moves.urllib.error import HTTPError
def send_request(self, data, path, method='POST'):
# Fixed headers for requests
headers = {'Content-Type': 'application/json'}
try:
response, response_content = self.connection.send(path, data, method=method, headers=headers)
except HTTPError as exc:
return exc.code, exc.read()
# handle_response (defined separately) will take the format returned by the device
# and transform it into something more suitable for use by modules.
# This may be JSON text to Python dictionaries, for example.
return handle_response(response_content)
Авторизация
По умолчанию все запросы будут авторизованы с помощью HTTP Basic аутентификации. Если запрос может возвращать какой-либо токен вместо HTTP Basic, метод update_auth(self, response, response_text) должен быть реализован для проверки ответов на наличие таких токенов. Если токен предназначен для включения в заголовки каждого запроса, достаточно вернуть словарь, который будет объединён с вычисляемыми заголовками для каждого запроса. Предварительная реализация этого метода делает именно это для файлов cookie. Если токен используется другим способом, например, в строке запроса, вы должны вместо этого сохранить этот токен в переменную экземпляра, где метод send_request() (выше) может добавить его к каждому запросу.
def update_auth(self, response, response_text):
cookie = response.info().get('Set-Cookie')
if cookie:
return {'Cookie': cookie}
return None
Если вместо этого нужно явно запросить конечную точку входа для получения токена аутентификации, метод login(self, username, password) может быть реализован для вызова этой конечной точки. Если он реализован, этот метод будет вызван один раз перед запросом любых других ресурсов сервера. По умолчанию он также будет пытаться вызываться при получении ответа HTTP 401 от запроса.
def login(self, username, password):
login_path = '/my/login/path'
data = {'user': username, 'password': password}
response = self.send_request(data, path=login_path)
try:
# This is still sent as an HTTP header, so we can set our connection's _auth
# variable manually. If the token is returned to the device in another way,
# you will have to keep track of it another way and make sure that it is sent
# with the rest of the request from send_request()
self.connection._auth = {'X-api-token': response['token']}
except KeyError:
raise AnsibleAuthenticationFailure(message="Failed to acquire login token.")
Аналогично, logout(self) может быть реализован для вызова конечной точки для аннулирования и/или освобождения текущего токена, если такая конечная точка существует. Этот метод будет автоматически вызываться при закрытии подключения (и, следовательно, при сбросе).
def logout(self):
logout_path = '/my/logout/path'
self.send_request(None, path=logout_path)
# Clean up tokens
self.connection._auth = None
Обработка ошибок
Метод handle_httperror(self, exception) может обрабатывать статусы, возвращаемые сервером. Значение возврата указывает, как плагин будет продолжать запрос:
- Значение
trueозначает, что запрос может быть повторён. Это может использоваться для указания временной ошибки или ошибки, которая была разрешена. Например, предварительная реализация попытается вызватьlogin()при получении 401 и вернётtrueпри успехе. - Значение
falseозначает, что плагин не может восстановиться от этого ответа. Статус кода будет возвращён вызывающему модулю как исключение. Любое другое значение будет рассматриваться как некритический ответ от запроса. Это может быть полезно, если сервер возвращает сообщения об ошибках в теле ответа. Возвращение исходного исключения обычно достаточно в этом случае, так как объекты HTTPError имеют тот же интерфейс, что и успешный ответ.
Например, плагины httpapi, см. исходный код плагинов httpapi, включённых в Ansible Core.
© 2012–2018 Michael DeHaan
© 2018–2019 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.8/network/dev_guide/developing_plugins_network.html