From 1609a71ce10c8e383a32fba4567ee248b51f582a Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Nov 2017 20:23:26 +0000 Subject: [PATCH] Re-enable and fix amulet tests * Re-enable amulet tests which were disabled in error by the a previous commit. * Charmhelper sync to pickup cinder v2+ compat fixes in amulet helper utils * Add thin-provisioning-tools to packages as cinder now creates thin lvm volumes by default and needs that pkg * General v2 compat fixes in amulet tests Change-Id: Ice227870c8a705d550107de280ebd65fdff31544 --- .../charmhelpers/contrib/charmsupport/nrpe.py | 8 ++- .../contrib/openstack/amulet/utils.py | 21 ++++++- hooks/charmhelpers/core/hookenv.py | 40 ++++++++++++++ hooks/cinder_utils.py | 1 + tests/basic_deployment.py | 55 +++++++++++-------- .../contrib/openstack/amulet/utils.py | 21 ++++++- tests/charmhelpers/core/hookenv.py | 40 ++++++++++++++ 7 files changed, 159 insertions(+), 27 deletions(-) diff --git a/hooks/charmhelpers/contrib/charmsupport/nrpe.py b/hooks/charmhelpers/contrib/charmsupport/nrpe.py index 455fe60d..1c55b30f 100644 --- a/hooks/charmhelpers/contrib/charmsupport/nrpe.py +++ b/hooks/charmhelpers/contrib/charmsupport/nrpe.py @@ -30,6 +30,7 @@ import yaml from charmhelpers.core.hookenv import ( config, + hook_name, local_unit, log, relation_ids, @@ -302,7 +303,12 @@ class NRPE(object): "command": nrpecheck.command, } - service('restart', 'nagios-nrpe-server') + # update-status hooks are configured to firing every 5 minutes by + # default. When nagios-nrpe-server is restarted, the nagios server + # reports checks failing causing unneccessary alerts. Let's not restart + # on update-status hooks. + if not hook_name() == 'update-status': + service('restart', 'nagios-nrpe-server') monitor_ids = relation_ids("local-monitors") + \ relation_ids("nrpe-external-master") diff --git a/hooks/charmhelpers/contrib/openstack/amulet/utils.py b/hooks/charmhelpers/contrib/openstack/amulet/utils.py index 4fda829f..c24be8f1 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/utils.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/utils.py @@ -628,6 +628,18 @@ class OpenStackAmuletUtils(AmuletUtils): _keypair = nova.keypairs.create(name=keypair_name) return _keypair + def _get_cinder_obj_name(self, cinder_object): + """Retrieve name of cinder object. + + :param cinder_object: cinder snapshot or volume object + :returns: str cinder object name + """ + # v1 objects store name in 'display_name' attr but v2+ use 'name' + try: + return cinder_object.display_name + except AttributeError: + return cinder_object.name + def create_cinder_volume(self, cinder, vol_name="demo-vol", vol_size=1, img_id=None, src_vol_id=None, snap_id=None): """Create cinder volume, optionally from a glance image, OR @@ -678,6 +690,13 @@ class OpenStackAmuletUtils(AmuletUtils): source_volid=src_vol_id, snapshot_id=snap_id) vol_id = vol_new.id + except TypeError: + vol_new = cinder.volumes.create(name=vol_name, + imageRef=img_id, + size=vol_size, + source_volid=src_vol_id, + snapshot_id=snap_id) + vol_id = vol_new.id except Exception as e: msg = 'Failed to create volume: {}'.format(e) amulet.raise_status(amulet.FAIL, msg=msg) @@ -692,7 +711,7 @@ class OpenStackAmuletUtils(AmuletUtils): # Re-validate new volume self.log.debug('Validating volume attributes...') - val_vol_name = cinder.volumes.get(vol_id).display_name + val_vol_name = self._get_cinder_obj_name(cinder.volumes.get(vol_id)) val_vol_boot = cinder.volumes.get(vol_id).bootable val_vol_stat = cinder.volumes.get(vol_id).status val_vol_size = cinder.volumes.get(vol_id).size diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py index 70085a40..5a88f798 100644 --- a/hooks/charmhelpers/core/hookenv.py +++ b/hooks/charmhelpers/core/hookenv.py @@ -22,6 +22,7 @@ from __future__ import print_function import copy from distutils.version import LooseVersion from functools import wraps +from collections import namedtuple import glob import os import json @@ -1164,3 +1165,42 @@ def meter_info(): """Get the meter status information, if running in the meter-status-changed hook.""" return os.environ.get('JUJU_METER_INFO') + + +def iter_units_for_relation_name(relation_name): + """Iterate through all units in a relation + + Generator that iterates through all the units in a relation and yields + a named tuple with rid and unit field names. + + Usage: + data = [(u.rid, u.unit) + for u in iter_units_for_relation_name(relation_name)] + + :param relation_name: string relation name + :yield: Named Tuple with rid and unit field names + """ + RelatedUnit = namedtuple('RelatedUnit', 'rid, unit') + for rid in relation_ids(relation_name): + for unit in related_units(rid): + yield RelatedUnit(rid, unit) + + +def ingress_address(rid=None, unit=None): + """ + Retrieve the ingress-address from a relation when available. Otherwise, + return the private-address. This function is to be used on the consuming + side of the relation. + + Usage: + addresses = [ingress_address(rid=u.rid, unit=u.unit) + for u in iter_units_for_relation_name(relation_name)] + + :param rid: string relation id + :param unit: string unit name + :side effect: calls relation_get + :return: string IP address + """ + settings = relation_get(rid=rid, unit=unit) + return (settings.get('ingress-address') or + settings.get('private-address')) diff --git a/hooks/cinder_utils.py b/hooks/cinder_utils.py index 7920c3c8..c9c0e56a 100644 --- a/hooks/cinder_utils.py +++ b/hooks/cinder_utils.py @@ -130,6 +130,7 @@ COMMON_PACKAGES = [ 'python-mysqldb', 'python-psycopg2', 'qemu-utils', + 'thin-provisioning-tools', ] API_PACKAGES = ['cinder-api'] diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 8bdd0679..7e006af7 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -248,6 +248,10 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): snap_new = self.cinder.volume_snapshots.create( volume_id=vol_id, display_name=name) snap_id = snap_new.id + except TypeError: + snap_new = self.cinder.volume_snapshots.create( + volume_id=vol_id, name=name) + snap_id = snap_new.id except Exception as e: msg = 'Failed to snapshot the volume: {}'.format(e) amulet.raise_status(amulet.FAIL, msg=msg) @@ -264,7 +268,8 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): # Validate snapshot u.log.debug('Validating snapshot attributes...') - snap_name = self.cinder.volume_snapshots.get(snap_id).display_name + snap_name = u._get_cinder_obj_name( + self.cinder.volume_snapshots.get(snap_id)) snap_stat = self.cinder.volume_snapshots.get(snap_id).status snap_vol_id = self.cinder.volume_snapshots.get(snap_id).volume_id msg_attr = ('Snapshot attributes - name:{} status:{} ' @@ -284,7 +289,9 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): cinder volumes and snapshots that exist.""" u.log.debug('Checking cinder volumes against lvm volumes...') # Inspect - cmd = 'sudo lvs | grep cinder-volumes | awk \'{ print $1 }\'' + cmd = ('sudo lvs | grep -E \'^\s*(volume|_snap)\' | ' + 'grep cinder-volumes | awk \'{ print $1 }\'') + output, code = self.cinder_sentry.run(cmd) u.log.debug('{} `{}` returned ' '{}'.format(self.cinder_sentry.info['unit_name'], @@ -311,7 +318,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): for vol_this in vol_list: try: vol_id = vol_this.id - vol_name = vol_this.display_name + vol_name = u._get_cinder_obj_name(vol_this) lv_id = 'volume-{}'.format(vol_id) _index = lv_id_list.index(lv_id) u.log.info('Volume ({}) correlates to lv ' @@ -326,7 +333,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): return None - def HOLDtest_100_services(self): + def test_100_services(self): """Verify that the expected services are running on the cinder unit.""" services = { @@ -339,13 +346,13 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): if self._get_openstack_release() < self.xenial_ocata: services[self.cinder_sentry].append('cinder-api') - def HOLDtest_110_memcache(self): + def test_110_memcache(self): u.validate_memcache(self.cinder_sentry, '/etc/cinder/cinder.conf', self._get_openstack_release(), earliest_release=self.trusty_mitaka) - def HOLDtest_110_users(self): + def test_110_users(self): """Verify expected users.""" u.log.debug('Checking keystone users...') if self._get_openstack_release() < self.xenial_pike: @@ -379,7 +386,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): if ret: amulet.raise_status(amulet.FAIL, msg=ret) - def HOLDtest_112_service_catalog(self): + def test_112_service_catalog(self): """Verify that the service catalog endpoint data""" u.log.debug('Checking keystone service catalog...') endpoint_vol = {'adminURL': u.valid_url, @@ -410,7 +417,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): if ret: amulet.raise_status(amulet.FAIL, msg=ret) - def HOLDtest_114_cinder_endpoint(self): + def test_114_cinder_endpoint(self): """Verify the cinder endpoint data.""" u.log.debug('Checking cinder endpoint...') endpoints = self.keystone.endpoints.list() @@ -428,7 +435,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): amulet.raise_status(amulet.FAIL, msg='cinder endpoint: {}'.format(ret)) - def HOLDtest_202_cinder_glance_image_service_relation(self): + def test_202_cinder_glance_image_service_relation(self): """Verify the cinder:glance image-service relation data""" u.log.debug('Checking cinder:glance image-service relation data...') unit = self.cinder_sentry @@ -439,7 +446,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('cinder image-service', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_203_glance_cinder_image_service_relation(self): + def test_203_glance_cinder_image_service_relation(self): """Verify the glance:cinder image-service relation data""" u.log.debug('Checking glance:cinder image-service relation data...') unit = self.glance_sentry @@ -453,7 +460,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('glance image-service', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_204_mysql_cinder_db_relation(self): + def test_204_mysql_cinder_db_relation(self): """Verify the mysql:glance shared-db relation data""" u.log.debug('Checking mysql:cinder db relation data...') unit = self.pxc_sentry @@ -467,7 +474,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('mysql shared-db', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_205_cinder_mysql_db_relation(self): + def test_205_cinder_mysql_db_relation(self): """Verify the cinder:mysql shared-db relation data""" u.log.debug('Checking cinder:mysql db relation data...') unit = self.cinder_sentry @@ -483,7 +490,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('cinder shared-db', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_206_keystone_cinder_id_relation(self): + def test_206_keystone_cinder_id_relation(self): """Verify the keystone:cinder identity-service relation data""" u.log.debug('Checking keystone:cinder id relation data...') unit = self.keystone_sentry @@ -515,7 +522,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('identity-service cinder', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_207_cinder_keystone_id_relation(self): + def test_207_cinder_keystone_id_relation(self): """Verify the cinder:keystone identity-service relation data""" u.log.debug('Checking cinder:keystone id relation data...') unit = self.cinder_sentry @@ -529,7 +536,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('cinder identity-service', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_208_rabbitmq_cinder_amqp_relation(self): + def test_208_rabbitmq_cinder_amqp_relation(self): """Verify the rabbitmq-server:cinder amqp relation data""" u.log.debug('Checking rmq:cinder amqp relation data...') unit = self.rabbitmq_sentry @@ -544,7 +551,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('amqp cinder', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_209_cinder_rabbitmq_amqp_relation(self): + def test_209_cinder_rabbitmq_amqp_relation(self): """Verify the cinder:rabbitmq-server amqp relation data""" u.log.debug('Checking cinder:rmq amqp relation data...') unit = self.cinder_sentry @@ -559,7 +566,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): msg = u.relation_error('cinder amqp', ret) amulet.raise_status(amulet.FAIL, msg=msg) - def HOLDtest_300_cinder_config(self): + def test_300_cinder_config(self): """Verify the data in the cinder.conf file.""" u.log.debug('Checking cinder config file data...') unit = self.cinder_sentry @@ -652,7 +659,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): message = "cinder config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def HOLDtest_301_cinder_logging_config(self): + def test_301_cinder_logging_config(self): """Verify the data in the cinder logging conf file.""" u.log.debug('Checking cinder logging config file data...') unit = self.cinder_sentry @@ -679,7 +686,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): message = "cinder logging config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def HOLDtest_303_cinder_rootwrap_config(self): + def test_303_cinder_rootwrap_config(self): """Inspect select config pairs in rootwrap.conf.""" u.log.debug('Checking cinder rootwrap config file data...') unit = self.cinder_sentry @@ -703,14 +710,14 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): u.log.debug('Cinder api check (volumes.list): {}'.format(check)) assert(check == []) - def HOLDtest_401_create_delete_volume(self): + def test_401_create_delete_volume(self): """Create a cinder volume and delete it.""" u.log.debug('Creating, checking and deleting cinder volume...') vol_new = u.create_cinder_volume(self.cinder) vol_id = vol_new.id u.delete_resource(self.cinder.volumes, vol_id, msg="cinder volume") - def HOLDtest_402_create_delete_volume_from_image(self): + def test_402_create_delete_volume_from_image(self): """Create a cinder volume from a glance image, and delete it.""" u.log.debug('Creating, checking and deleting cinder volume' 'from glance image...') @@ -723,7 +730,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): u.delete_resource(self.glance.images, img_id, msg="glance image") u.delete_resource(self.cinder.volumes, vol_id, msg="cinder volume") - def HOLDtest_403_volume_snap_clone_extend_inspect(self): + def test_403_volume_snap_clone_extend_inspect(self): """Create a cinder volume, clone it, extend its size, create a snapshot of the volume, create a volume from a snapshot, check status of each, inspect underlying lvm, then delete the resources.""" @@ -772,7 +779,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): u.log.debug('Deleting volume {}...'.format(vol.id)) u.delete_resource(self.cinder.volumes, vol.id, msg="cinder volume") - def HOLDtest_900_restart_on_config_change(self): + def test_900_restart_on_config_change(self): """Verify that the specified services are restarted when the config is changed.""" @@ -816,7 +823,7 @@ class CinderBasicDeployment(OpenStackAmuletDeployment): self.d.configure(juju_service, set_default) - def HOLDtest_910_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 = self.d.sentry['cinder'][0] diff --git a/tests/charmhelpers/contrib/openstack/amulet/utils.py b/tests/charmhelpers/contrib/openstack/amulet/utils.py index 4fda829f..c24be8f1 100644 --- a/tests/charmhelpers/contrib/openstack/amulet/utils.py +++ b/tests/charmhelpers/contrib/openstack/amulet/utils.py @@ -628,6 +628,18 @@ class OpenStackAmuletUtils(AmuletUtils): _keypair = nova.keypairs.create(name=keypair_name) return _keypair + def _get_cinder_obj_name(self, cinder_object): + """Retrieve name of cinder object. + + :param cinder_object: cinder snapshot or volume object + :returns: str cinder object name + """ + # v1 objects store name in 'display_name' attr but v2+ use 'name' + try: + return cinder_object.display_name + except AttributeError: + return cinder_object.name + def create_cinder_volume(self, cinder, vol_name="demo-vol", vol_size=1, img_id=None, src_vol_id=None, snap_id=None): """Create cinder volume, optionally from a glance image, OR @@ -678,6 +690,13 @@ class OpenStackAmuletUtils(AmuletUtils): source_volid=src_vol_id, snapshot_id=snap_id) vol_id = vol_new.id + except TypeError: + vol_new = cinder.volumes.create(name=vol_name, + imageRef=img_id, + size=vol_size, + source_volid=src_vol_id, + snapshot_id=snap_id) + vol_id = vol_new.id except Exception as e: msg = 'Failed to create volume: {}'.format(e) amulet.raise_status(amulet.FAIL, msg=msg) @@ -692,7 +711,7 @@ class OpenStackAmuletUtils(AmuletUtils): # Re-validate new volume self.log.debug('Validating volume attributes...') - val_vol_name = cinder.volumes.get(vol_id).display_name + val_vol_name = self._get_cinder_obj_name(cinder.volumes.get(vol_id)) val_vol_boot = cinder.volumes.get(vol_id).bootable val_vol_stat = cinder.volumes.get(vol_id).status val_vol_size = cinder.volumes.get(vol_id).size diff --git a/tests/charmhelpers/core/hookenv.py b/tests/charmhelpers/core/hookenv.py index 70085a40..5a88f798 100644 --- a/tests/charmhelpers/core/hookenv.py +++ b/tests/charmhelpers/core/hookenv.py @@ -22,6 +22,7 @@ from __future__ import print_function import copy from distutils.version import LooseVersion from functools import wraps +from collections import namedtuple import glob import os import json @@ -1164,3 +1165,42 @@ def meter_info(): """Get the meter status information, if running in the meter-status-changed hook.""" return os.environ.get('JUJU_METER_INFO') + + +def iter_units_for_relation_name(relation_name): + """Iterate through all units in a relation + + Generator that iterates through all the units in a relation and yields + a named tuple with rid and unit field names. + + Usage: + data = [(u.rid, u.unit) + for u in iter_units_for_relation_name(relation_name)] + + :param relation_name: string relation name + :yield: Named Tuple with rid and unit field names + """ + RelatedUnit = namedtuple('RelatedUnit', 'rid, unit') + for rid in relation_ids(relation_name): + for unit in related_units(rid): + yield RelatedUnit(rid, unit) + + +def ingress_address(rid=None, unit=None): + """ + Retrieve the ingress-address from a relation when available. Otherwise, + return the private-address. This function is to be used on the consuming + side of the relation. + + Usage: + addresses = [ingress_address(rid=u.rid, unit=u.unit) + for u in iter_units_for_relation_name(relation_name)] + + :param rid: string relation id + :param unit: string unit name + :side effect: calls relation_get + :return: string IP address + """ + settings = relation_get(rid=rid, unit=unit) + return (settings.get('ingress-address') or + settings.get('private-address'))