Особенности и проблемы mock-сервера Swordfish API Emulator

Эта статья — продолжение истории про разработку Ansible-модулей для взаимодействия с системами хранения данных (СХД) через API, которое удовлетворяет спецификации Swordfish. Модули в перспективе облегчат управление парком СХД от разных производителей.

Чтобы тестировать работоспособность созданных модулей, нужен настоящий сервер СХД (или тестовый mock-сервер), который предоставляет необходимый REST API. Получить настоящий сервер от производителя СХД — сложная задача, которая требует финансовых и временных затрат на получение программно-аппаратного обеспечения. В случае open-source разработки такая опция доступна не всем командам. 

Компания SNIA, разработчик спецификации Swordfish, подумала об этом и предоставила разработчикам свободно распространяемый mock-сервер — Swordfish API Emulator. В рамках проекта Лаборатории YADRO на базе СПбПУ Петра Великого мы протестировали его в работе. В статье расскажем, как он устроен и как с ним взаимодействовать в задачах. 

b78b354df676270b4b8af04371633c12.png

Что такое Swordfish API Emulator

Swordfish API Emulator — это веб-приложение для эмуляции системы, которая использует RESTful-операции, удовлетворяющие спецификации Swordfish. Он необходим для разработки программного обеспечения, которое взаимодействует с серверами СХД в соответствии со спецификацией.

В документации к рассматриваемому эмулятору указано, что он расширяет функционал Redfish Interface Emulator, добавляя возможности взаимодействия со всеми схемами данных из Swordfish API. На деле оказалось, что это не совсем так, но об этом позже. Сначала рассмотрим устройство Swordfish API Emulator. 

Устройство эмулятора

Для работы эмулятор использует Python и фреймворк Flask. 

Выделим несколько важных файлов эмулятора:

  • resources_manager.py — скрипт ответчает за подключение и инициализацию всех ресурсов, которые могут быть использованы при работе эмулятора. Здесь же прикрепляются Python API-файлы к URL-адресам.

  • utils.py — скрипт, содержащий вспомогательные функции эмулятора. 

  • g.py — скрипт, управляющий жизненным циклом Flask-сервера.

  • emulator.py — скрипт, содержащий команды управления запуском, перезапуском, иницилизацией resources_manager.py и другие необходимые для работы эмулятора функции.

Конфигурация эмулятора осуществляется в файле emulator-config.json. Он имеет следующие параметры:

  • MODE — указывает способ задания порта. Cloud — порт задается с помощью Cloud Foundry. Local — порт задается локально с помощью аргументов командной строки или по умолчанию при установке.  

  • HTTPS — определяет, используем ли https [Enable/Disable].

  • STATIC — определяет, используем ли статичные ресурсы из ./api_emulator/redfish/static [Enable/Disable].  

  • SPEC — используемая спецификация. Для работы эмулятора с Swordfish необходимо указать Redfish.  

  • TRAYS —  путь к начальным статическим ресурсам.

  • MOCKUPFOLDERS — путь к папке с макетом ресурсов, который используется во время создания новых экземпляров ресурсов.

  • AUTHENTICATION — включение/выключение авторизации.

  • CERTIFICATE — путь к SSL-сертификату. 

Стоит отметить, что AUTHENTICATION в статусе Enable использует https вне зависимости от установленного значения в свойстве HTTPS.

Файл emulator-config.json по умолчанию содержит следующие данные:

{
  "MODE": "Local",
  "HTTPS": "Disable",
  "TRAYS": [],
  "STATIC": "Disable",
  "SPEC": "Redfish",
  "MOCKUPFOLDERS": ["Mockups"],
  "AUTHENTICATION": "Enable",
  "CERTIFICATE": ["server.crt", "server.key"]
}

Теперь рассмотрим устройство ресурсов, с которыми взаимодействует эмулятор в процессе работы.

Ресурсы

Ресурсы — это данные или объекты СХД, предоставленные эмулятором: хранилища, диски, аккаунты, роли, сессии, системы и другое. В Redfish Interface Emulator их можно разделить на статические и динамические:

  • Статические ресурсы доступны только для чтения, то есть они отвечают на HTTP-запросы GET и DELETE. Эти ресурсы нельзя изменить с помощью POST-, PUT- и PATCH-запросов. Статические ресурсы создаются на основе макетов, размещенных в каталоге api_emulator/redfish/static. Этот тип ресурсов не используется Swordfish API Emulator.

  • Динамические ресурсы могут быть прочитаны, удалены, изменены с помощью HTTP-запросов GET, POST, PATCH, DELETE, PUT. С динамическим ресурсом связан один файл шаблона и один файл API. Для простоты идентификации оба файла имеют схожее название. Шаблоны хранятся в каталоге api_emulator/redfish/template, а файлы схем API — в api_emulator/redfish.

