Implementation of Shipyard API functional tests

As part of this patchset we have implemented the following GET Shipyard 
API functional tests to ascertain based on some of the response 
parameters obtained.
We have also assumed that some of the create/POST operations are not 
performed yet which we will be taking in our next patchset

1. Test_actions
   list actions
   get action
   get action step
   
2. Airflow monitoring
   get workflow list
   get workflow
   
3. Document staging
   get collection list
   get collection
   get collection id
   get collection version buffer
   get collection version committed
   get collection version successful site action
   get collection version last site action
   get renderedconfigdocs
   get renderedconfigdocs version buffer
   get renderedconfigdocs version committed
   get renderedconfigdocs version successful site action
   get renderedconfigdocs version last site action
   get collection compare two revisions doc

4. Log retrieval
   get action step logs
   
5. Site Status
   get site statuses
   get site statuses node provision status
   get site statuses node power status
   get site statuses both filters together
   
   
Change-Id: Idd2ecaedf3c7e7fe07b68869e437b8d385cec8cd
This commit is contained in:
pradeepkumarks 2018-11-06 04:33:58 -06:00 committed by Pradeep Kumar
parent 7d1b41e644
commit cf034ee652
11 changed files with 390 additions and 20 deletions

View File

@ -44,7 +44,7 @@ def import_no_clients_in_api_tests(physical_line, filename):
"""Check for client imports from airship_tempest_plugin/tests/api
T102: Cannot import OpenStack python clients
"""
if "airship_tempest_plugin/tests/api" in filename:
if "airship_tempest_plugin/tests/api/" in filename:
res = PYTHON_CLIENT_RE.match(physical_line)
if res:
return (physical_line.find(res.group(1)),
@ -144,7 +144,7 @@ def no_rbac_rule_validation_decorator(physical_line, filename):
"""
global have_rbac_decorator
if ("airship_tempest_plugin/tests/api/rbac" in filename or
if ("airship_tempest_plugin/tests/api/" in filename or
"airship_tempest_plugin/tests/scenario" in filename):
if RULE_VALIDATION_DECORATOR.match(physical_line):
@ -163,7 +163,7 @@ def no_rbac_suffix_in_test_filename(filename):
"""Check that RBAC filenames end with "_rbac" suffix.
P101
"""
if "airship_tempest_plugin/tests/api" in filename:
if "airship_tempest_plugin/tests/api/" in filename:
if filename.endswith('rbac_base.py'):
return
@ -176,7 +176,7 @@ def no_rbac_test_suffix_in_test_class_name(physical_line, filename):
"""Check that RBAC class names end with "RbacTest"
P102
"""
if "airship_tempest_plugin/tests/api/rbac/" in filename:
if "airship_tempest_plugin/tests/api/" in filename:
if filename.endswith('rbac_base.py'):
return
@ -190,7 +190,7 @@ def no_client_alias_in_test_cases(logical_line, filename):
"""Check that test cases don't use "self.client" to define a client.
P103
"""
if "airship_tempest_plugin/tests/api" in filename:
if "airship_tempest_plugin/tests/api/" in filename:
if "self.client" in logical_line or "cls.client" in logical_line:
return 0, "Do not use 'self.client' as a service client alias"

View File

@ -22,6 +22,7 @@ from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
# NOTE(rb560u): The following will need to be rewritten in the future if
# functional testing is desired:
# - 'def create_action`
@ -37,7 +38,10 @@ class ActionsClient(rest_client.RestClient):
resp, body = self.get('actions')
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
if isinstance(body, list):
return rest_client.ResponseBodyList(resp, body)
else:
return rest_client.ResponseBody(resp, body)
def create_action(self, action=None):
url = 'actions'

View File

@ -19,7 +19,6 @@ http://airship-shipyard.readthedocs.io/en/latest/API.html#airflow-monitoring-api
"""
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
@ -30,7 +29,10 @@ class AirflowMonitoringClient(rest_client.RestClient):
resp, body = self.get('workflows')
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
if isinstance(body, list):
return rest_client.ResponseBodyList(resp, body)
else:
return rest_client.ResponseBody(resp, body)
def get_workflow(self, workflow_id=None):
resp, body = self.get('workflows/%s' % workflow_id)

View File

@ -19,9 +19,9 @@ http://airship-shipyard.readthedocs.io/en/latest/API.html#document-staging-api
"""
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
# NOTE(rb560u): The following will need to be rewritten in the future if
# functional testing is desired:
# - 'def create_configdocs`
@ -37,7 +37,10 @@ class DocumentStagingClient(rest_client.RestClient):
resp, body = self.get('configdocs')
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
if isinstance(body, list):
return rest_client.ResponseBodyList(resp, body)
else:
return rest_client.ResponseBody(resp, body)
def create_configdocs(self, collection_id=None):
url = "configdocs/%s" % collection_id
@ -52,13 +55,30 @@ class DocumentStagingClient(rest_client.RestClient):
resp, body = self.get('configdocs/%s' % collection_id)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
if isinstance(body, list):
return rest_client.ResponseBodyList(resp, body)
else:
return rest_client.ResponseBody(resp, body)
def get_configdocs_version(self, collection_id=None, version_arg=None):
resp, body = self.get('configdocs/%s?version=%s' %
(collection_id, version_arg))
self.expected_success(200, resp.status)
return rest_client.ResponseBodyData(resp, body)
def get_renderedconfigdocs(self):
resp, body = self.get('renderedconfigdocs')
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
if isinstance(body, list):
return rest_client.ResponseBodyList(resp, body)
else:
return rest_client.ResponseBody(resp, body)
def get_renderedconfigdocs_version(self, version_arg=None):
resp, body = self.get('renderedconfigdocs?version=%s' % version_arg)
self.expected_success(200, resp.status)
return rest_client.ResponseBodyData(resp, body)
def commit_configdocs(self, force=False, dryrun=False):
post_body = json.dumps({"force": force, "dryrun": dryrun})
@ -66,3 +86,12 @@ class DocumentStagingClient(rest_client.RestClient):
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def get_configdocs_compare_two(self, version_arg=None):
resp, body = self.get('configdocs?%s' % version_arg)
self.expected_success(200, resp.status)
body = json.loads(body)
if isinstance(body, list):
return rest_client.ResponseBodyList(resp, body)
else:
return rest_client.ResponseBody(resp, body)

View File

@ -18,8 +18,6 @@
http://airship-shipyard.readthedocs.io/en/latest/API.html#airflow-monitoring-api
"""
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
@ -30,5 +28,4 @@ class LogRetrievalClient(rest_client.RestClient):
resp, body = \
self.get('actions/%s/steps/%s/logs' % (action_id, step_id))
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
return rest_client.ResponseBodyData(resp, body)

View File

@ -19,8 +19,8 @@ https://github.com/openstack/airship-shipyard/blob/master/docs/source/API.rst#si
"""
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
from tempest.lib import decorators
class SiteStatusesClient(rest_client.RestClient):
@ -32,3 +32,8 @@ class SiteStatusesClient(rest_client.RestClient):
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def get_site_statuses_arg(self, args=None):
resp, body = self.get('site_statuses?filters=%s' % args)
self.expected_success(200, resp.status)
return rest_client.ResponseBodyData(resp, body)

View File

@ -15,17 +15,14 @@
#
from airship_tempest_plugin.tests.api.shipyard import base
from tempest.lib import decorators
class ActionsTest(base.BaseShipyardTest):
def _get_action_id(self):
resp = self.shipyard_actions_client.list_actions()
self.assertTrue(len(resp[1]) > 0,
'No actions available, nothing to test')
# get the response body
return resp[1]['id']
def _get_action_step_id(self):
@ -44,10 +41,18 @@ class ActionsTest(base.BaseShipyardTest):
@decorators.idempotent_id('b0d4c23a-d3a4-4a12-8e10-ac6f8a98d33e')
def test_get_action(self):
"""Get action detail against particular action_id,
Successful with response status 200
"""
action_id = self._get_action_id()
"""Get actions, Successful with response status 200"""
resp = self.shipyard_actions_client.get_action(action_id)
self.assertEqual(resp.response['status'], '200')
if ('command_audit' in resp) and (resp['command_audit']):
self.assertEqual(resp['command_audit'][0]['action_id'], action_id)
else:
raise 'response is missing `command_audit` or' + \
'`command_audit` is empty'
@decorators.idempotent_id('a8bc9e6b-bfa3-4635-a1ec-0b9ddc9cb03f')
def test_get_action_step(self):
@ -55,3 +60,4 @@ class ActionsTest(base.BaseShipyardTest):
action_id, step_id = self._get_action_step_id()
resp = self.shipyard_actions_client.get_action_step(action_id, step_id)
self.assertEqual(resp.response['status'], '200')
self.assertEqual(resp['task_id'], step_id)

View File

@ -0,0 +1,41 @@
# Copyright 2018 AT&T Corp
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from airship_tempest_plugin.tests.api.shipyard import base
from tempest.lib import decorators
class AirflowMonitoringTest(base.BaseShipyardTest):
def _get_workflows_id(self):
resp = self.shipyard_airflow_monitoring_client.list_workflows()
self.assertTrue(len(resp[0]) > 0,
'No configdocs available, nothing to test')
return resp[0]['workflow_id']
@decorators.idempotent_id('5a41bb54-c010-4d09-9d68-eece565e66f3')
def test_get_workflows_list(self):
"""Get list of workflows, Successful with response status 200"""
response = self.shipyard_airflow_monitoring_client.list_workflows()
self.assertEqual(response.response['status'], '200')
@decorators.idempotent_id('7e4fb56b-6637-48bf-808d-c166ee5f804f')
def test_get_workflow(self):
"""Get a particular workflow detail, Successful with response status 200"""
workflow_id = self._get_workflows_id()
response = self.shipyard_airflow_monitoring_client. \
get_workflow(workflow_id)
self.assertEqual(response.response['status'], '200')
self.assertEqual(response['workflow_id'], workflow_id)

View File

@ -0,0 +1,192 @@
# Copyright 2018 AT&T Corp
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import logging
from airship_tempest_plugin.tests.api.shipyard import base
from tempest.lib import decorators
from tempest.lib import exceptions
logger = logging.getLogger(__name__)
handler = logging.FileHandler('testLog/shipyard_api.log')
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(funcName)s - \
%(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
class DocumentStagingTest(base.BaseShipyardTest):
def _get_collection_id(self):
resp = self.shipyard_document_staging_client.get_configdocs_status()
self.assertTrue(len(resp[0]) > 0,
'No configdocs available, nothing to test')
return resp[0]['collection_name']
@decorators.idempotent_id('db351ee5-a608-4492-b705-6e6c654a60e7')
def test_get_collection_list(self):
"""Config docs status, Successful with response status 200"""
response = self.shipyard_document_staging_client. \
get_configdocs_status()
self.assertEqual(response.response['status'], '200')
@decorators.idempotent_id('743ab304-be35-4e45-ad47-e124dce3b9dc')
def test_get_collection(self):
"""Get Config docs for a particular collection, Successful with
response status 200.
In case the document does not found, return 404 Not Found
"""
collection_id = self._get_collection_id()
try:
response = self.shipyard_document_staging_client. \
get_configdocs(collection_id)
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response[0]) > 0,
'No configdocs available, nothing to test')
except exceptions.NotFound:
logger.info("The Shipyard buffer does not contain this collection")
pass
@decorators.idempotent_id('bd138747-582e-4c2e-9f26-09c3fccad96f')
def test_get_collection_version_buffer(self):
"""Get Config docs of buffer version,
Successful with response status 200
In case the document does not found, return 404 Not Found
"""
collection_id = self._get_collection_id()
try:
response = self.shipyard_document_staging_client. \
get_configdocs_version(collection_id, "buffer")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No configdocs of \
buffer version available')
except exceptions.NotFound:
logger.info("The Shipyard buffer does not contain this collection")
pass
@decorators.idempotent_id('d42c691a-07a2-41cc-91a6-9d02be8f2649')
def test_get_collection_version_committed(self):
"""Get Config docs of committed version,
Successful with response status 200
"""
collection_id = self._get_collection_id()
response = self.shipyard_document_staging_client. \
get_configdocs_version(collection_id, "committed")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No configdocs of \
committed version available')
@decorators.idempotent_id('2940492a-47b0-4218-859a-b9cb556f480b')
def test_get_collection_version_successful_site_action(self):
"""Get Config docs of successful_site_action version,
Successful with response status 200
"""
collection_id = self._get_collection_id()
response = self.shipyard_document_staging_client. \
get_configdocs_version(collection_id, "successful_site_action")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No configdocs of \
successful_site_action version available')
@decorators.idempotent_id('f6b86b85-5797-4664-95a9-0b6d242b50b7')
def test_get_collection_version_last_site_action(self):
"""Get Config docs of last_site_action,
Successful with response status 200
"""
collection_id = self._get_collection_id()
response = self.shipyard_document_staging_client. \
get_configdocs_version(collection_id, "last_site_action")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No configdocs of \
last_site_action version available')
@decorators.idempotent_id('2db31479-8d1f-4d95-a3a0-31b1bf726f39')
def test_get_renderedconfigdocs(self):
"""Get RenderedConfig docs, Successful with response status 200
In case the document does not found, return 404 Not Found
"""
try:
response = self.shipyard_document_staging_client. \
get_renderedconfigdocs()
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response[1]) > 0, 'No RenderedConfig docs available')
except exceptions.NotFound:
logger.info("buffer version does not exist")
pass
@decorators.idempotent_id('ffd9d1d9-9846-4493-9f9e-e35f12ce56da')
def test_get_renderedconfigdocs_version_buffer(self):
"""Get RenderedConfig docs of buffer version,
Successful with response status 200
In case the document does not found, return 404 Not Found
"""
try:
response = self.shipyard_document_staging_client. \
get_renderedconfigdocs_version("buffer")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No renderedconfigdocs of \
buffer version available')
except exceptions.NotFound:
logger.info("buffer version does not exist")
pass
@decorators.idempotent_id('353e9955-7f12-4561-a3c2-c9819f259775')
def test_get_renderedconfigdocs_version_committed(self):
"""Get RenderedConfig docs of committed version,
Successful with response status 200
"""
response = self.shipyard_document_staging_client. \
get_renderedconfigdocs_version("committed")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No renderedconfigdocs of \
committed version available')
@decorators.idempotent_id('3daac942-baec-4078-a632-71b66dca1e91')
def test_get_renderedconfigdocs_version_successful_site_action(self):
"""Get RenderedConfig docs of successful_site_action version,
Successful with response status 200
In case the document does not found, return 404 Not Found
"""
try:
response = self.shipyard_document_staging_client. \
get_renderedconfigdocs_version("successful_site_action")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No renderedconfigdocs of \
successful_site_action version available')
except exceptions.NotFound:
logger.info("This revision does not exist")
pass
@decorators.idempotent_id('9af56232-eacf-4baa-85e9-cbb93772974d')
def test_get_renderedconfigdocs_version_last_site_action(self):
"""Get RenderedConfig docs of last_site_action,
Successful with response status 200
"""
response = self.shipyard_document_staging_client. \
get_renderedconfigdocs_version("last_site_action")
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No renderedconfigdocs of \
last_site_action version available')
@decorators.idempotent_id('1aedf793-dde4-49df-a6d2-109bc11b6db5')
def test_get_collection_compare_two_revisions_doc(self):
"""Get Config docs of two versions and compare in between,
Successful with response status 200
"""
response = self.shipyard_document_staging_client. \
get_configdocs_compare_two("committed%2Cbuffer")
self.assertEqual(response.response['status'], '200')
self.assertEqual(response[0]['new_version'], 'buffer')
self.assertEqual(response[0]['base_version'], 'committed')

View File

@ -0,0 +1,35 @@
# Copyright 2018 AT&T Corp
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from airship_tempest_plugin.tests.api.shipyard import base
from tempest.lib import decorators
class LogRetrievalTest(base.BaseShipyardTest):
def _get_action_step_id(self):
resp = self.shipyard_actions_client.list_actions()
self.assertTrue(len(resp[1]) > 0,
'No actions available, nothing to test')
return resp[1]['id'], resp[1]['steps'][0]['id']
@decorators.idempotent_id('1a8e95fd-77cc-4576-99e1-4ebbb4d8469a')
def test_get_action_step_logs(self):
"""Get the actions step log, Successful with response status 200"""
action_id, step_id = self._get_action_step_id()
response = self.shipyard_log_retrieval_client. \
get_action_step_logs(action_id, step_id)
self.assertEqual(response.response['status'], '200')
self.assertTrue(len(response.data) > 0, 'No actions step log available')

View File

@ -0,0 +1,59 @@
# Copyright 2018 AT&T Corp
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from airship_tempest_plugin.tests.api.shipyard import base
from tempest.lib import decorators
class SiteStatusesTest(base.BaseShipyardTest):
@decorators.idempotent_id('c4c6fd0f-83dd-42ae-a4eb-430708490b71')
def test_get_site_statuses(self):
"""Get the Site status, Successful with response status 200"""
response = self.shipyard_site_statuses_client.get_site_statuses()
self.assertEqual(response.response['status'], '200')
self.assertEqual(response['nodes_provision_status'][0]['status'], 'Deployed')
self.assertEqual(response['machines_powerstate'][0]['power_state'], 'on')
@decorators.idempotent_id('9cfbf0b9-e826-4a9d-b1d2-055adaa8ecc2')
def test_get_site_statuses_node_provision_status(self):
"""Get the Site status of Node Provision and
Successful with response status 200
"""
response = self.shipyard_site_statuses_client. \
get_site_statuses_arg('nodes-provision-status')
self.assertEqual(response.response['status'], '200')
self.assertEqual(response['nodes_provision_status'][0]['status'], 'Deployed')
@decorators.idempotent_id('c329dbe8-4cb2-4a8b-9aa8-3a032c73102b')
def test_get_site_statuses_node_power_status(self):
"""Get the Site status of Node Power and
Successful with response status 200
"""
response = self.shipyard_site_statuses_client. \
get_site_statuses_arg('machines-power-state')
self.assertEqual(response.response['status'], '200')
self.assertEqual(response['machines_powerstate'][0]['power_state'], 'on')
@decorators.idempotent_id('52a87dde-1458-45fd-92c6-edcd93eb0c14')
def test_get_site_statuses_both_filters_together(self):
"""Get the Site status of both filters together and
Successful with response status 200
"""
response = self.shipyard_site_statuses_client. \
get_site_statuses_arg('nodes-provision-status%2Cmachines-power-state')
self.assertEqual(response.response['status'], '200')
self.assertEqual(response['nodes_provision_status'][0]['status'], 'Deployed')
self.assertEqual(response['machines_powerstate'][0]['power_state'], 'on')