Add Amulet Tests

This change adds amulet tests to the charm and fixes a bug
causing the status to never report a unit ready state which
was blocking amulet runs.

Closes-Bug: 1604172
Change-Id: I345fb1a3c63a17f1b0070426d06c517c908c7d81
This commit is contained in:
Liam Young 2016-09-28 10:11:18 +00:00
parent 068a29eccf
commit 26b4fb2374
11 changed files with 206 additions and 17 deletions

View File

@ -1,5 +1,4 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
charm-tools>=2.0.0
simplejson>=2.2.0
# Requirements to build the charm
charm-tools
simplejson
flake8

View File

@ -10,5 +10,4 @@ if __name__ == '__main__':
# Cloud may have different artifacts (flavors, images etc) since last run
# so rerun handlers file to regenerate config.
reactive.main()
charm = tempest.get_charm()
charm.run_test('smoke')
tempest.run_test('smoke')

View File

@ -15,7 +15,7 @@ import charmhelpers.fetch as fetch
def install():
"""Use the singleton from the BarbicanCharm to install the packages on the
"""Use the singleton from the TempestCharm to install the packages on the
unit
"""
TempestCharm.singleton.install()
@ -28,6 +28,14 @@ def render_configs(interfaces_list):
if not os.path.isdir(TempestCharm.TEMPEST_LOGDIR):
os.makedirs(TempestCharm.TEMPEST_LOGDIR)
TempestCharm.singleton.render_with_interfaces(interfaces_list)
TempestCharm.singleton.assess_status()
def run_test(tox_target):
"""Use the singleton from the TempestCharm to install the packages on the
unit
"""
TempestCharm.singleton.run_test(tox_target)
class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
@ -247,6 +255,7 @@ class TempestCharm(charm.OpenStackCharm):
release = 'liberty'
name = 'tempest'
required_relations = ['identity-admin']
"""Directories and files used for running tempest"""
TEMPEST_ROOT = '/var/lib/tempest/'
TEMPEST_LOGDIR = TEMPEST_ROOT + '/logs'

View File

@ -1 +0,0 @@
../requirements.txt

View File

@ -1 +0,0 @@
../test-requirements.txt

23
src/test-requirements.txt Normal file
View File

@ -0,0 +1,23 @@
# charm-proof
charm-tools>=2.0.0
# amulet deployment helpers
bzr+lp:charm-helpers#egg=charmhelpers
# BEGIN: Amulet OpenStack Charm Helper Requirements
# Liberty client lower constraints
amulet>=1.14.3,<2.0
bundletester>=0.6.1,<1.0
aodhclient>=0.1.0
python-ceilometerclient>=1.5.0,<2.0
python-cinderclient>=1.4.0,<2.0
python-glanceclient>=1.1.0,<2.0
python-heatclient>=0.8.0,<1.0
python-keystoneclient>=1.7.1,<2.0
python-neutronclient>=3.1.0,<4.0
python-novaclient>=2.30.1,<3.0
python-openstackclient>=1.7.0,<2.0
python-swiftclient>=2.6.0,<3.0
pika>=0.10.0,<1.0
distro-info
# END: Amulet OpenStack Charm Helper Requirements
# NOTE: workaround for 14.04 pip/tox
pytz

View File

@ -0,0 +1,124 @@
import subprocess
import json
import time
from charmhelpers.contrib.openstack.amulet.deployment import (
OpenStackAmuletDeployment
)
from charmhelpers.contrib.openstack.amulet.utils import (
OpenStackAmuletUtils,
DEBUG,
)
# Use DEBUG to turn on debug logging
u = OpenStackAmuletUtils(DEBUG)
class TempestBasicDeployment(OpenStackAmuletDeployment):
"""Amulet tests on a basic tempest deployment."""
def __init__(self, series, openstack=None, source=None, stable=False):
"""Deploy the entire test environment."""
super(TempestBasicDeployment, self).__init__(series, openstack,
source, stable)
self._add_services()
self._add_relations()
self._configure_services()
self._deploy()
u.log.info('Waiting on extended status checks...')
exclude_services = ['mysql', 'mongodb']
self._auto_wait_for_status(exclude_services=exclude_services)
self._initialize_tests()
def _add_services(self):
"""Add services
Add the services that we're testing, where tempest is local,
and the rest of the service are from lp branches that are
compatible with the local charm (e.g. stable or next).
"""
this_service = {'name': 'tempest'}
other_services = [{'name': 'mysql'},
{'name': 'rabbitmq-server'},
{'name': 'keystone'},
{'name': 'openstack-dashboard'},
{'name': 'glance'}]
super(TempestBasicDeployment, self)._add_services(
this_service,
other_services,
no_origin=['tempest'])
def _add_relations(self):
"""Add all of the relations for the services."""
relations = {
'keystone:identity-admin': 'tempest:identity-admin',
'tempest:dashboard': 'openstack-dashboard:website',
'openstack-dashboard:identity-service':
'keystone:identity-service',
'keystone:shared-db': 'mysql:shared-db',
'glance:identity-service': 'keystone:identity-service',
'glance:shared-db': 'mysql:shared-db',
'glance:amqp': 'rabbitmq-server:amqp'
}
super(TempestBasicDeployment, self)._add_relations(relations)
def _configure_services(self):
"""Configure all of the services."""
keystone_config = {'admin-password': 'openstack',
'admin-token': 'ubuntutesting'}
configs = {'keystone': keystone_config}
super(TempestBasicDeployment, self)._configure_services(configs)
def _get_token(self):
return self.keystone.service_catalog.catalog['token']['id']
def _initialize_tests(self):
"""Perform final initialization before tests get run."""
# Access the sentries for inspecting service units
self.mysql_sentry = self.d.sentry['mysql'][0]
self.keystone_sentry = self.d.sentry['keystone'][0]
self.rabbitmq_sentry = self.d.sentry['rabbitmq-server'][0]
self.tempest_sentry = self.d.sentry['tempest'][0]
self.openstack_dashboard_sentry = \
self.d.sentry['openstack-dashboard'][0]
u.log.debug('openstack release val: {}'.format(
self._get_openstack_release()))
u.log.debug('openstack release str: {}'.format(
self._get_openstack_release_string()))
def _run_action(self, unit_id, action, *args):
command = ["juju", "action", "do", "--format=json", unit_id, action]
command.extend(args)
print("Running command: %s\n" % " ".join(command))
output = subprocess.check_output(command)
output_json = output.decode(encoding="UTF-8")
data = json.loads(output_json)
action_id = data[u'Action queued with id']
return action_id
def _wait_on_action(self, action_id):
command = ["juju", "action", "fetch", "--format=json", action_id]
while True:
try:
output = subprocess.check_output(command)
except Exception as e:
print(e)
return False
output_json = output.decode(encoding="UTF-8")
data = json.loads(output_json)
if data[u"status"] == "completed":
return True
elif data[u"status"] == "failed":
return False
time.sleep(2)
def test_run_tempest(self):
u.log.debug('Running Tempest...')
unit = self.tempest_sentry
assert u.status_get(unit)[0] == "active"
action_id = self._run_action(unit.info['unit_name'], "run-tempest")
assert self._wait_on_action(action_id), "run-tempest action failed."

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
"""Amulet tests on a basic tempest deployment on trusty-mitaka."""
from basic_deployment import TempestBasicDeployment
if __name__ == '__main__':
# Tempest is installed through pip so cloud archive is not needed here
deployment = TempestBasicDeployment(series='trusty',
openstack='cloud:trusty-mitaka',
source='cloud:trusty-updates/mitaka')
deployment.run_tests()

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
"""Amulet tests on a basic tempest deployment on xenial-mitaka."""
from basic_deployment import TempestBasicDeployment
if __name__ == '__main__':
deployment = TempestBasicDeployment(series='xenial')
deployment.run_tests()

17
src/tests/tests.yaml Normal file
View File

@ -0,0 +1,17 @@
# Bootstrap the model if necessary.
bootstrap: True
# Re-use bootstrap node instead of destroying/re-bootstrapping.
reset: True
# Use tox/requirements to drive the venv instead of bundletester's venv feature.
virtualenv: False
# Leave makefile empty, otherwise unit/lint tests will rerun ahead of amulet.
makefile: []
# Do not specify juju PPA sources. Juju is presumed to be pre-installed
# and configured in all test runner environments.
#sources:
# Do not specify or rely on system packages.
#packages:
# Do not specify python packages here. Use test-requirements.txt
# and tox instead. ie. The venv is constructed before bundletester
# is invoked.
#python-packages:

View File

@ -1 +0,0 @@
Add amulet functional tests.

View File

@ -1,8 +1,7 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
coverage>=3.6
mock>=1.2
pep8>=1.7.0
# Unit test requirements
flake8>=2.2.4,<=2.4.1
os-testr>=0.4.1
charms.reactive
mock>=1.2
coverage>=3.6
git+https://github.com/openstack/charms.openstack#egg=charms.openstack