Динамические ресурсы хранятся в каталоге Resources в иерархии, отражающей родительско-дочерние отношение между ресурсами:

..
└── Resources/
    ├── index.json
    ├── $metadata/
    │   └── index.xml
    ├── AccountService/
    │   ├── index.json
    │   ├── Accounts/
    │   │   └── index.json
    │   └── Roles/
    │       ├── index.json
    │       ├── Administrator/
    │       │   └── index.json
    │       ├── Operator/
    │       │   └── index.json
    │       └── ReadOnlyUser/
    │           └── index.json
    ├── Chassis/
    │   ├── index.json
    │   └── ...
    ├── Fabrics/
    │   ├── index.json
    │   └── ...
    ├── Managers/
    │   ├── index.json
    │   └── ...
    ├── Registries/
    │   ├── index.json
    │   └── ...
    ├── SessionService/
    │   ├── index.json
    │   └── ...
    ├── Storage/
    │   ├── index.json
    │   └── ...
    └── Systems/
        ├── index.json
        └── ...

Иерархия каталогов сервер-эмулятора Swordfish.

Когда ресурс создается или изменяется, он всегда сохраняется в представленной выше иерархии каталогов данных сервер-эмулятора. Другими словами, все изменения ресурсов эмулятора сохранятся при перезапуске. 

При создании новых ресурсов через POST-запрос создаются ресурсы по умолчанию, так же по умолчанию создаются подресурсы созданного ресурса. 

В качестве примера файла шаблона и API рассмотрим ресурс Drive, который представляет собой диск эмулируемой системы хранения данных.

Файл шаблона ресурса Drive

Файл шаблона используется для создания новых экземпляров динамических ресурсов по умолчанию. Файл состоит из двух элементов: переменной TEMPLATE и функции получения нового экземпляра ресурса. 

В словаре TEMPLATE указаны поля, которые будут использоваться при создании нового ресурса:

_TEMPLATE = \
{
	"@Redfish.Copyright": "Copyright 2014-2021 SNIA. All rights reserved.",
	"@odata.id": "{rb}Systems/{ComputerSystemId}/Storage/{StorageId}/Drives/{DriveId}",
	"@odata.type": "#Drive.v1_15_0.Drive",
	"Id": "{DriveId}",
	"Name": "Drive",
}

Содержание словаря TEMPLATE.

Функция, возвращающая новый экземпляр ресурса, создает копию объявленного шаблона, а затем заполняет поля данными, взятыми из wildcards, которые передаются при вызове функции. Ниже представлена схема функции get_Drive0_instance, которая возвращает шаблон ресурса Drive0.

Блок-схема функции get_Drive0_instance.

Блок-схема функции get_Drive0_instance.

Файл API

Файл API содержит два разных класса: класс API коллекции и класс API ресурса. 

Коллекция определяет реакцию ресурса на HTTP-запросы, приходящие на REST API. Далее представлена блок-схема запросов-ответов, которые может обрабатывать API коллекции ресурса Drive.

Блок-схема запросов-ответов, обрабатываемых API коллекции Drive.

Блок-схема запросов-ответов, обрабатываемых API коллекции Drive.

В свою очередь, API ресурса определяет реакцию отдельных ресурсов в коллекции на REST-операции:

Блок-схема запросов-ответов Drive API.

Блок-схема запросов-ответов Drive API.

Взаимодействие с эмулятором

Так как эмулятор является HTTP-сервером, мы можем получить к нему доступ через любой HTTP-клиент. Протестируем функциональность эмулятора двумя способами: с помощью утилиты curl и инструмента Postman.

Взаимодействие через утилиту curl

Попробуем получить информацию обо всех хранилищах. Для взаимодействия с Swordfish API не забываем об авторизации. В эмуляторе существует два аккаунта: Administrator с ролью Admin и User с ролью ReadOnlyUser. Пароль от аккаунтов: Password. Более детально о ролевой модели, реализованной в Swordfish API Emulator, мы расскажем в отдельном блоке текста.

