Spec-Zone.ru › Ansible

Разработка плагинов для сетевых модулей

Вы можете расширить существующие сетевые модули с помощью пользовательских плагинов в вашей коллекции.

  • Плагины подключения к сети
  • Разработка плагинов httpapi

    • Отправка запросов
    • Авторизация
    • Обработка ошибок
  • Разработка плагинов NETCONF
  • Разработка плагинов network_cli
  • Разработка плагинов cli_parser в коллекции

Плагины подключения к сети

Каждый плагин сетевого подключения имеет набор собственных плагинов, которые обеспечивают описание подключения для определенного набора устройств. Конкретный используемый плагин выбирается во время выполнения на основе значения переменной ansible_network_os, присвоенной хосту. Эта переменная должна быть установлена в такое же значение, как имя загружаемого плагина. Таким образом, ansible_network_os=nxos будет пытаться загрузить плагин в файл с именем nxos.py, поэтому важно называть плагин осмысленно для пользователей.

Публичные методы этих плагинов могут вызываться из модуля или модуля_utils с объектом прокси-подключения так же, как и другие методы подключения. Ниже приведен очень простой пример использования такого вызова в файле модуля_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.

Разработка плагинов NETCONF

Плагин подключения netconf обеспечивает подключение к удалённым устройствам через подсистему SSH NETCONF. Сетевые устройства обычно используют этот плагин подключения для отправки и получения вызовов RPC через NETCONF.

Плагин подключения netconf использует библиотеку Python ncclient для инициализации сессии NETCONF с удалённым сетевым устройством, поддерживающим NETCONF. ncclient также выполняет запросы NETCONF RPC и получает ответы. Вы должны установить ncclient на локальном узле управления Ansible.

Для использования плагина подключения netconf для сетевых устройств, поддерживающих стандартные операции NETCONF (RFC 6241), такие как get, get-config, edit-config, установите ansible_network_os=default . Вы можете использовать модули netconf_get, netconf_config и netconf_rpc для работы с удалённым хостом, поддерживающим NETCONF.

В качестве участника и пользователя вы должны иметь возможность использовать все методы класса NetconfBase, если ваше устройство поддерживает стандартный NETCONF. Вы можете внести вклад в новый плагин, если устройство, с которым вы работаете, имеет специфический для поставщика NETCONF RPC. Для поддержки RPC NETCONF, специфического для поставщика, добавьте реализацию в сетевой плагин NETCONF, специфичный для операционной системы.

Например, для Junos:

  • См. методы RPC Junos, специфичные для поставщика, реализованные в plugins/netconf/junos.py.
  • Установите значение ansible_network_os в имя файла плагина netconf, то есть junos в данном случае.

Разработка плагинов network_cli

Тип подключения network_cli использует paramiko_ssh в основе, которое создаёт псевдотерминал для отправки команд и получения ответов. network_cli загружает два плагина, специфичных для платформы, на основе значения ansible_network_os:

  • Плагин терминала (например, plugins/terminal/ios.py) — управляет параметрами, относящимися к терминалу, такими как установка длины и ширины терминала, отключение страниц и повышение привилегий. Также определяет регулярные выражения для идентификации командной строки и сообщений об ошибках.
  • Плагины cliconf (например, ios cliconf) — предоставляет уровень абстракции для операций отправки и получения на низком уровне. Например, метод edit_config() гарантирует, что приглашение находится в режиме config перед выполнением команд конфигурации.

Для внесения вклада в новую сетевую операционную систему для работы с подключением network_cli реализуйте плагины cliconf и terminal для этой сетевой операционной системы.

Плагины могут находиться в:

  • Рядом с playbook в папках

    cliconf_plugins/
    terminal_plugins/
    
  • Ролях

    myrole/cliconf_plugins/
    myrole/terminal_plugins/
    
  • Коллекциях

    myorg/mycollection/plugins/terminal/
    myorg/mycollection/plugins/cliconf/
    

Пользователь также может установить DEFAULT_CLICONF_PLUGIN_PATH для настройки пути к плагину cliconf.

После добавления плагинов cliconf и terminal в ожидаемые расположения пользователи могут:

  • Использовать cli_command для выполнения произвольной команды на сетевом устройстве.
  • Использовать cli_config для внесения изменений в конфигурацию на удалённых хостах без модулей, специфичных для платформы.

Разработка плагинов cli_parser в коллекции

Вы можете использовать cli_parse в качестве точки входа для плагина cli_parser в вашей собственной коллекции.

Следующий пример демонстрирует начало настраиваемого плагина cli_parser:

from ansible_collections.ansible.netcommon.plugins.module_utils.cli_parser.cli_parserbase import (
    CliParserBase,
)

class CliParser(CliParserBase):
    """ Sample cli_parser plugin
    """

    # Use the follow extension when loading a template
    DEFAULT_TEMPLATE_EXTENSION = "txt"
    # Provide the contents of the template to the parse function
    PROVIDE_TEMPLATE_CONTENTS = True

    def myparser(text, template_contents):
      # parse the text using the template contents
      return {...}

    def parse(self, *_args, **kwargs):
        """ Standard entry point for a cli_parse parse execution

        :return: Errors or parsed text as structured data
        :rtype: dict

        :example:

        The parse function of a parser should return a dict:
        {"errors": [a list of errors]}
        or
        {"parsed": obj}
        """
        template_contents = kwargs["template_contents"]
        text = self._task_args.get("text")
        try:
            parsed = myparser(text, template_contents)
        except Exception as exc:
            msg = "Custom parser returned an error while parsing. Error: {err}"
            return {"errors": [msg.format(err=to_native(exc))]}
        return {"parsed": parsed}

Следующая задача использует этот настраиваемый плагин cli_parser:

- name: Use a custom cli_parser
  ansible.netcommon.cli_parse:
    command: ls -l
    parser:
      name: my_organiztion.my_collection.custom_parser

Для разработки пользовательского плагина: - Каждый плагин cli_parser требует класс CliParser. - Каждый плагин cli_parser требует функцию parse. - Всегда возвращайте словарь с errors или parsed. - Разместите настраиваемый плагин cli_parser в директории plugins/cli_parsers коллекции. - Обратитесь к текущим плагинам cli_parsers для примеров.

См. также

  • Парсинг полуструктурированного текста с Ansible

© 2012–2018 Michael DeHaan
© 2018–2024 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/latest/network/dev_guide/developing_plugins_network.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API