Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
В этой статье я расскажу об инфраструктуре нашего проекта для запуска REST API автотестов для сервисов Open Telekom Cloud. Акцент делается на Python и Robot Framework составляющей, с небольшим обзором инфраструктуры проекта (Jenkins/Gitlab/InfluxDB/Grafana). Статья описывает один из многочисленных способов настройки окружения, на котором запускаются автотесты (внутри нашего проекта есть 3 инфраструктуры с разным способом запуска, обработки и выгрузки результатов тестов - одна из них описана в этой статье).
Информация, изложенная в этой статье, может быть полезна как для тестировщиков-автоматизаторов, так и для DevOps-инженеров, у которых есть необходимость в создании собственного окружения для автотестов. Если вам просто интересно, как работает автоматическая прогонка тестов в большом проекте - тогда вам тоже сюда. Сразу оговорюсь, что текущая инфраструктура справляется со своими задачами тестирования API запросов, но может не подойти для мобильного тестирования / проведения нагрузочных тестов или проч. Если у вас предложения, по улучшению - буду рад увидеть их в комментариях.
Open Telekom Cloud
Open Telekom Cloud (далее - OTC) - самый крупный европейский публичный облачный сервис основанный на Openstack. Платформа создана для компаний и стартапов, которые работают с европейскими пользователями, чьи данные должны храниться в пределах Евросоюза. Более подробную информацию об облаке можно найти по ссылкам:
Видео об Open Telekom Cloud
Главная страница Open Telekom Cloud
Статья об управлении структурой Open Telekom Cloud
По структуре сервисов и API запросам он больше всего похож на Amazon Web Service / Huawei Cloud / SberCloud.
Мое знакомство с облачным провайдером Open Telekom Cloud началось более года назад. До этого опыта в автомазированном тестировании я не имел, так что изначально все взаимодействие ограничивалось Robot Framework’ом и написанием keyword-driven API тестов с использованием кастомной библиотеки для Open Telekom Cloud. Со временем стали появляться новые задачи, которые требовали более глубокого понимания нашей среды, в первую очередь, касающиеся Python, настройки Jenkins и CI/CD Gitlab’а.
Почему Robot Framework?
Robot framework (RF) – это open source keyword-driven фреймворк на основе Python, разработанный для автоматизации тестирования.
В контексте нашего проекта я хочу выделить три основные причины использования Robot Framework в качестве основного инструмента для написания автотестов:
Отчеты RF
Отчеты в Robot Framework генерируются автоматически в процессе работы. По итогам получаем 3 файла с результатами тестов – output.xml (сводка по тестам, которую в дальнейшем можно парсить командой rebot), report.html (общая сводка по тестам) и log.html (подробные результаты по каждому тесту).
Детальный отчет можно получить, добавив аргумент -b и название файла (в который должен сохраняться отчет) в команде запуска robot:
~$ robot -b debug.log test_suite.robot
Стандартные репорты robot’а достаточно ясные, и даже человек, не связанный с тестированием, поймет в какой момент в тест сьюте что-то сломалось. Также это наглядный инструмент для менеджмента, который позволяет сразу оценить общее количество тестов и процент успешного выполнения.
Проблемы могут возникнуть на этапе воспроизведения неудавшихся тестов. Debug логи robot тестов не сохраняются на нодах Jenkins из-за соображения безопасности, а сам robot плохо оптимизирован для отладки. Но, как показывает практика, большая часть результатов тестов воспроизводится.
RF прост для понимания
Благодаря простому синтаксису ключевых слов в библиотеках RF большинство тестов можно понять без глубокого погружения в тест-кейс. Также для написания тестов на RF не требуется знание ООП и умения программирования на Python. Нужно лишь подключить необходимые библиотеки, для которых достаточно понимания базовых концептов типа списков, словарей, объектов.
Пример теста с ключевыми словами встроенной библиотеки RF:
*** Settings ***
Library String
*** Variables ***
${var} world
*** Keywords ***
My log uppercase keyword
${uvar} = Convert to Uppercase ${var}
Log To Console Hello ${uvar}
*** Test Cases ***
Sample Testcase
My log uppercase keyword
Синтаксис ключевых слов библиотек OTC тоже стараемся поддерживать максимально простым и понятным.
В целом это упрощает порог вхождения для новых сотрудников, которые у нас регулярно появляются, так как команда активно растет и всегда есть открытые позиции в тест автоматизации. Новому сотруднику не обязательно иметь опыт в автоматизации и ООП. Для начала ему хватит RF, с помощью которого можно без проблем покрыть сервисы тестами. Если же ставятся более серьезные задачи, или тест сьюты в RF становятся слишком большими и сложными — значит пришло время углубиться в Python и дописать модуль / ключевые слова для своего сервиса.
RF поддерживает теги
Теги позволяют разбить запуск тестов для разных окружений. На практике это означает, что внутри одного тест сьюта с помощью тегов можно отметить, какие тесты должны запускаться на каком окружении.
Теги могут быть добавлены в разделе Settings robot framework’а:
*** Setting ***
Force Tags status:stable
... release:OTC_2.1
... importance:critical
... frequency:hourly
... region:prod_eu-de
Такой вариант добавляет теги для всех тест-кейсов.
Либо можно отдельно добавить теги для тест-кейса:
*** Setting ***
*** Keywords ***
*** Variables ***
*** Test Cases ***
Example-Test-Case
[Tags] status:stable
... release:OTC_2.1
... importance:critical
... frequency:hourly
... region:prod_eu-de
Далее запуская тесты для определенного региона или настраивая Jenkins/Gitlab, мы явно указываем тесты с каким тегом должны быть запущены:
~$ robot -i region:prod_eu-de test_suite.robot
Инфраструктура проекта
Запуск автотестов нашего проекта происходит на виртуальных машинах Open Telekom Cloud с помощью Jenkins. В нашей конфигурации Jenkins есть два вида задач – hourly и nightly – задачи запускаемые на нодах Jenkins каждый час/ночь, в зависимости от проставленного в Robot тега (если тест сьют требует более 5-10 минут на исполнение, то тег ставится nightly).
Ниже приведен процесс автоматизации наших тестов:
Перед запуском очередной задачи на ноде, Jenkins обновляет репозиторий Gitlab, в котором хранятся тесты Robot Framework. После чего происходит запуск тестов.
Часть результатов мы получаем во время исполнения тестов RF (обычно это тесты на производительность сервисов), они парсятся через RF и отправляется curl’ом в InfluxDB и Grafana. По завершению всех тестов результаты (а именно output.xml) парсятся уже через Jenkins и выгружаются в Grafana и Web Reports. В итоге имеем следующий результат:
Главная страничка Web reports отображает статистику по всем окружениям, в которых запускаются тесты. Нажав на одно из них, мы проваливаемся в report.html и можем посмотреть детальное исполнение тестов.
Автоматизированные тесты в OTC и задача QA
Мы занимаемся тестированием REST API и написанием соответствующих автотестов для сервисов Open Telekom Cloud. С точки зрения тестирования – это основная задача QA, хотя RF позволяет писать UI/UX тесты (с помощью Selenium), тесты на производительность сервисов. В основе RF лежит Python, поэтому возможности ограничены лишь теми библиотеками, которые вы подключаете к RF.
Для каждого сервиса OTC есть API документация, которая содержит в себе описание возможных REST запросов, связанных с сервисом. Это основной документ, с которым работают и на который ориентируются инженеры при создании автотестов:
Пример Get запроса для сервиса CBR (Cloud backup and recovery):
GET http://{cbr_endpoint_url}/v3/{project_id}/operation-logs/{operation_log_id}
Соответствующий запрос написанный в RF с использованием внутренних библиотек будет выглядеть следующим образом:
*** Test Cases***
Get operation log ID
Use service cbr
${resp} = Get Request /operation-logs/{operatiom_log_id}
Базовый тест сьют обычно имеет следующую структуру: создание объекта сервиса - настройка / изменение объекта сервиса - удаление объекта сервиса. Часто для создания какого-то сервиса требуется предварительное создание и настройка другого сервиса. Например, до создания виртуальной машины требуется создать виртуальную сеть и секьюрити группу.
Robot Framework
Для работы с RF не требуется знание Python, достаточно использовать заложенные в него ключевые слова и внешние библиотеки. В нашем проекте используется следующий набор библиотек для RF:
При подключении OTC Library в тест сьют и дальнейшем запуске в первую очередь через Openstack sdk и OpenStackConfig (openstack.config.loader) создается сессия. Авторизация сессии происходит двумя способами:
clouds.yaml
OpenStack sdk самостоятельно ищет файл для авторизации в следующих местах:
system-wide (/etc/openstack/{clouds,secure}.yaml)
Home directory / user space (~/.config/openstack/{clouds,secure}.yaml)
Current directory (./{clouds,secure}.yaml)
clouds:
otc:
profile: otc
auth:
username: '<USER_NAME>'
password: ''
project_name: '<eu-de_project>'
# or project_id: '<123456_PROJECT_ID>'
user_domain_name: 'OTC00000000001000000xxx'
# or user_domain_id: '<123456_DOMAIN_ID>'
account_key: '<AK_VALUE>' # AK/SK pair for access to OBS
secret_key: '<SK_VALUE>
После того, как файл создан, нужно указать имя конфигурации для переменной окружения OS_CLOUD:
~$ export OS_CLOUD=otc
Переменные окружения
В случае, если OpenStack sdk не обнаруживает переменную OS_CLOUD и/или файл clouds.yaml, он проверяет переменные окружения. Авторизация через переменные окружение может осуществляться вручную или через файл (например.ostackrc):
# .ostackrc file
export OS_USERNAME="<USER_NAME>"
export OS_USER_DOMAIN_NAME=<OTC00000000001000000XYZ>
export OS_PASSWORD=<PASSWORD> # optional
export OS_TENANT_NAME=eu-de
export OS_PROJECT_NAME=<eu-de_PROJECT_NAME>
export OS_AUTH_URL=https://iam.eu-de.otc.t-systems.com:443/v3
export NOVA_ENDPOINT_TYPE=publicURL
export OS_ENDPOINT_TYPE=publicURL
export CINDER_ENDPOINT_TYPE=publicURL
export OS_VOLUME_API_VERSION=2
export OS_IDENTITY_API_VERSION=3
export OS_IMAGE_API_VERSION=2
Добавляем переменные в окружение с помощью
~$ source .ostackrc
После чего можно использовать OTC Library.
Далее есть два варианта построения тестов – с использованием модулей сервисов (библиотека ключевых слов, заранее написанного для сервиса на python, например OtcLibrary.evs_robot), либо прописывать вручную все API запросы и последующие проверки статус кода и тела ответа.
Базовый вариант (без модулей) тест сьюта выглядит следующим образом:
*** Settings ***
Library OtcLibrary
*** Test Cases ***
Check EVS disk type
Use service evs
${resp} = Get Request /volumes/ 4d7c7b9d-1b70-40a8-8dd5-edd86eb08154
Should Be Equal As Strings ${resp.status_code} 200
Should Be True “${resp.json()[“volume”][“volume_type”]}” == “SATA”
Команда “Use service” запрашивает endpoint сервиса, который далее подставляется в последующие запросы. Тело ответа и статус код содержится в переменной ${resp}, над которой дальше проводят всевозможные проверки.
Также OtcLibrary содержит в себе основные ключевые слова для работы с проектом и регионами – вот часть из них:
Get ProjectId
Get ProjectName
Get Region
Get DomainId
…
Основой OtcLibrary является Python библиотека RequestsLibrary, так что структура запросов и переменной с ответом сервера почти ничем не отличаются.
Также использование RequestsLibrary позволяет создание нескольких сессий с легким переключением между ними.
Для основных сервисов OTC написаны модули с набором ключевых слов. Как правило внутри ключевого слова уже совершаются проверки статус кода и полей в ответе запроса, итоговый вид тест сьюта менее загруженный:
*** Settings ***
Library OtcLibrary
Library OtcLibrary.evs_robot
*** Test Cases ***
Check EVS disk type
${resp}= Query EVS disk diskname
Should Be True “${resp[“volume_type”]}” == “SATA”
В данном примере метод Query EVS disk описан в модуле OtcLibrary.evs_robot и содержит отправку запроса, а также проверку кода статуса ответа следующего вида:
def query_evs_disk(self, name=None):
'''query details of an EVS disk'''
if name:
...
response = self._session.get(self._url + '/cloudvolumes/detail', params=params)
status_code_should_be(response, 200)
return response.json()
else:
raise TypeError(name + '() needs name as an argument')
Далее работа идет с телом ответа в json формате.
Для робота создано большое количество комьюнити библиотек, что расширяет возможности тестирования и позволяет создавать не только функциональные тесты. Например, комбинация SSHLibrary и модулей сервиса ecs позволяет создать VM и подключиться через ssh с дальнейшим запуском команд на VM.
Таким образом у нас настроены тесты на производительность виртуальных жестких дисков (SATA, SSD и проч.) через утилиту fio, после пробега команд и последующего парсинга, результат отправляется API запросами на Influx и отображается на Grafana.
В принципе, таким образом можно автоматизировать почти любой ручной тест. Например, рассмотрим тесты для сервиса s3, который используется для хранения и доступа данных в "бакетах" - виртуальных контейнерах облачного сервиса. В качестве инструментов тестирования используется утилита s3tester и obsutil. Все действия выполняются через ключевые слова RF, порядок работы в данном случае следующий:
создание VM и s3 контейнера;
удаленное подключение к VM;
загрузка s3tester/obsutil на VM, изменение прав доступа к файлам;
запуск команд s3tester/obsutil с дальнейшей выгрузкой на сервер с Grafana.
В данном случае, чтобы не загромождать тест-кейс, сделано разделение по ресурсам:
obs/
Resources/
keywords.robot
settings.robot
setup_and_teardown.robot
variables.robot
OBS_Performance_obsutil.robot
OBS_Performance_s3tester.robot
Файл OBS_Performance_obsutil.robot:
*** Settings ***
Documentation OBS Performance Testing- Obsutil
Suite Setup Setup Suite
Suite Teardown Teardonw Suite
Resource ${CURDIR}/Resources/settings.robot
#путь к файлам, в которых находятся ключевые слова тест-кейса
*** Test Cases ***
Start test
Start Test Keyword
Set Suite Variable ${ecs_type} ${ecs_type2}
Create KeyPair
Create KeyPair Keyword
OBS Performance test - az1
Preparing testing ECS Keyword ${AZ-1} s3.2xlarge.1
#создание VM
BuiltIn.Sleep 15
Get EIP Keyword
Open Connection And Login With Public Key Keyword
#подключение к VM по ssh
Configure Ecs
#загрузка obsutil на VM, изменение прав доступа к файлам
Set Up Configuration File
Execute Performance Tests Obsutil ${AZ-1}
# запуск тестов obsutil в выбранной availability zone,
# парсинг и выгрузка в Grafana
Delete ECS ${servername}
Wait for ECS job completion
Run Keyword and Ignore Error
Release EIP
${hostip}
...
Тесты, исполняемые на VM, подключаются через Resource и содержатся в keywords.robot. Они, так же как и основной тест-кейс, представляют из себя набор ключевых слов, в основе которого стоят команды типа:
${output}= Execute Command ./obsutil cp obs://${bucket_name}/upload ~/download
запускающие утилиту obsutil/s3tester непосредственно на VM через SSHLibarary robot framework’а.
После этого результаты парсятся и выгружаются в InfluxDB следующей командой:
${output}= Execute Command curl -XPOST -u "${login}:${password}" '${influx_url}?db=${database}' --data-binary 'storage,env=${env},az=${region}-${AZ},request_type=${upload_type},size=${size} download=${download},upload=${upload}
В итоге данные отображаются в Grafana:
Python
Хорошо, с роботом разобрались, но как реализована кастомная библиотека и модули?
Часть, связанная с python, включает в себя две основных библиотеки – OTCLibrary (конечной библиотекой, с которой работают QA) и otcmodules, подключаемой к OTCLibrary. Otcmodules представляют собой набор классов с низкоуровневой логикой отправки запросов для сервисов OTC.
Otcmodules
Otcmodules были отделены от основной библиотеки, чтобы не загружать ее. Для основных сервисов прописаны классы с инициализацией сессии отдельного класса и соответствующие методы, каждый из которых представляет задокументированный API запрос. В методах происходит проверка передаваемых параметров запроса, логические действия и создание недостающих параметров перед отправкой самого запроса. Каждый метод класса заканчивается отправкой REST API и проверкой статус кода:
def getdisks(self, name=None, status=None):
'''get details for EVS disks'''
params = {}
if name:
params['name'] = name
if status:
params['status'] = status
response = self._session.get(self._url + '/cloudvolumes/detail', params=params)
status_code_should_be(response, 200)
return response.json()
Для инициализации самого класса требуется передать объект авторизованной сессии, service endpoint и project id:
class EVS(OtcTags):
'''EVS API'''
def __init__(self, session, baseurl, projectid):
'''save session and endpoint URL'''
self._session = session
self._session.headers.update({'Content-Type': 'application/json', 'X-Language': 'en-us'})
self._url = baseurl+'/v2/'+projectid
self._tagsurl = baseurl+'/v3/'+projectid
...
def getavailabilityzones(self):
'''Get List of Availability Zones'''
response = self._session.get(self._url + '/os-availability-zone')
status_code_should_be(response, 200)
return response.json()
Для проверки работы классов и методов otcmodules созданы юнит-тесты. Не вдаваясь в подробности того, как происходит авторизация, привожу конечную реализацию:
import unittest
import os
import sys
import requests
import random
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
path = os.path.dirname(os.path.dirname(__file__))
sys.path.append(path)
from otcmodules.utils.defaults.common import Common
from otcmodules.services.evs import EVS
SERVICENAME_EVS = 'volume'
class EVSTests(unittest.TestCase):
'''EVS testcases'''
def setUp(self):
'''prepare EVS objects'''
self.evs = Common._create_service_instance(EVS, SERVICENAME_EVS)
def tearDown(self):
'''remove EVS disk'''
self.evsdisk.delete_no_wait()
def testEVSObject(self):
'''run basic tests on EVS object'''
self.assertIsNotNone(self.evs._url)
self.assertIsNotNone(self.evs._urlv1)
def testDiskLifeCycle(self):
'''test the disk life cycle (create, get, expand, delete)'''
rid = format(random.randint(1, 2 ** 16), '04x')
diskname = 'testdisk-' + rid
# Create
self.evs.createdisk(diskname, 'eu-de-01', '10')
r = self.evs.waitforjob()
self.assertTrue(r)
job = self.evs.getjob()
self.assertIn('volume_id', job['entities'])
diskid = job['entities']['volume_id']
# Get (by id)
disk = self.evs.getdiskbyid(diskid)
self.assertIsNotNone(disk)
self.assertIn(disk['id'], diskid)
self.assertEqual(disk['name'], diskname)
# Expand
self.evs.expanddisk(disk['id'], 20)
r = self.evs.waitforjob()
self.assertTrue(r)
# Delete
self.evs.deletedisk(disk['id'])
r = self.evs.waitforjob()
self.assertTrue(r)
if __name__ == '__main__':
unittest.main()
OTC Library
OTC Library содержит в себе законченные модули с набором ключевых слов сервисов, данные модули уже готовы к инициализации и запуску в роботе QA-инженерами.
Структура OTCLibrary:
robot-otc/
OTCLibrary/
core/
__init__.py
cloudconnection.py
OtcKeywords.py
OtcRequestsLibrary.py
__init__.py
bms_robot.py
cbr_robot_ext.py
css_robot.py
cts_robot.py
dcaas_robot.py
evs_robot.py
...
setup.py
Авторизация
Основная сессия создается с помощью OpenStackConfig Openstack SDK (openstack.config.loader) и метода get_one(). По факту информация считывается из переменных окружения или файла clouds.yaml:
from openstack.config.loader import OpenStackConfig
...
def applyConfig(func):
config = OpenStackConfig()
s_data = config.get_one(cloud=cloud)
authdata = s_data.get_auth_args()
for key, value in authdata.items():
if value:
kwargs.setdefault(key, value)
kwargs.setdefault('domain_name', authdata.get('user_domain_name'))
kwargs.setdefault('region_name', config.region_name)
...
cloud – конфигурация в файле clouds.yaml. Если cloud указан, то OpenStackConfig будет загружать сессию указанной конфигурации. В случае, если cloud не указан или clouds.yaml не найден, будут подгружены переменные окружения.
Далее данные авторизации передаются через декоратор @applyconfig в init_cloud_session(), используемый при инициации библиотеки. Создается объект с авторизованной сессией:
class OtcKeywords(OtcRequestsLibrary):
def __init__(self,
initsession=True,
debug=0,
scope='project',
**kwargs):
super(OtcKeywords, self).__init__()
self.ROBOT_LIBRARY_LISTENER = self
self.lasthttperror=None
self.sessions={}
self.auth_url=None
self.project_name=None
self.https_insecure=None
if initsession:
self.init_cloud_session(
debug=debug,
scope=scope,
**kwargs)
@applyConfig
def init_cloud_session(self,
alias='default',
scope='project',
debug=0,
username=None,
password=None,
domain_name=None,
region_name=None,
auth_url=None,
project_name=None,
https_insecure=None,
proxies=None,
**kwargs):
"""Create a ready-to-use, configured and authenticated session"""
if username is None:
raise TypeError('No username was set!)
if password is None:
raise TypeError('No password was set!)
s = self.create_session(alias=alias,proxies=proxies,
verify=not https_insecure,
debug=debug)
project_name = project_name or region_name
iamsession = iam(s, auth_url)
authdata = {
'username': username,
'password': password,
'domain_name': domain_name,
'region_name': region_name,
'project_name': project_name,
'scope': scope
}
self.auth_url = auth_url
self.project_name = project_name
self.https_insecure = https_insecure
iamsession.authenticate(**authdata)
dnsdomain = ".".join(urlparse(auth_url).netloc.split(".")[1:])
self.dnsdomain = dnsdomain
self.sessions[alias]={'iam': iamsession, 'dnsdomain': dnsdomain}
При этом объект сессии будет храниться в OtcKeywords.sessions.
OtcRequestsLibrary, от которого наследует OtcKeywords – это класс, который в свою очередь наследован от стандартной библиотеки RequestsLibrary с небольшими изменениями под наш облачный сервис. Благодаря этому и методу use_service в OtcKeywords (который просто переключает url сессии на endpoint сервиса, переданный методу) при подключении OtcLibrary endpoint можно явно не указывать.
Помимо этого, класс OtcKeywords содержит методы для работы с сессией, получения service endpoint и project id – для дальнейшего подключения модулей сервисов из otcmodules.
OTCLibrary написана достаточно гибко и позволяет помимо подключения otcmodules использовать модули, написанные на Openstack SDK, например otcextensions (расширение Openstack SDK, включающие сервисы, предоставляемые OTC). Чтобы не запутаться в созданных сессиях и подключенных модулях, проверки инициализованных сессий и действия с сессией вынесены в файл cloudconnection.py с классом cloudconnection:
class cloudconnection(object):
'''Provides the basic information for cloud requests'''
__instance = None
def __new__(cls):
if cloudconnection.__instance is None:
cloudconnection.__instance = object.__new__(cls)
return cloudconnection.__instance
def __init__(self):
try:
self.otclib = BuiltIn().get_library_instance(OTCROBOTLIB)
except RobotNotRunningError:
from OtcLibrary import OtcLibrary
self.otclib = OtcLibrary()
def getSession(self):
'''get the session object for requests'''
try:
return self.otclib.getSession()
except:
self.otclib.init_cloud_session()
return self.otclib.getSession()
def getEndpointURL(self, service):
'''get the endpoint URL for a cloud service'''
return self.otclib.get_endpoint_url(service)
def getProjectId(self):
'''get the project id from the cloud connection'''
return self.otclib.get_projectid()
...
def getOpenstackConnection(self):
'''Get Openstack Connection object'''
return self.otclib._openstack_connection()
def getOtcextensionsConnection(self, **kwargs):
'''Get Openstack Connection object'''
return self.otclib._otcextensions_connection(**kwargs)
В итоге сами модули робота сервисов инициализируются через cloudconnection() и выглядят следующим образом:
Модули OTCLibrary основанные на otcmodules
evs_robot.py
from otcmodules.services.evs import EVS
from OtcLibrary.core.cloudconnection import cloudconnection
...
class evs_robot():
''' robot library to access the OTC elastic volume service'''
ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
SERVICENAME = 'evs'
default_availability_zone = property(lambda self: 'eu-de-01', None)
def __init__(self):
'''setup session, base URL, and project Id for EVS'''
self.evs = EVS(cloudconnection().getSession(), cloudconnection().getBaseEndpointURL(evs_robot.SERVICENAME),
cloudconnection().getProjectId())
def create_evs_disk(self, name, availability_zone=None, size=None, backupid=None,
volume_type=None, description=None, imageRef=None,
count=1, encryption=False, cmkid=None, snapshotid=None, multiattach=False, hwpassthrough=None):
'''create an EVS disk'''
if volume_type is None:
volume_type = self._get_default_volume_type()
if backupid is None and size is None:
size = 10
# Forcing to Set AZ - Used for testing services on specific AZ
if 'SET_AZ' in os.environ:
availability_zone = os.environ['SET_AZ']
if availability_zone is None:
availability_zone = self._get_default_availability_zone(volume_type)
return self.evs.createdisk(name, availability_zone, size, backupid, volume_type, description, imageRef, count,
encryption, cmkid, snapshotid, multiattach, hwpassthrough)
def _get_default_availability_zone(self, volume_type):
...
def _get_default_volume_type(self):
...
Модули OTCLibrary основанные на otcextensions
cbr_robot_ext.py
from OtcLibrary.core.cloudconnection import cloudconnection
class cbr_robot_ext():
""" Robot Library to access the OTC Cloud Backup and recovery service """
def __init__(self, api_version='3'):
self.api_version = api_version
self.region = cloudconnection().getregion()
self.cbr = cloudconnection().getOtcextensionsConnection().cbr
def query_cbr_backups(self, **query):
...
В будущем планируется полный переход с otcmodules на otcextensions. В использовании otcextensions есть несколько плюсов:
при использовании otcextensions можно полностью отказаться от otcmodules
otcextensions – open-source проект. Для QA это означает большую вероятность выявления багов в процессе использования не только самими QA, но так же пользователями и коллегами из других команд. Текущий же проект является внутренним и используется только QA
в основе otcextensions используется Openstack SDK, для построения запросов используются базовые методы openstack типо _find(), _get(), _delete() и проч. Разрабатывая модули по новой документации с использованием базовых методов openstack, QA легче находить ошибки документации, ошибки в логике API и тем самым делать облачный сервис совместимым с openstack
Итоги
Текущая инфраструктура позволяет максимально быстро вовлечь нового QA инженера в работу. В начале «трудовой деятельности» каждого QA-инженера создается VM с предустановленными библиотеками Otcmodules и OTC Library. Деплой и настройка происходит с помощью ansible playbook, в основе которого используется коллекция openstack.cloud. Далее настраивается синхронизация тестов с VM, в нашем случае на Pycharm. Осталось заполнить переменные окружения – и все готово! Можно приступать к написанию и запуску тестов.
Таким образом robot framework подходит для большого проекта, в котором нужно быстро ввести в работу QA инженера - независимо от его предыдущего опыта в автоматизации.
Ссылки
Open Telekom Cloud front page: https://open-telekom-cloud.com/en
Open Telekom cloud video: https://www.youtube.com/watch?v=ZXwMPsafSew
Open Telekom Cloud documentation: https://docs.otc.t-systems.com/
Robot framework main page: https://robotframework.org/
OTC Extensions repository: https://github.com/opentelekomcloud/python-otcextensions
Openstack SDK documentation: https://docs.openstack.org/openstacksdk/latest/
Openstack ansible collection: https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html