Чтобы получить информацию о хранилищах, необходимо отправить GET-запрос на end-point https://127.0.0.1:5000/redfish/v1/Storage. Так как SSL-сертификаты не подтверждены, добавляем флаг -k:

$ curl --user Administrator:Password https://127.0.0.1:5000/redfish/v1/Storage -k

Пример ответа сервер-эмулятора на запрос утилиты curl:

{
    "@odata.id": "/redfish/v1/Storage",
    "@odata.type": "#StorageCollection.StorageCollection",
    "Members": [
        {
            "@odata.id": "/redfish/v1/Storage/IPAttachedDrive1"
        },
        {
            "@odata.id": "/redfish/v1/Storage/IPAttachedDrive2"
        }
    ],
    "Members@odata.count": 2,
    "Name": "Storage Collection"
}

В нашем случае ответ эмулятора содержит:

  • Путь запрашиваемого ресурса: /redfish/v1/Storage.

  • Тип ресурса: #StorageCollection.StorageCollection.

  • Пути к подресурсам (в данном случае к хранилищам): /redfish/v1/Storage/IPAttachedDrive1, /redfish/v1/Storage/IPAttachedDrive2.

  • Количество подресурсов: 2.

  • Имя полученного ресурса: Storage Collection.

Взаимодействие через Postman  

Для начала необходимо авторизоваться. Это можно сделать двумя способами:

Первый способ. В инструменте Postman:

  • в блоке Authorization указываем значение Basic Auth,

  • в поле Username указываем Administrator,

  • в поле Password указываем Password.

Заполнение блока Authorization в инструменте Postman.

Заполнение блока Authorization в инструменте Postman.

Второй способ. Отправляем HTTP-запрос типа POST на end-point https://127.0.0.1:5000/redfish/v1/SessionService/Sessions со следующим телом POST-запроса:

{
 "UserName": "Administrator",   
"Password": "Password"
} 

Из ответа получаем X-Auth-Token, который передаем в заголовках следующего запроса.

Ответ сервера, содержащий X-Auth-Token.

Ответ сервера, содержащий X-Auth-Token.

Как в предыдущем примере, получим информацию о хранилищах. Для этого отправим HTTP-запрос типа GET на адрес https://127.0.0.1:5000/redfish/v1/Storage/.

Так выглядит ответ сервер-эмулятора на GET-запрос — такой же, как в первом варианте с Postman:

{
    "@odata.id": "/redfish/v1/Storage",
    "@odata.type": "#StorageCollection.StorageCollection",
    "Members": [
        {
            "@odata.id": "/redfish/v1/Storage/IPAttachedDrive1"
        },
        {
            "@odata.id": "/redfish/v1/Storage/IPAttachedDrive2"
        }
    ],
    "Members@odata.count": 2,
    "Name": "Storage Collection"
}

Итак, мы рассмотрели разные способы взаимодействия с эмулятором и изучили его устройство. Теперь перейдем к проблемам, с которыми мы столкнулись при использовании эмулятора для разработки собственных Ansible-модулей для спецификации Swordfish.

Проблемы

Запутанная иерархия файлов эмулятора

В главной директории исходных кодов эмулятора лежат всевозможные файлы и папки — как необходимые для работы эмулятора, так и в большинстве случаев ненужные. Например, Swordfish API Emulator использует только один файл конфигурации — emulator-config.json. Остальные конфигурационные файлы остались от Redfish Interface Emulator. Одной из причин не структурированности кода является автоматическая генерация кода для API ресурсов по XML-схеме. 

.
├── api_emulator/
│   └── ...
├── codegen/
│   └── ...
├── doc/
│   └── ...
├── infragen/
│   └── ...
├── Resources/
│   └── ...
├── templates/
│   └── ...
├── unittest_data/
│   └── ...
├── venv/
│   └── ...
├── .gitignore
├── AUTHORS.md
├── certificate_config.cnf
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── emulator.py
├── emulator-config.json
├── emulator-config_dynamic_dontpopulated.json
├── emulator-config_dynamic_populate.json
├── emulator-config_static.json
├── g.py
├── LICENSE.md
├── Procfile
├── README.md
├── requirements.txt
├── runtime.txt
├── server.crt
├── server.csr
├── server.key
├── server_public.key
├── test-composed.json
├── test-composed-with-id.json
├── unittests.py
└── v3.ext

