
В этой статье я расскажу об инфраструктуре нашего проекта для запуска REST API автотестов для сервисов Open Telekom Cloud. Акцент делается на Python и Robot Framework составляющей, с небольшим обзором инфраструктуры проекта (Jenkins/Gitlab/InfluxDB/Grafana). Статья описывает один из многочисленных способов настройки окружения, на котором запускаются автотесты (внутри нашего проекта есть 3 инфраструктуры с разным способом запуска, обработки и выгрузки результатов тестов - одна из них описана в этой статье).
Информация, изложенная в этой статье, может быть полезна как для тестировщиков-автоматизаторов, так и для DevOps-инженеров, у которых есть необходимость в создании собственного окружения для автотестов. Если вам просто интересно, как работает автоматическая прогонка тестов в большом проекте - тогда вам тоже сюда. Сразу оговорюсь, что текущая инфраструктура справляется со своими задачами тестирования API запросов, но может не подойти для мобильного тестирования / проведения нагрузочных тестов или проч. Если у вас предложения, по улучшению - буду рад увидеть их в комментариях.
Open Telekom Cloud
Open Telekom Cloud (далее - OTC) - самый крупный европейский публичный облачный сервис основанный на Openstack. Платформа создана для компаний и стартапов, которые работают с европейскими пользователями, чьи данные должны храниться в пределах Евросоюза. Более подробную информацию об облаке можно найти по ссылкам:
По структуре сервисов и 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
