New integration tests - base functional

Features:
* create node group templates
* create cluster templates
* create clusters
* check cluster status
* default templates for hdp and vanilla plugins

Command for run tests:
$ tox -e scenario <path_to_yaml>

partially implements bp: scenario-integration-tests

Change-Id: Ief6aafff713520c972846aa45f0aaefd34ea1bd7
This commit is contained in:
Sergey Reshetnyak 2015-01-14 14:39:51 +03:00 committed by Vitaly Gridnev
parent e8c8880d35
commit 23e9aacad4
16 changed files with 531 additions and 0 deletions

View File

@ -48,6 +48,8 @@ include sahara/service/edp/resources/*.xml
include sahara/service/edp/resources/*.jar
include sahara/service/edp/resources/launch_command.py
include sahara/swift/resources/*.xml
include sahara/tests/scenario/testcase.py.mako
recursive-include sahara/tests/scenario/templates *.json
include sahara/tests/unit/plugins/vanilla/hadoop2/resources/*.txt
include sahara/tests/unit/plugins/mapr/utils/resources/*.topology
include sahara/tests/unit/plugins/mapr/utils/resources/*.json

View File

@ -0,0 +1,17 @@
credentials:
os_username: admin
os_password: nova
os_tenant: admin
os_auth_url: http://localhost:5000/v2.0
network:
private_network: private
public_network: public
clusters:
- plugin_name: vanilla
plugin_version: 2.6.0
image: sahara-juno-vanilla-2.6.0-ubuntu-14.04
- plugin_name: hdp
plugin_version: 2.0.6
image: f3c4a228-9ba4-41f1-b100-a0587689d4dd

View File

View File

@ -0,0 +1,196 @@
# Copyright (c) 2015 Mirantis Inc.
#
# 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 __future__ import print_function
import functools
import glob
import json
import os
import time
import fixtures
from oslo_utils import excutils
from tempest_lib import base
from tempest_lib import exceptions as exc
from sahara.tests.scenario import clients
from sahara.tests.scenario import utils
DEFAULT_TEMPLATES_PATH = (
'sahara/tests/scenario/templates/%(plugin_name)s/%(hadoop_version)s')
def errormsg(message):
def decorator(fct):
@functools.wraps(fct)
def wrapper(*args, **kwargs):
try:
return fct(*args, **kwargs)
except Exception:
with excutils.save_and_reraise_exception():
print(message)
return wrapper
return decorator
class BaseTestCase(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(BaseTestCase, cls).setUpClass()
cls.network = None
cls.credentials = None
cls.testcase = None
def setUp(self):
super(BaseTestCase, self).setUp()
self._init_clients()
self.plugin_opts = {
'plugin_name': self.testcase['plugin_name'],
'hadoop_version': self.testcase['plugin_version']
}
self.template_path = DEFAULT_TEMPLATES_PATH % self.plugin_opts
def _init_clients(self):
username = self.credentials['os_username']
password = self.credentials['os_password']
tenant_name = self.credentials['os_tenant']
auth_url = self.credentials['os_auth_url']
sahara_url = self.credentials['sahara_url']
self.sahara = clients.SaharaClient(username=username,
api_key=password,
project_name=tenant_name,
auth_url=auth_url,
sahara_url=sahara_url)
self.nova = clients.NovaClient(username=username,
api_key=password,
project_id=tenant_name,
auth_url=auth_url)
self.neutron = clients.NeutronClient(username=username,
password=password,
tenant_name=tenant_name,
auth_url=auth_url)
def create_cluster(self):
ngs = self._create_node_group_templates()
cl_tmpl_id = self._create_cluster_template(ngs)
cl_id = self._create_cluster(cl_tmpl_id)
self._poll_cluster_status(cl_id)
@errormsg("Create node group templates failed")
def _create_node_group_templates(self):
ng_id_map = {}
floating_ip_pool = None
if self.network['type'] == 'neutron':
floating_ip_pool = self.neutron.get_network_id(
self.network['public_network'])
elif not self.network['auto_assignment_floating_ip']:
floating_ip_pool = self.network['public_network']
node_groups = []
if self.testcase.get('node_group_templates'):
for ng in self.testcase['node_group_templates']:
node_groups.append(ng)
else:
templates_path = os.path.join(self.template_path,
'node_group_template_*.json')
for template_file in glob.glob(templates_path):
with open(template_file) as data:
node_groups.append(json.load(data))
for ng in node_groups:
kwargs = dict(ng)
kwargs.update(self.plugin_opts)
kwargs['name'] = utils.rand_name(kwargs['name'])
kwargs['floating_ip_pool'] = floating_ip_pool
ng_id = self.__create_node_group_template(**kwargs)
ng_id_map[ng['name']] = ng_id
return ng_id_map
@errormsg("Create cluster template failed")
def _create_cluster_template(self, node_groups):
template = None
if self.testcase.get('cluster_template'):
template = self.testcase['cluster_template']
else:
template_path = os.path.join(self.template_path,
'cluster_template.json')
with open(template_path) as data:
template = json.load(data)
kwargs = dict(template)
ngs = kwargs['node_group_templates']
del kwargs['node_group_templates']
kwargs['node_groups'] = []
for ng, count in ngs.items():
kwargs['node_groups'].append({
'name': utils.rand_name(ng),
'node_group_template_id': node_groups[ng],
'count': count})
kwargs.update(self.plugin_opts)
kwargs['name'] = utils.rand_name(kwargs['name'])
if self.network['type'] == 'neutron':
kwargs['net_id'] = self.neutron.get_network_id(
self.network['private_network'])
return self.__create_cluster_template(**kwargs)
@errormsg("Create cluster failed")
def _create_cluster(self, cluster_template_id):
if self.testcase.get('cluster'):
kwargs = dict(self.testcase['cluster'])
else:
kwargs = {} # default template
kwargs.update(self.plugin_opts)
kwargs['name'] = utils.rand_name(kwargs.get('name', 'test'))
kwargs['cluster_template_id'] = cluster_template_id
kwargs['default_image_id'] = self.nova.get_image_id(
self.testcase['image'])
return self.__create_cluster(**kwargs)
def _poll_cluster_status(self, cluster_id):
# TODO(sreshetniak): make timeout configurable
with fixtures.Timeout(1800, gentle=True):
while True:
status = self.sahara.get_cluster_status(cluster_id)
if status == 'Active':
break
if status == 'Error':
raise exc.TempestException("Cluster in %s state" % status)
time.sleep(3)
# sahara client ops
def __create_node_group_template(self, *args, **kwargs):
id = self.sahara.create_node_group_template(*args, **kwargs)
if not self.testcase['retain_resources']:
self.addCleanup(self.sahara.delete_node_group_template, id)
return id
def __create_cluster_template(self, *args, **kwargs):
id = self.sahara.create_cluster_template(*args, **kwargs)
if not self.testcase['retain_resources']:
self.addCleanup(self.sahara.delete_cluster_template, id)
return id
def __create_cluster(self, *args, **kwargs):
id = self.sahara.create_cluster(*args, **kwargs)
if not self.testcase['retain_resources']:
self.addCleanup(self.sahara.delete_cluster, id)
return id

View File

@ -0,0 +1,109 @@
# Copyright (c) 2015 Mirantis Inc.
#
# 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 time
import fixtures
from neutronclient.neutron import client as neutron_client
from novaclient import client as nova_client
from oslo_utils import uuidutils
from saharaclient.api import base as saharaclient_base
from saharaclient import client as sahara_client
from tempest_lib import exceptions as exc
class Client(object):
def is_resource_deleted(self, method, *args, **kwargs):
raise NotImplementedError
def delete_resource(self, method, *args, **kwargs):
# TODO(sreshetniak): make timeout configurable
with fixtures.Timeout(300, gentle=True):
while True:
if self.is_resource_deleted(method, *args, **kwargs):
break
time.sleep(5)
class SaharaClient(Client):
def __init__(self, *args, **kwargs):
self.sahara_client = sahara_client.Client('1.1', *args, **kwargs)
def create_node_group_template(self, *args, **kwargs):
data = self.sahara_client.node_group_templates.create(*args, **kwargs)
return data.id
def delete_node_group_template(self, node_group_template_id):
return self.delete_resource(
self.sahara_client.node_group_templates.delete,
node_group_template_id)
def create_cluster_template(self, *args, **kwargs):
data = self.sahara_client.cluster_templates.create(*args, **kwargs)
return data.id
def delete_cluster_template(self, cluster_template_id):
return self.delete_resource(
self.sahara_client.cluster_templates.delete,
cluster_template_id)
def create_cluster(self, *args, **kwargs):
data = self.sahara_client.clusters.create(*args, **kwargs)
return data.id
def delete_cluster(self, cluster_id):
return self.delete_resource(
self.sahara_client.clusters.delete,
cluster_id)
def get_cluster_status(self, cluster_id):
data = self.sahara_client.clusters.get(cluster_id)
return str(data.status)
def is_resource_deleted(self, method, *args, **kwargs):
try:
method(*args, **kwargs)
except saharaclient_base.APIException as ex:
return ex.error_code == 404
return False
class NovaClient(Client):
def __init__(self, *args, **kwargs):
self.nova_client = nova_client.Client('1.1', *args, **kwargs)
def get_image_id(self, image_name):
if uuidutils.is_uuid_like(image_name):
return image_name
for image in self.nova_client.images.list():
if image.name == image_name:
return image.id
raise exc.NotFound(image_name)
class NeutronClient(Client):
def __init__(self, *args, **kwargs):
self.neutron_client = neutron_client.Client('2.0', *args, **kwargs)
def get_network_id(self, network_name):
if uuidutils.is_uuid_like(network_name):
return network_name
networks = self.neutron_client.list_networks(name=network_name)
networks = networks['networks']
if len(networks) < 1:
raise exc.NotFound(network_name)
return networks[0]['id']

95
sahara/tests/scenario/runner.py Executable file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
# Copyright (c) 2015 Mirantis Inc.
#
# 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 __future__ import print_function
import argparse
import os
import sys
import tempfile
from mako import template as mako_template
import yaml
from sahara.openstack.common import fileutils
TEST_TEMPLATE_PATH = 'sahara/tests/scenario/testcase.py.mako'
def set_defaults(config):
# set up credentials
config['credentials'] = config.get('credentials', {})
creds = config['credentials']
creds['os_username'] = creds.get('os_username', 'admin')
creds['os_password'] = creds.get('os_password', 'nova')
creds['os_tenant'] = creds.get('os_tenant', 'admin')
creds['os_auth_url'] = creds.get('os_auth_url',
'http://localhost:5000/v2.0')
creds['sahara_url'] = creds.get('sahara_url', None)
# set up network
config['network'] = config.get('network', {})
net = config['network']
net['type'] = net.get('type', 'neutron')
net['private_network'] = net.get('private_network', 'private')
net['auto_assignment_floating_ip'] = net.get('auto_assignment_floating_ip',
False)
net['public_network'] = net.get('public_network', 'public')
# set up tests parameters
for testcase in config['clusters']:
testcase['class_name'] = "".join([
testcase['plugin_name'],
testcase['plugin_version'].replace('.', '_')])
testcase['retain_resources'] = testcase.get('retain_resources', False)
def main():
# parse args
parser = argparse.ArgumentParser(description="Scenario tests runner.")
parser.add_argument('scenario_file', help="Path to scenario file.")
args = parser.parse_args()
scenario_file = args.scenario_file
# parse config
with open(scenario_file, 'r') as yaml_file:
config = yaml.load(yaml_file)
set_defaults(config)
credentials = config['credentials']
network = config['network']
testcases = config['clusters']
# create testcase file
test_template = mako_template.Template(filename=TEST_TEMPLATE_PATH)
testcase_data = test_template.render(testcases=testcases,
credentials=credentials,
network=network)
test_dir_path = tempfile.mkdtemp()
print("The generated test file located at: %s" % test_dir_path)
fileutils.write_to_tempfile(testcase_data, prefix='test_', suffix='.py',
path=test_dir_path)
# run tests
os.environ['DISCOVER_DIRECTORY'] = test_dir_path
return_code = os.system('bash tools/pretty_tox.sh')
sys.exit(return_code)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,7 @@
{
"name": "hdp-206",
"node_group_templates": {
"hdp-master": 1,
"hdp-worker": 3
}
}

View File

@ -0,0 +1,16 @@
{
"name": "hdp-master",
"flavor_id": "3",
"node_processes": [
"NAMENODE",
"SECONDARY_NAMENODE",
"ZOOKEEPER_SERVER",
"AMBARI_SERVER",
"HISTORYSERVER",
"RESOURCEMANAGER",
"GANGLIA_SERVER",
"NAGIOS_SERVER",
"OOZIE_SERVER"
],
"auto_security_group" : true
}

View File

@ -0,0 +1,15 @@
{
"name": "hdp-worker",
"flavor_id": "3",
"node_processes": [
"HDFS_CLIENT",
"DATANODE",
"ZOOKEEPER_CLIENT",
"MAPREDUCE2_CLIENT",
"YARN_CLIENT",
"NODEMANAGER",
"PIG",
"OOZIE_CLIENT"
],
"auto_security_group" : true
}

View File

@ -0,0 +1,7 @@
{
"name": "vanilla-26",
"node_group_templates": {
"vanilla-master": 1,
"vanilla-worker": 3
}
}

View File

@ -0,0 +1,11 @@
{
"name": "vanilla-master",
"flavor_id": "3",
"node_processes": [
"namenode",
"resourcemanager",
"historyserver",
"oozie"
],
"auto_security_group": true
}

View File

@ -0,0 +1,9 @@
{
"name": "vanilla-worker",
"flavor_id": "3",
"node_processes": [
"datanode",
"nodemanager"
],
"auto_security_group": true
}

View File

@ -0,0 +1,18 @@
from sahara.tests.scenario import base
% for testcase in testcases:
${make_testcase(testcase)}
% endfor
<%def name="make_testcase(testcase)">
class ${testcase['class_name']}TestCase(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(${testcase['class_name']}TestCase, cls).setUpClass()
cls.credentials = ${credentials}
cls.network = ${network}
cls.testcase = ${testcase}
def test_plugin(self):
self.create_cluster()
</%def>

View File

@ -0,0 +1,24 @@
# Copyright (c) 2015 Mirantis Inc.
#
# 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_utils import uuidutils
def rand_name(name=''):
rand_data = uuidutils.generate_uuid()[:8]
if name:
return '%s-%s' % (name, rand_data)
else:
return rand_data

View File

@ -4,6 +4,7 @@
hacking>=0.10.0,<0.11
Mako>=0.4.0
MySQL-python
bashate>=0.2 # Apache-2.0
coverage>=3.6

View File

@ -21,6 +21,10 @@ setenv =
DISCOVER_DIRECTORY=sahara/tests/integration
commands = bash tools/pretty_tox.sh '{posargs}'
[testenv:scenario]
setenv = VIRTUALENV={envdir}
commands = python {toxinidir}/sahara/tests/scenario/runner.py "{posargs}"
[testenv:cover]
commands = python setup.py testr --coverage --testr-args='{posargs}'