Запутанная иерархия исходных файлов эмулятора Swordfish.

Также в качестве примера можно выделить юнит-тесты, которые просто так лежат в главной директории. На наш взгляд, их можно было переместить в отдельный каталог для дополнительной структуризации кода проекта.

Как исправить проблему: Необходима реструктуризация проекта. Для улучшения иерархии эмулятора необходимо изменить настройки генератора, который находится в каталоге autogen. А также может потребоваться изменение архитектуры кода. Зато в будущем это даст большую гибкость при обновлении функциональности эмулятора. 

Отсутствие некоторых API

Самый простой пример отсутствующих API — это Actions. У ресурсов есть отдельное поле Actions, в котором перечислены действия, которые можно совершить с помощью них. Например, перезапустить эмулятор. При попытке вызвать необходимый Action мы получим ошибку, так как необходимый URL не назначен в resource_manager.py — необходимой функциональности просто не существует.

Как исправить проблему: В качестве решения проблемы можно провести масштабное тестирование функциональности эмулятора в соответствии со спецификацией. Найти и добавить отсутствующий API.

Зависимость от Redfish Interface Emulator

Проблема зависимости Swordfish API Emulator от Redfish Interface Emulator проявляется на этапе установки эмулятора. Во время установки происходит клонирование репозитория эмулятора от DMTF. После удаляются ненужные файлы и добавляются схемы Swordfish. Таким образом Swordfish API Emulator зависит от команды разработчиков, которые не имеют отношение к SNIA. Если исходный код эмулятора Redfish будет подвержен некоторым серьезным изменениям, то Swordfish API Emulator может перестать работать корректно. 

Как исправить проблему: Тут потенциальным решением может стать форк репозитория Swordfish API Emulator для далнейшего редактирования эмулятора, чтобы убрать зависимость от Redfish Interface Emulator. В нашем случае мы решили написать свой эмулятор на языке Go. Этот язык мы выбрали не случайно: он прост в использовании и идеально подходит для написания клиент-серверных приложений, которым и является эмулятор СХД. Это уберет зависимость от стороннего кода и даст больше возможностей для реализации недостающей функциональности. 

Неудобная архитектура кода и неработающая функциональность

У эмулятора неудобная архитектура кода, из-за чего его тяжело модифицировать. Например, при попытке добавить Actions на перезапуск СХД, мы столкнулись с проблемой циклических зависимостей. Проблема была в том, что в emulator.py происходит импорт ResourceManager, в котором происходит импорт нашего Action.Reset, в котором происходит импорт функции перезапуска из emulator.py. Возможным решением этой проблемы могло быть использование представленного API в emulator.py, но оно не работает.

Демонстрация операции, которая не поддерживается в сервер-эмуляторе. 

Демонстрация операции, которая не поддерживается в сервер-эмуляторе. 

Как исправить проблему: Необходимо произвести переработку архитектуры эмулятора, исправить неработающие функции и добавить новые. Например, изменение состояний работы дисков и эмуляцию их поломки. 

Нереализованная система ролей 

Подразумевается, что для работы с разными типами данных и ресурсами на уровне СХД — например, со Storage Pools или Volumes — важно определить права пользователей, в пределах которых они могли бы взаимодействовать с системой. Ключом к решению задачи служит распределение пользователей по определенным ролям. 

В спецификации Swordfish выделяется три основные роли: DevOps, StorageAdmin и CloudAdmin. У каждой есть спектр выполняемых задач:

  • Роль DevOps необходима, чтобы использовать конфигурации систем хранения данных для автоматизации и масштабирования бизнес-операций.

  • Роль StorageAdmin предназначена для управления и решения оперативных задач хранения, таких как планирование жизненного цикла хранилища и повседневное администрирование хранилища.

  • Роль CloudAdmin позволяет отвечать за управление всеми аспектами облачной инфраструктуры, включая хранилище.

Также некоторые операции могут выполнять пользователи с разными привилегиями. Например, присоединить существующее пространство имен могут пользователи с ролями CloudAdmin и StorageAdmin. Стоит отметить, что для управления пользователями и распределением ролей уже существует особый вид роли из Redfish — Administrator. В итоге у нас четыре роли, права которых нужно реализовать.

Теперь посмотрим, как это сделать на практике.

Реализация многоролевой модели в сервер-эмуляторе Swordfish

