Start point of Senlin functional tests
Implements: blueprint functional-test-startpoint Change-Id: I9856b37775b454795614dfe3915e0a048644351f
This commit is contained in:
parent
9b1023ce72
commit
23b69e90b2
|
@ -0,0 +1,38 @@
|
|||
# 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 os
|
||||
import testtools
|
||||
|
||||
from senlin.tests.functional.common import client
|
||||
|
||||
|
||||
class SenlinFunctionalTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SenlinFunctionalTest, self).setUp()
|
||||
self.username = os.getenv('OS_USERNAME')
|
||||
self.password = os.getenv('OS_PASSWORD')
|
||||
self.project_name = os.getenv('OS_TENANT_NAME')
|
||||
self.user_domain_name = os.getenv('OS_USER_DOMAIN_NAME',
|
||||
'Default')
|
||||
self.project_domain_name = os.getenv('OS_PROJECT_DOMAIN_NAME',
|
||||
'Default')
|
||||
self.region_name = os.getenv('OS_REGION_NAME', 'RegionOne')
|
||||
self.auth_url = os.getenv('OS_AUTH_URL')
|
||||
self.client = client.TestSenlinAPIClient(self.username,
|
||||
self.password,
|
||||
self.project_name,
|
||||
self.user_domain_name,
|
||||
self.project_domain_name,
|
||||
self.region_name,
|
||||
self.auth_url)
|
|
@ -0,0 +1,166 @@
|
|||
# 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 oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
import requests
|
||||
|
||||
from senlin.common.i18n import _
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIResponse(object):
|
||||
content = ""
|
||||
body = {}
|
||||
headers = {}
|
||||
|
||||
def __init__(self, response):
|
||||
super(APIResponse, self).__init__()
|
||||
self.status = response.status_code
|
||||
self.content = response.content
|
||||
if self.content:
|
||||
self.body = jsonutils.loads(self.content)
|
||||
self.headers = response.headers
|
||||
|
||||
def __str__(self):
|
||||
return "<Response body:%r, status_code:%s>" % (self.body, self.status)
|
||||
|
||||
|
||||
class SenlinApiException(Exception):
|
||||
def __init__(self, message=None, response=None):
|
||||
self.response = response
|
||||
if not message:
|
||||
message = 'Unspecified error'
|
||||
|
||||
if response is not None:
|
||||
message = _('%(msg)s\nStatus Code: %(status)s\nBody: %(body)s'
|
||||
) % {'msg': message, 'status': response.status_code,
|
||||
'body': response.content}
|
||||
|
||||
super(SenlinApiException, self).__init__(message)
|
||||
|
||||
|
||||
class TestSenlinAPIClient(object):
|
||||
"""Simple Senlin API Client"""
|
||||
|
||||
def __init__(self, auth_user, auth_key, auth_project, auth_user_domain,
|
||||
auth_project_domain, region, auth_url):
|
||||
|
||||
super(TestSenlinAPIClient, self).__init__()
|
||||
self.auth_result = None
|
||||
self.auth_user = auth_user
|
||||
self.auth_key = auth_key
|
||||
self.auth_project = auth_project
|
||||
self.auth_user_domain = auth_user_domain
|
||||
self.auth_project_domain = auth_project_domain
|
||||
self.region = region
|
||||
# We use keystone v3 API to do the functional test
|
||||
self.auth_url = auth_url.replace('v2.0', 'v3')
|
||||
self.catalogs = None
|
||||
|
||||
def request(self, url, method='GET', body=None, headers=None):
|
||||
_headers = {'Content-Type': 'application/json'}
|
||||
_headers.update(headers or {})
|
||||
|
||||
response = requests.request(method, url, data=body, headers=_headers)
|
||||
return response
|
||||
|
||||
def _authenticate(self):
|
||||
if self.auth_result:
|
||||
return self.auth_result
|
||||
|
||||
# We use password as the method and specify the scope when
|
||||
# performing authentication
|
||||
body = {
|
||||
'auth': {
|
||||
'identity': {
|
||||
'methods': ['password'],
|
||||
'password': {
|
||||
'user': {
|
||||
'domain': {
|
||||
'name': self.auth_user_domain,
|
||||
},
|
||||
'name': self.auth_user,
|
||||
'password': self.auth_key,
|
||||
}
|
||||
}
|
||||
},
|
||||
'scope': {
|
||||
'project': {
|
||||
'domain': {
|
||||
'name': self.auth_project_domain
|
||||
},
|
||||
"name": self.auth_project,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auth_url = self.auth_url + '/auth/tokens'
|
||||
response = self.request(auth_url, method='POST',
|
||||
body=jsonutils.dumps(body))
|
||||
|
||||
http_status = response.status_code
|
||||
LOG.debug(_('Doing authentication: auth_url %(auth_url)s, status '
|
||||
'%(http_status)s.'), {'auth_url': auth_url,
|
||||
'http_status': http_status})
|
||||
|
||||
if http_status == 401:
|
||||
raise Exception(_('Authentication failed: %s'), response._content)
|
||||
|
||||
self.auth_token = response.headers['x-subject-token']
|
||||
self.catalogs = jsonutils.loads(response._content)['token']['catalog']
|
||||
|
||||
return self.auth_token
|
||||
|
||||
def api_request(self, http_method, relative_url, body=None,
|
||||
expected_resp_status=None, **kwargs):
|
||||
token = self._authenticate()
|
||||
|
||||
endpoints = None
|
||||
for c in self.catalogs:
|
||||
if c['type'] == 'clustering':
|
||||
endpoints = c['endpoints']
|
||||
break
|
||||
if endpoints is None:
|
||||
raise Exception('Endpoints of clustering service was not found')
|
||||
|
||||
endpoint = None
|
||||
for e in endpoints:
|
||||
if e['interface'] == 'admin' and e['region'] == self.region:
|
||||
endpoint = e['url']
|
||||
break
|
||||
if endpoint is None:
|
||||
raise Exception(_('Admin endpoint of clustering service in '
|
||||
'%(region)s was not found.'
|
||||
), {'region': self.region})
|
||||
|
||||
full_url = '%s/%s' % (endpoint, relative_url)
|
||||
|
||||
headers = kwargs.setdefault('headers', {})
|
||||
headers['X-Auth-Token'] = token
|
||||
headers['Content-Type'] = 'application/json'
|
||||
kwargs['method'] = http_method
|
||||
|
||||
response = self.request(full_url, **kwargs)
|
||||
http_status = response.status_code
|
||||
LOG.debug(_('request url %(url)s, status_code %(status)s, response '
|
||||
'body %(body)s'
|
||||
), {'url': relative_url, 'status': http_status,
|
||||
'body': response._content})
|
||||
|
||||
if expected_resp_status:
|
||||
if http_status not in expected_resp_status:
|
||||
raise SenlinApiException(message="Unexpected status code",
|
||||
response=response)
|
||||
|
||||
return APIResponse(response)
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash -xe
|
||||
|
||||
# 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.
|
||||
|
||||
# This script is executed inside post_test_hook function in devstack gate.
|
||||
|
||||
export DEST=${DEST:-/opt/stack/new}
|
||||
export DEVSTACK_DIR=$DEST/devstack
|
||||
export SENLIN_DIR=$DEST/senlin
|
||||
|
||||
# TODO(Yanyan Hu): Do more preparation work which is necessary
|
||||
|
||||
# Run functional test
|
||||
source $DEVSTACK_DIR/openrc admin admin
|
||||
echo "Running Senlin functional test suite..."
|
||||
cd $SENLIN_DIR
|
||||
sudo -E tox -efunctional
|
|
@ -0,0 +1,29 @@
|
|||
# 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 oslo_log import log as logging
|
||||
|
||||
from senlin.tests.functional import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestCluster(base.SenlinFunctionalTest):
|
||||
|
||||
def test_get_clusters(self):
|
||||
# Check that listing clusters works.
|
||||
rel_url = 'clusters'
|
||||
status = [200]
|
||||
resp = self.client.api_request('GET', rel_url,
|
||||
expected_resp_status=status)
|
||||
clusters = resp.body['clusters']
|
||||
self.assertEqual([], clusters)
|
9
tox.ini
9
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py27,py34,pep8
|
||||
envlist = py27,py34,pep8,functional
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
|
@ -14,10 +14,15 @@ deps = -r{toxinidir}/test-requirements.txt
|
|||
|
||||
usedevelop = True
|
||||
install_command = pip install {opts} {packages}
|
||||
commands = ostestr {posargs}
|
||||
commands =
|
||||
ostestr --slowest --regex '^((?!tests\.functional).)+$' {posargs}
|
||||
|
||||
whitelist_externals = bash
|
||||
|
||||
[testenv:functional]
|
||||
commands =
|
||||
ostestr --slowest --concurrency 1 --regex '^(.*?tests\.functional)' {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
commands =
|
||||
flake8 senlin bin/senlin-api bin/senlin-engine bin/senlin-manage
|
||||
|
|
Loading…
Reference in New Issue