# USSL - USSC SOAR SCRIPT LIB

<!-- Оглавление -->
## Оглавление
- [1. Описание](#описание)
    - [Установка](#установка)
- [2. Взаимодействие с внешними системами](#взаимодействие-с-внешними-системами)
    - [Доступные методы модуля ussl.transport](#доступные-методы-модуля-ussltransport)
    - [Доступные на данный момент транспорты](#доступные-на-данный-момент-транспорты)
- [3. Взаимодействие с USSC SOAR](#взаимодействие-с-USSC-SOAR)
    - [Использование форматирования и переназначения](#использование-форматирования-и-переназначения)
    - [Ключи расширения](#ключи-расширения)
        - [__SOAR.input](#soarinput)
        - [__SOAR.secrets](#soarsecrets)
        - [__SOAR.format](#soarformat)
    - [Использование ussl.postprocessing в скриптах](#использование-usslpostprocessing-в-скриптах)

<!-- /Оглавление -->

<!-- Описание -->
## Описание:

Библиотека разработана для упрощения работы с сетевыми устройствами, а также для расширения возможностей USSC SOAR.

### Установка

Установка через PyPI:
> `pip install ussl`


<!-- /Описание -->


<!-- Взаимодействие с внешними системами -->
## Взаимодействие с внешними системами:
Для упрощения взаимодействия с различными системами в модуле ussl.transport реализован единый интерфейс. На вход он принимает объект Protocol, содержащий данные о подключении и объект (или список объектов) Query, содержащий данные о команде, которую необходимо выполнить; в качестве ответа возвращается объект Responce.
<br>
<details>
  <summary>Поля объекта Protocol</summary>

    Общие для всех интерфейсов поля:
        host: ip-адрес или имя хоста, к которому необходимо подключиться;
        username: имя пользователя, под которым необходимо подключиться;
        password: пароль от указанного пользователя;
        interface: интерфейс, к которому необходимо подключиться (ssh, winrm, и т.д.);
        port: порт, на котором работает интерфейс;
        query: команда или набор команд, которые необходимо выполнить;
        encoding: кодировка запроса;
        decoding: кодировка ответа;
        window_width: ширина окна консоли (влияет на форматирование ответа).

    Поля, специфичные для winrm:
        domain: имя домена к которому необходимо подключиться;
        scheme: схема подключения (http или https);
        path: путь до WS-Management;
        transport: протокол аутентификации.

    Поля, специфичные для ssh:
        clean_timeout: таймаут очищения канала;
        look_for_keys: включить или отключить аутентификацию по ключам;
        auth_timeout: таймаут авторизации;
        timeout: таймаут соединения;
        pem_file: значение закрытого ключа авторизации от указанного пользователя.

</details>
<details>
  <summary>Поля объекта Query</summary>

    command: содержит командe, которую необходимо выполнить;
    timeout: содержит время, отведенное на выпонение команды;
    expects: содержит регулярные выражения, которые описывают
    ожидаемый ответ от конечной системы;
    sudo: содержит пароль от супер пользователя или enable.

</details>
<details>
  <summary>Поля объекта Responce</summary>
  
    result: содержит исходный ответ от целевой системы;
    text: содержится форматированный ответ от целевой системы;
    status: содержится статус выполнения переданной команды.

</details>
<br>
Из особенностей поведения можно выделить следующее:

- При передаче списка Query в Responce попадёт вывод последней команды или, если в ходе выполнения произошла ошибка, вывод ошибки с соответствующим статусом;

- ...

### Доступные методы модуля ussl.transport:

 ```python
from ussl.transport import Transport
from ussl.model import Protocol, Query

Transport.connect(Protocol(...)) # Устанавливает соединение с указанными параметрами, ничего не возвращает. В этом случае Protocol может не содержать Query.
Transport.execute(..., query=[Query(...)]) # Выполняет команды (или команду) в случае, если было установлено соединение. 
Transport.connect_and_execute(Protocol(..., query=[Query(...)])) # Устанавливает подключение и выполняет команды (или команду).
 ```

### Доступные на данный момент транспорты:

* WinRM
* SSH
* LDAP
___
<!-- /Взаимодействие с внешними системами -->


<!-- Взаимодействие с USSC SOAR -->

## Взаимодействие с USSC SOAR:

Для упрощения взаимодействия с USSC SOAR были разработан модуль ussl.postprocessing. Он берёт на себя работу с вводом/выводом данных в скриптах, переназначение ключей объектов, а также форматирование значений объектов, передаваемых в скрипт стандратным образом.

### Использование форматирования и переназначения

Для того чтобы использовать возможности расширения, необходимо чтобы выполнялись следующие условия:

- На вход скрипта подается **объект** JSON (не массив);
- В объекте содержится как минимум один из [ключей расширения](#ключи-расширения);
- Полезная информация* передаётся в ключе *default_input*;

> \* под полезной информацией подразумевается объект json, который передаётся на вход плейбука, а также между его шагами.

Изначально использование расширений задумывалась через поле [Parameters](https://states-language.net/#filters).


### Ключи расширения

- #### __SOAR.input
    Ключ предназначен для замены ключей в default_input. Ключ должен содержать объект, ключи которого являются **заменяемыми** ключами, а значения **заменяющими**. В результате на вход скрипта придёт JSON с уже замененными ключами.

    <details>
    <summary>Пример использования</summary>

    ```json
    {
        "default_input": {
            "input": {
                "some_key": "some_value"
            }
        },
        "__SOAR.input": {
            "some_key": "new_some_key"
        }
    }
    ```
 
    > *При передаче через Parameters значения в поле default_input должно иметь следующий вид*.

    ```json
    {
        "default_input.$": ".$"
    }
    ```

    </details>

<br>

- #### __SOAR.secrets
    Ключ предназначен для замены ключей в secrets. Этот ключ может быть полезен при использовании одинаковых секретов на нескольких разных устройствах, без создания новых секретов. Логика работы аналогична [__SOAR.input](#soarinput).

    <details>
    <summary>Пример использования</summary>

    Объект secret невозможно сформировать из вне, поэтому, для примера, будет использоваться следующий объект:
    
    ```json
    {
        "secrets": {
            "passwd": "1234567890"
        }
    }
    ```
    
    Тогда замена будет выглядеть следующим образом.
    
    ```json
    {
        "default_input": {
            "input": {
                "some_key": "some_value"
            }
        },
        "__SOAR.secrets": {
            "passwd": "password"
        }
    }
    ```
    > *При передаче через Parameters значения в поле default_input должно иметь следующий вид*.

    ```json
    {
        "default_input.$": ".$"
    }
    ```

    </details>

<br>

- #### __SOAR.format
    Ключ предназначен для форматирования текста, без изменения кода скриптов. Ключ должен содержать объект, ключи которого являются новыми полями default_input (в случае, если такое поле уже существует, его значение будет заменено), а значение - строка, в которую можно подставлять значения переменных, существующих в объекте default_json. В случае, если такой переменной не найдено место подстановки останется без изменений. Для подстановки переменной необходимо использовать фигурные скобики.

    > *Ключ __SOAR.format применяется после выполнения скрипта, т.е. если до этого применялся ключ __SOAR.input, то нужно использовать уже **замененные** значения ключей*

    <details>
    <summary>Пример использования</summary>
    
    ```json
    {
        "default_input": {
            "input": {
                "some_key": "some_value"
            }
        },
        "__SOAR.format": {
            "new_key": "Строка, содержащая {some_key}"
        }
    }
    ```

    В результате выполнения преобразований, скрипт вернёт следующий JSON

    ```json
    {
        "some_key": "some_value",
        "new_key": "Строка, содержащая some_value"
    }
    ```

    > *При передаче через Parameters значения в поле default_input должно иметь следующий вид*.

    ```json
    {
        "default_input.$": ".$"
    }
    ```

    </details>

### Использование ussl.postprocessing в скриптах

Для того чтобы использовать возможности ussl.postprocessing достаточно при создании скрипта выполнить несколько условий, а именно:

- Создать класс с произвольным именем
- Унаследоваться от класса ussl.posprocessing.base.BaseFunction
- Реализовать методы *validate_input*, *validate_secrets* или передать классы-схемы в параметры **input_schema** и **secrets_model** вашего класса для валидации входных данных
- Реализовать метод *function*
- Для вывода ошибок использовать исключения из модуля exceptions
- Вывести из метода словарь с результатом выполнения и сообщение
- В конце скрипта создать экземпляр созданного класса

```python
from ussl.postprocessing.base import BaseFunction
from marshmallow import Schema, fields

class InputSchema(Schema):
    name: str = fields.String(required=True)

class NewFunction(BaseFunction):
    def __init__(self):
        super().__init__(inputs_model=InputSchema)
    def function(self) -> (dict, str):
        ...
        result_key = self.input_json.get('name')

        return {"reslut_key": result_key}, "Успешно"
    
    def validate_input(self, input_json: dict):
        # Валидация входных данных 
        ...
        return input_json
    
    def validate_secrets(self, secrets: dict):
        # Валидация секретов 
        ...
        return secrets

NewFunction()
```