Start point of Senlin functional tests

Implements: blueprint functional-test-startpoint
Change-Id: I9856b37775b454795614dfe3915e0a048644351f
This commit is contained in:
yanyanhu 2015-07-31 05:34:43 -04:00
parent 9b1023ce72
commit 23b69e90b2
7 changed files with 267 additions and 2 deletions

View File

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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