Для упрощения решения задачи ее можно разбить на три этапа:  

  1. Тестовая проверка ролей и пользователей на уровне эмулятора, то есть все роли, пользователи и пароли берутся из кода Swordfish-эмулятора.

  2. Реализация более адекватной версии со считыванием данных из ресурсов эмулятора и их непосредственное использование.

  3. Реализация соответствия разделения ролей спецификации Swordfish.

Рассмотрим каждый в отдельности. 

Этап 1. Статическая версия пользователя и роли

В исходном коде эмулятора существует класс AccountService, где распределены пользователи, пароли и роли. Важно, что в коде присутствует следующий комментарий от разработчиков:  

# Redfish Emulator Role Service. 
# Temporary version, to be removed when AccountService goes dynamic

То есть подразумевается, что это лишь «заглушка», которая справляется с ролью «костыля» до момента реализации динамической версии. Добавив и реализовав в скрипте utils.py новую функцию check_role, а также использовав ее в скрипте Connections_api.py, нам удалось интегрировать проверку ролей. 

Статическая реализация ролевой модели.

Статическая реализация ролевой модели.

На скрине отмечена следующая информация:

  • 1 — адрес ресурса, доступ к которому хочет получить пользователь.

  • 2 — логин и пароль пользователя.

  • 3 — ответ сервер-эмулятора на наш запрос.

Запрос на получение доступа к ресурсам не прошел из-за отсутствия необходимых прав у роли пользователя. Получается, у пользователя нет доступа к ресурсам и система распределения ролей работает. Но такая версия нас не устраивает. 

Как добавлять новые роли и их права? А как добавлять новых пользователей и сразу переходить на их аккаунты и взаимодействовать с ресурсами? Это мы сделаем на втором этапе, где рассмотрим динамическую реализацию пользователей и ролей, то есть основанную на использовании ресурсов эмулятора.

Этап 2. Динамическая версия пользователя и роли

Для реализации динамической версии предлагаем создать следующую структуру взаимодействия:

  1. При запуске эмулятора происходит считывание статических данных существующих пользователей из директории Resources, где находятся Accounts и Roles. 

  2. Считанные данные используются на уровне эмулятора, и с их помощью пользователь может войти в систему под любыми логином и паролем, существующими в Resources.

  3. Готово! Пользователь может взаимодействовать с СХД/эмулятором и применять те методы и функции, которые соответствуют его роли.

Важно: доступ к ресурсам Accounts и Roles будет лишь у самой привилегированной роли — Administrator. Также будет безопаснее ограничить количество пользователей с этой ролью до одного.

Этап 3. Приведение динамической версии под спецификацию Swordfish

После реализации второго этапа останется реализовать следующие пункты:  

  • настроить правильное создание ролей и аккаунтов,

  • настроить правильное использование свойств той или иной роли,  

  • протестировать каждый метод, описанный в спецификации Swordfish, на соответствие ролей. 

В итоге вы получите полностью реализованные аккаунты и роли в соответствии со спецификацией Swordfish — там самая динамическая версия AccountService. Доказать полноту решения можно путем создания новых аккаунтов с разными ролями, после чего использовать эти аккаунты для взаимодействия с эмулятором, применяя описанные в Swordfish User Guide методы. 

По большей части речь идет о работе с предшественником Swordfish — Redfish, так как именно в этой спецификации в полной мере раскрываются детали реализации AccountService и ее функций. Из-за недостатков эмуляторов нужно сначала доработать динамическую реализацию AccountService эмулятора Redfish, после чего создавать и распределять роли спецификации Swordfish.

Что можно сделать еще

Вы можете интегрировать новые специфические роли для решения задач в рамках определенной СХД, а также ввести более совершенные методы верификации пользователей. Также рекомендуем обезопасить аккаунт главного администратора для минимизации риска взломов аккаунтов и последующих несанкционированных действий. Это можно реализовать введением двухфакторной аутентификации или с помощью YubiKey. 

Вместо заключения

В статье мы разобрали особенности и проблемы Swordfish API Emulator: разобрались с устройством и научились с ним взаимодействовать. Также подробно рассказали, как можно реализовать систему ролей в эмуляторе. В будущем наша команда исправит проблемы, выявленные во время эксплуатации эмулятора, и перенесет его на Go. Это позволит избавиться от зависимости от Redfish Interface Emulator.

Если интересно прочитать продолжение, пишите в комментариях!

Источники

© Habrahabr.ru