[1chb1n, r=gnuoy] Amulet fixes
This commit is contained in:
commit
a559ab7a10
|
@ -14,6 +14,7 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import six
|
||||
from collections import OrderedDict
|
||||
from charmhelpers.contrib.amulet.deployment import (
|
||||
|
@ -114,6 +115,45 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
for service, config in six.iteritems(configs):
|
||||
self.d.configure(service, config)
|
||||
|
||||
def _auto_wait_for_status(self, message=None, exclude_services=None,
|
||||
timeout=1800):
|
||||
"""Wait for all units to have a specific extended status, except
|
||||
for any defined as excluded. Unless specified via message, any
|
||||
status containing any case of 'ready' will be considered a match.
|
||||
|
||||
Examples of message usage:
|
||||
|
||||
Wait for all unit status to CONTAIN any case of 'ready' or 'ok':
|
||||
message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE)
|
||||
|
||||
Wait for all units to reach this status (exact match):
|
||||
message = 'Unit is ready'
|
||||
|
||||
Wait for all units to reach any one of these (exact match):
|
||||
message = re.compile('Unit is ready|OK|Ready')
|
||||
|
||||
Wait for at least one unit to reach this status (exact match):
|
||||
message = {'ready'}
|
||||
|
||||
See Amulet's sentry.wait_for_messages() for message usage detail.
|
||||
https://github.com/juju/amulet/blob/master/amulet/sentry.py
|
||||
|
||||
:param message: Expected status match
|
||||
:param exclude_services: List of juju service names to ignore
|
||||
:param timeout: Maximum time in seconds to wait for status match
|
||||
:returns: None. Raises if timeout is hit.
|
||||
"""
|
||||
|
||||
if not message:
|
||||
message = re.compile('.*ready.*', re.IGNORECASE)
|
||||
|
||||
if not exclude_services:
|
||||
exclude_services = []
|
||||
|
||||
services = list(set(self.d.services.keys()) - set(exclude_services))
|
||||
service_messages = {service: message for service in services}
|
||||
self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
|
||||
|
||||
def _get_openstack_release(self):
|
||||
"""Get openstack release.
|
||||
|
||||
|
|
|
@ -566,7 +566,14 @@ def chdir(d):
|
|||
os.chdir(cur)
|
||||
|
||||
|
||||
def chownr(path, owner, group, follow_links=True):
|
||||
def chownr(path, owner, group, follow_links=True, chowntopdir=False):
|
||||
"""
|
||||
Recursively change user and group ownership of files and directories
|
||||
in given path. Doesn't chown path itself by default, only its children.
|
||||
|
||||
:param bool follow_links: Also Chown links if True
|
||||
:param bool chowntopdir: Also chown path itself if True
|
||||
"""
|
||||
uid = pwd.getpwnam(owner).pw_uid
|
||||
gid = grp.getgrnam(group).gr_gid
|
||||
if follow_links:
|
||||
|
@ -574,6 +581,10 @@ def chownr(path, owner, group, follow_links=True):
|
|||
else:
|
||||
chown = os.lchown
|
||||
|
||||
if chowntopdir:
|
||||
broken_symlink = os.path.lexists(path) and not os.path.exists(path)
|
||||
if not broken_symlink:
|
||||
chown(path, uid, gid)
|
||||
for root, dirs, files in os.walk(path):
|
||||
for name in dirs + files:
|
||||
full = os.path.join(root, name)
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import subprocess
|
||||
|
||||
"""
|
||||
Basic ceilometer functional tests.
|
||||
"""
|
||||
import amulet
|
||||
import json
|
||||
import time
|
||||
|
@ -35,6 +29,11 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
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):
|
||||
|
@ -49,6 +48,7 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
{'name': 'rabbitmq-server'},
|
||||
{'name': 'keystone'},
|
||||
{'name': 'mongodb'},
|
||||
{'name': 'glance'}, # to satisfy workload status
|
||||
{'name': 'ceilometer-agent'},
|
||||
{'name': 'nova-compute'}]
|
||||
super(CeilometerBasicDeployment, self)._add_services(this_service,
|
||||
|
@ -67,7 +67,11 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
'ceilometer-service',
|
||||
'nova-compute:nova-ceilometer': 'ceilometer-agent:nova-ceilometer',
|
||||
'nova-compute:shared-db': 'mysql:shared-db',
|
||||
'nova-compute:amqp': 'rabbitmq-server:amqp'
|
||||
'nova-compute:amqp': 'rabbitmq-server:amqp',
|
||||
'glance:identity-service': 'keystone:identity-service',
|
||||
'glance:shared-db': 'mysql:shared-db',
|
||||
'glance:amqp': 'rabbitmq-server:amqp',
|
||||
'nova-compute:image-service': 'glance:image-service'
|
||||
}
|
||||
super(CeilometerBasicDeployment, self)._add_relations(relations)
|
||||
|
||||
|
@ -96,9 +100,6 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
u.log.debug('openstack release str: {}'.format(
|
||||
self._get_openstack_release_string()))
|
||||
|
||||
# Let things settle a bit before moving forward
|
||||
time.sleep(30)
|
||||
|
||||
# Authenticate admin with keystone endpoint
|
||||
self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
|
||||
user='admin',
|
||||
|
@ -139,6 +140,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
def test_100_services(self):
|
||||
"""Verify the expected services are running on the corresponding
|
||||
service units."""
|
||||
u.log.debug('Checking system services on units...')
|
||||
|
||||
ceilometer_svcs = [
|
||||
'ceilometer-agent-central',
|
||||
'ceilometer-collector',
|
||||
|
@ -159,6 +162,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
if ret:
|
||||
amulet.raise_status(amulet.FAIL, msg=ret)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_110_service_catalog(self):
|
||||
"""Verify that the service catalog endpoint data is valid."""
|
||||
u.log.debug('Checking keystone service catalog data...')
|
||||
|
@ -179,6 +184,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
if ret:
|
||||
amulet.raise_status(amulet.FAIL, msg=ret)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_112_keystone_api_endpoint(self):
|
||||
"""Verify the ceilometer api endpoint data."""
|
||||
u.log.debug('Checking keystone api endpoint data...')
|
||||
|
@ -199,6 +206,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = 'Keystone endpoint: {}'.format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_114_ceilometer_api_endpoint(self):
|
||||
"""Verify the ceilometer api endpoint data."""
|
||||
u.log.debug('Checking ceilometer api endpoint data...')
|
||||
|
@ -218,6 +227,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = 'Ceilometer endpoint: {}'.format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_200_ceilometer_identity_relation(self):
|
||||
"""Verify the ceilometer to keystone identity-service relation data"""
|
||||
u.log.debug('Checking ceilometer to keystone identity-service '
|
||||
|
@ -243,6 +254,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('ceilometer identity-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_201_keystone_ceilometer_identity_relation(self):
|
||||
"""Verify the keystone to ceilometer identity-service relation data"""
|
||||
u.log.debug('Checking keystone:ceilometer identity relation data...')
|
||||
|
@ -270,20 +283,35 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('keystone identity-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_202_keystone_ceilometer_identity_notes_relation(self):
|
||||
"""Verify ceilometer to keystone identity-notifications relation"""
|
||||
u.log.debug('Checking keystone:ceilometer '
|
||||
'identity-notifications relation data...')
|
||||
|
||||
# Relation data may vary depending on timing of hooks and relations.
|
||||
# May be glance- or keystone- or another endpoint-changed value, so
|
||||
# check that at least one ???-endpoint-changed value exists.
|
||||
unit = self.keystone_sentry
|
||||
relation = ['identity-service', 'ceilometer:identity-notifications']
|
||||
expected = {
|
||||
'ceilometer-endpoint-changed': u.not_null,
|
||||
}
|
||||
ret = u.validate_relation_data(unit, relation, expected)
|
||||
if ret:
|
||||
message = u.relation_error('keystone identity-notifications', ret)
|
||||
relation_data = unit.relation('identity-service',
|
||||
'ceilometer:identity-notifications')
|
||||
|
||||
expected = '-endpoint-changed'
|
||||
found = 0
|
||||
for key in relation_data.keys():
|
||||
if expected in key and relation_data[key]:
|
||||
found += 1
|
||||
u.log.debug('{}: {}'.format(key, relation_data[key]))
|
||||
|
||||
if not found:
|
||||
message = ('keystone:ceilometer identity-notification relation '
|
||||
'error\n expected something like: {}\n actual: '
|
||||
'{}'.format(expected, relation_data))
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_203_ceilometer_amqp_relation(self):
|
||||
"""Verify the ceilometer to rabbitmq-server amqp relation data"""
|
||||
u.log.debug('Checking ceilometer:rabbitmq amqp relation data...')
|
||||
|
@ -300,6 +328,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('ceilometer amqp', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_204_amqp_ceilometer_relation(self):
|
||||
"""Verify the rabbitmq-server to ceilometer amqp relation data"""
|
||||
u.log.debug('Checking rabbitmq:ceilometer amqp relation data...')
|
||||
|
@ -316,6 +346,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('rabbitmq amqp', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_205_ceilometer_to_mongodb_relation(self):
|
||||
"""Verify the ceilometer to mongodb relation data"""
|
||||
u.log.debug('Checking ceilometer:mongodb relation data...')
|
||||
|
@ -331,6 +363,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('ceilometer shared-db', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_206_mongodb_to_ceilometer_relation(self):
|
||||
"""Verify the mongodb to ceilometer relation data"""
|
||||
u.log.debug('Checking mongodb:ceilometer relation data...')
|
||||
|
@ -343,14 +377,13 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
'type': 'database',
|
||||
}
|
||||
|
||||
if self._get_openstack_release() == self.precise_icehouse:
|
||||
expected['replset'] = 'myset'
|
||||
|
||||
ret = u.validate_relation_data(unit, relation, expected)
|
||||
if ret:
|
||||
message = u.relation_error('mongodb database', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_207_ceilometer_ceilometer_agent_relation(self):
|
||||
"""Verify the ceilometer to ceilometer-agent relation data"""
|
||||
u.log.debug('Checking ceilometer:ceilometer-agent relation data...')
|
||||
|
@ -379,6 +412,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('ceilometer-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_208_ceilometer_agent_ceilometer_relation(self):
|
||||
"""Verify the ceilometer-agent to ceilometer relation data"""
|
||||
u.log.debug('Checking ceilometer-agent:ceilometer relation data...')
|
||||
|
@ -391,6 +426,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('ceilometer-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_209_nova_compute_ceilometer_agent_relation(self):
|
||||
"""Verify the nova-compute to ceilometer relation data"""
|
||||
u.log.debug('Checking nova-compute:ceilometer relation data...')
|
||||
|
@ -425,6 +462,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('ceilometer-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_300_ceilometer_config(self):
|
||||
"""Verify the data in the ceilometer config file."""
|
||||
u.log.debug('Checking ceilometer config file data...')
|
||||
|
@ -480,6 +519,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = "ceilometer config error: {}".format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_301_nova_config(self):
|
||||
"""Verify data in the nova compute nova config file"""
|
||||
u.log.debug('Checking nova compute config file...')
|
||||
|
@ -528,6 +569,8 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
if ret:
|
||||
amulet.raise_status(amulet.FAIL, msg=ret)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_302_nova_ceilometer_config(self):
|
||||
"""Verify data in the ceilometer config file on the
|
||||
nova-compute (ceilometer-agent) unit."""
|
||||
|
@ -550,11 +593,14 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = "ceilometer config error: {}".format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_400_api_connection(self):
|
||||
"""Simple api calls to check service is up and responding"""
|
||||
u.log.debug('Checking api functionality...')
|
||||
assert(self.ceil.samples.list() == [])
|
||||
assert(self.ceil.meters.list() == [])
|
||||
u.log.debug('OK')
|
||||
|
||||
# NOTE(beisner): need to add more functional tests
|
||||
|
||||
|
@ -569,38 +615,49 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
set_default = {'debug': 'False'}
|
||||
set_alternate = {'debug': 'True'}
|
||||
|
||||
# Config file affected by juju set config change
|
||||
# Services which are expected to restart upon config change,
|
||||
# and corresponding config files affected by the change
|
||||
conf_file = '/etc/ceilometer/ceilometer.conf'
|
||||
services = {
|
||||
'ceilometer-collector': conf_file,
|
||||
'ceilometer-api': conf_file,
|
||||
'ceilometer-alarm-evaluator': conf_file,
|
||||
'ceilometer-alarm-notifier': conf_file,
|
||||
'ceilometer-agent-notification': conf_file,
|
||||
}
|
||||
|
||||
# Services which are expected to restart upon config change
|
||||
services = [
|
||||
'ceilometer-agent-central',
|
||||
'ceilometer-collector',
|
||||
'ceilometer-api',
|
||||
'ceilometer-alarm-evaluator',
|
||||
'ceilometer-alarm-notifier',
|
||||
'ceilometer-agent-notification',
|
||||
]
|
||||
if self._get_openstack_release() == self.trusty_liberty or \
|
||||
self._get_openstack_release() >= self.wily_liberty:
|
||||
# Liberty and later
|
||||
services['ceilometer-polling'] = conf_file
|
||||
else:
|
||||
# Juno and earlier
|
||||
services['ceilometer-agent-central'] = conf_file
|
||||
|
||||
# Make config change, check for service restarts
|
||||
u.log.debug('Making config change on {}...'.format(juju_service))
|
||||
mtime = u.get_sentry_time(sentry)
|
||||
self.d.configure(juju_service, set_alternate)
|
||||
|
||||
sleep_time = 40
|
||||
for s in services:
|
||||
for s, conf_file in services.iteritems():
|
||||
u.log.debug("Checking that service restarted: {}".format(s))
|
||||
if not u.service_restarted(sentry, s,
|
||||
conf_file, sleep_time=sleep_time,
|
||||
pgrep_full=True):
|
||||
if not u.validate_service_config_changed(sentry, mtime, s,
|
||||
conf_file,
|
||||
retry_count=4,
|
||||
retry_sleep_time=20,
|
||||
sleep_time=sleep_time):
|
||||
self.d.configure(juju_service, set_default)
|
||||
msg = "service {} didn't restart after config change".format(s)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
sleep_time = 0
|
||||
|
||||
self.d.configure(juju_service, set_default)
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_1000_pause_and_resume(self):
|
||||
def test_910_pause_and_resume(self):
|
||||
"""The services can be paused and resumed. """
|
||||
u.log.debug('Checking pause and resume actions...')
|
||||
unit_name = "ceilometer/0"
|
||||
unit = self.d.sentry.unit[unit_name]
|
||||
|
||||
|
@ -613,3 +670,4 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
action_id = self._run_action(unit_name, "resume")
|
||||
assert self._wait_on_action(action_id), "Resume action failed."
|
||||
assert u.status_get(unit)[0] == "active"
|
||||
u.log.debug('OK')
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import six
|
||||
from collections import OrderedDict
|
||||
from charmhelpers.contrib.amulet.deployment import (
|
||||
|
@ -114,6 +115,45 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
for service, config in six.iteritems(configs):
|
||||
self.d.configure(service, config)
|
||||
|
||||
def _auto_wait_for_status(self, message=None, exclude_services=None,
|
||||
timeout=1800):
|
||||
"""Wait for all units to have a specific extended status, except
|
||||
for any defined as excluded. Unless specified via message, any
|
||||
status containing any case of 'ready' will be considered a match.
|
||||
|
||||
Examples of message usage:
|
||||
|
||||
Wait for all unit status to CONTAIN any case of 'ready' or 'ok':
|
||||
message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE)
|
||||
|
||||
Wait for all units to reach this status (exact match):
|
||||
message = 'Unit is ready'
|
||||
|
||||
Wait for all units to reach any one of these (exact match):
|
||||
message = re.compile('Unit is ready|OK|Ready')
|
||||
|
||||
Wait for at least one unit to reach this status (exact match):
|
||||
message = {'ready'}
|
||||
|
||||
See Amulet's sentry.wait_for_messages() for message usage detail.
|
||||
https://github.com/juju/amulet/blob/master/amulet/sentry.py
|
||||
|
||||
:param message: Expected status match
|
||||
:param exclude_services: List of juju service names to ignore
|
||||
:param timeout: Maximum time in seconds to wait for status match
|
||||
:returns: None. Raises if timeout is hit.
|
||||
"""
|
||||
|
||||
if not message:
|
||||
message = re.compile('.*ready.*', re.IGNORECASE)
|
||||
|
||||
if not exclude_services:
|
||||
exclude_services = []
|
||||
|
||||
services = list(set(self.d.services.keys()) - set(exclude_services))
|
||||
service_messages = {service: message for service in services}
|
||||
self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
|
||||
|
||||
def _get_openstack_release(self):
|
||||
"""Get openstack release.
|
||||
|
||||
|
|
Loading…
Reference in New Issue