resync tests/charmhelpers
This commit is contained in:
parent
9e989a55e8
commit
78e0326cad
@ -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 amulet
|
||||
import ConfigParser
|
||||
import distro_info
|
||||
import io
|
||||
@ -173,6 +174,11 @@ class AmuletUtils(object):
|
||||
|
||||
Verify that the specified section of the config file contains
|
||||
the expected option key:value pairs.
|
||||
|
||||
Compare expected dictionary data vs actual dictionary data.
|
||||
The values in the 'expected' dictionary can be strings, bools, ints,
|
||||
longs, or can be a function that evaluates a variable and returns a
|
||||
bool.
|
||||
"""
|
||||
self.log.debug('Validating config file data ({} in {} on {})'
|
||||
'...'.format(section, config_file,
|
||||
@ -195,20 +201,18 @@ class AmuletUtils(object):
|
||||
if actual != v:
|
||||
return "section [{}] {}:{} != expected {}:{}".format(
|
||||
section, k, actual, k, expected[k])
|
||||
else:
|
||||
# handle not_null, valid_ip boolean comparison methods, etc.
|
||||
if v(actual):
|
||||
return None
|
||||
else:
|
||||
# handle function pointers, such as not_null or valid_ip
|
||||
elif not v(actual):
|
||||
return "section [{}] {}:{} != expected {}:{}".format(
|
||||
section, k, actual, k, expected[k])
|
||||
return None
|
||||
|
||||
def _validate_dict_data(self, expected, actual):
|
||||
"""Validate dictionary data.
|
||||
|
||||
Compare expected dictionary data vs actual dictionary data.
|
||||
The values in the 'expected' dictionary can be strings, bools, ints,
|
||||
longs, or can be a function that evaluate a variable and returns a
|
||||
longs, or can be a function that evaluates a variable and returns a
|
||||
bool.
|
||||
"""
|
||||
self.log.debug('actual: {}'.format(repr(actual)))
|
||||
@ -219,8 +223,10 @@ class AmuletUtils(object):
|
||||
if (isinstance(v, six.string_types) or
|
||||
isinstance(v, bool) or
|
||||
isinstance(v, six.integer_types)):
|
||||
# handle explicit values
|
||||
if v != actual[k]:
|
||||
return "{}:{}".format(k, actual[k])
|
||||
# handle function pointers, such as not_null or valid_ip
|
||||
elif not v(actual[k]):
|
||||
return "{}:{}".format(k, actual[k])
|
||||
else:
|
||||
@ -435,15 +441,13 @@ class AmuletUtils(object):
|
||||
for cmd in commands:
|
||||
output, code = sentry_unit.run(cmd)
|
||||
if code == 0:
|
||||
msg = ('{} `{}` returned {} '
|
||||
self.log.debug('{} `{}` returned {} '
|
||||
'(OK)'.format(sentry_unit.info['unit_name'],
|
||||
cmd, code))
|
||||
self.log.debug(msg)
|
||||
else:
|
||||
msg = ('{} `{}` returned {} '
|
||||
return ('{} `{}` returned {} '
|
||||
'{}'.format(sentry_unit.info['unit_name'],
|
||||
cmd, code, output))
|
||||
return msg
|
||||
return None
|
||||
|
||||
def get_process_id_list(self, sentry_unit, process_name):
|
||||
@ -460,7 +464,7 @@ class AmuletUtils(object):
|
||||
msg = ('{} `{}` returned {} '
|
||||
'{}'.format(sentry_unit.info['unit_name'],
|
||||
cmd, code, output))
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
return str(output).split()
|
||||
|
||||
def get_unit_process_ids(self, unit_processes):
|
||||
@ -481,47 +485,37 @@ class AmuletUtils(object):
|
||||
self.log.debug('Actual PIDs: {}'.format(actual))
|
||||
|
||||
if len(actual) != len(expected):
|
||||
msg = ('Unit count mismatch. expected, actual: {}, '
|
||||
return ('Unit count mismatch. expected, actual: {}, '
|
||||
'{} '.format(len(expected), len(actual)))
|
||||
return msg
|
||||
|
||||
for (e_sentry, e_proc_names) in expected.iteritems():
|
||||
e_sentry_name = e_sentry.info['unit_name']
|
||||
if e_sentry in actual.keys():
|
||||
a_proc_names = actual[e_sentry]
|
||||
else:
|
||||
msg = ('Expected sentry ({}) not found in actual dict data.'
|
||||
return ('Expected sentry ({}) not found in actual dict data.'
|
||||
'{}'.format(e_sentry_name, e_sentry))
|
||||
return msg
|
||||
|
||||
if len(e_proc_names.keys()) != len(a_proc_names.keys()):
|
||||
msg = ('Process name count mismatch. expected, actual: {}, '
|
||||
return ('Process name count mismatch. expected, actual: {}, '
|
||||
'{}'.format(len(expected), len(actual)))
|
||||
return msg
|
||||
|
||||
for (e_proc_name, e_pids_length), (a_proc_name, a_pids) in \
|
||||
zip(e_proc_names.items(), a_proc_names.items()):
|
||||
if e_proc_name != a_proc_name:
|
||||
msg = ('Process name mismatch. expected, actual: {}, '
|
||||
return ('Process name mismatch. expected, actual: {}, '
|
||||
'{}'.format(e_proc_name, a_proc_name))
|
||||
return msg
|
||||
|
||||
a_pids_length = len(a_pids)
|
||||
if e_pids_length != a_pids_length:
|
||||
msg = ('PID count mismatch. {} ({}) expected, actual: {}, '
|
||||
'{} ({})'.format(e_sentry_name,
|
||||
e_proc_name,
|
||||
e_pids_length,
|
||||
a_pids_length,
|
||||
return ('PID count mismatch. {} ({}) expected, actual: '
|
||||
'{}, {} ({})'.format(e_sentry_name, e_proc_name,
|
||||
e_pids_length, a_pids_length,
|
||||
a_pids))
|
||||
return msg
|
||||
else:
|
||||
msg = ('PID check OK: {} {} {}: '
|
||||
'{}'.format(e_sentry_name,
|
||||
e_proc_name,
|
||||
e_pids_length,
|
||||
a_pids))
|
||||
self.log.debug(msg)
|
||||
self.log.debug('PID check OK: {} {} {}: '
|
||||
'{}'.format(e_sentry_name, e_proc_name,
|
||||
e_pids_length, a_pids))
|
||||
return None
|
||||
|
||||
def validate_list_of_identical_dicts(self, list_of_dicts):
|
||||
@ -532,10 +526,8 @@ class AmuletUtils(object):
|
||||
|
||||
self.log.debug('Hashes: {}'.format(hashes))
|
||||
if len(set(hashes)) == 1:
|
||||
msg = 'Dicts within list are identical'
|
||||
self.log.debug(msg)
|
||||
self.log.debug('Dicts within list are identical')
|
||||
else:
|
||||
msg = 'Dicts within list are not identical'
|
||||
return msg
|
||||
return 'Dicts within list are not identical'
|
||||
|
||||
return None
|
||||
|
@ -79,9 +79,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
||||
services.append(this_service)
|
||||
use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph',
|
||||
'ceph-osd', 'ceph-radosgw']
|
||||
# Openstack subordinate charms do not expose an origin option as that
|
||||
# is controlled by the principle
|
||||
ignore = ['neutron-openvswitch']
|
||||
# Most OpenStack subordinate charms do not expose an origin option
|
||||
# as that is controlled by the principle.
|
||||
ignore = ['cinder-ceph', 'hacluster', 'neutron-openvswitch']
|
||||
|
||||
if self.openstack:
|
||||
for svc in services:
|
||||
@ -150,8 +150,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
||||
return releases[self.series]
|
||||
|
||||
def get_ceph_expected_pools(self, radosgw=False):
|
||||
"""Return a list of expected ceph pools based on Ubuntu-OpenStack
|
||||
release and whether ceph radosgw is flagged as present or not."""
|
||||
"""Return a list of expected ceph pools in a ceph + cinder + glance
|
||||
test scenario, based on OpenStack release and whether ceph radosgw
|
||||
is flagged as present or not."""
|
||||
|
||||
if self._get_openstack_release() >= self.trusty_kilo:
|
||||
# Kilo or later
|
||||
|
@ -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 amulet
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@ -177,6 +178,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
def authenticate_cinder_admin(self, keystone_sentry, username,
|
||||
password, tenant):
|
||||
"""Authenticates admin user with cinder."""
|
||||
# NOTE(beisner): cinder python client doesn't accept tokens.
|
||||
service_ip = \
|
||||
keystone_sentry.relation('shared-db',
|
||||
'mysql:shared-db')['private-address']
|
||||
@ -279,7 +281,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
msg='Image status wait')
|
||||
if not ret:
|
||||
msg = 'Glance image failed to reach expected state.'
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
# Re-validate new image
|
||||
self.log.debug('Validating image attributes...')
|
||||
@ -299,7 +301,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
self.log.debug(msg_attr)
|
||||
else:
|
||||
msg = ('Volume validation failed, {}'.format(msg_attr))
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
return image
|
||||
|
||||
@ -343,7 +345,8 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
self.log.warn('/!\\ DEPRECATION WARNING: use '
|
||||
'delete_resource instead of delete_instance.')
|
||||
self.log.debug('Deleting instance ({})...'.format(instance))
|
||||
return self.delete_resource(nova.servers, instance, msg='nova instance')
|
||||
return self.delete_resource(nova.servers, instance,
|
||||
msg='nova instance')
|
||||
|
||||
def create_or_get_keypair(self, nova, keypair_name="testkey"):
|
||||
"""Create a new keypair, or return pointer if it already exists."""
|
||||
@ -361,8 +364,8 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
|
||||
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
|
||||
optionally as a clone of an existing volume, or optionally
|
||||
"""Create cinder volume, optionally from a glance image, OR
|
||||
optionally as a clone of an existing volume, OR optionally
|
||||
from a snapshot. Wait for the new volume status to reach
|
||||
the expected status, validate and return a resource pointer.
|
||||
|
||||
@ -373,29 +376,33 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
:param snap_id: optional snapshot id to use
|
||||
:returns: cinder volume pointer
|
||||
"""
|
||||
# Handle parameter input
|
||||
# Handle parameter input and avoid impossible combinations
|
||||
if img_id and not src_vol_id and not snap_id:
|
||||
self.log.debug('Creating cinder volume from glance image '
|
||||
'({})...'.format(img_id))
|
||||
# Create volume from image
|
||||
self.log.debug('Creating cinder volume from glance image...')
|
||||
bootable = 'true'
|
||||
elif src_vol_id and not img_id and not snap_id:
|
||||
# Clone an existing volume
|
||||
self.log.debug('Cloning cinder volume...')
|
||||
bootable = cinder.volumes.get(src_vol_id).bootable
|
||||
elif snap_id and not src_vol_id and not img_id:
|
||||
# Create volume from snapshot
|
||||
self.log.debug('Creating cinder volume from snapshot...')
|
||||
snap = cinder.volume_snapshots.find(id=snap_id)
|
||||
vol_size = snap.size
|
||||
snap_vol_id = cinder.volume_snapshots.get(snap_id).volume_id
|
||||
bootable = cinder.volumes.get(snap_vol_id).bootable
|
||||
elif not img_id and not src_vol_id and not snap_id:
|
||||
# Create volume
|
||||
self.log.debug('Creating cinder volume...')
|
||||
bootable = 'false'
|
||||
else:
|
||||
# Impossible combination of parameters
|
||||
msg = ('Invalid method use - name:{} size:{} img_id:{} '
|
||||
'src_vol_id:{} snap_id:{}'.format(vol_name, vol_size,
|
||||
img_id, src_vol_id,
|
||||
snap_id))
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
# Create new volume
|
||||
try:
|
||||
@ -407,7 +414,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
vol_id = vol_new.id
|
||||
except Exception as e:
|
||||
msg = 'Failed to create volume: {}'.format(e)
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
# Wait for volume to reach available status
|
||||
ret = self.resource_reaches_status(cinder.volumes, vol_id,
|
||||
@ -415,7 +422,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
msg="Volume status wait")
|
||||
if not ret:
|
||||
msg = 'Cinder volume failed to reach expected state.'
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
# Re-validate new volume
|
||||
self.log.debug('Validating volume attributes...')
|
||||
@ -433,7 +440,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
self.log.debug(msg_attr)
|
||||
else:
|
||||
msg = ('Volume validation failed, {}'.format(msg_attr))
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
return vol_new
|
||||
|
||||
@ -514,9 +521,9 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
|
||||
def get_ceph_osd_id_cmd(self, index):
|
||||
"""Produce a shell command that will return a ceph-osd id."""
|
||||
cmd = ("`initctl list | grep 'ceph-osd ' | awk 'NR=={} {{ print $2 }}'"
|
||||
" | grep -o '[0-9]*'`".format(index + 1))
|
||||
return cmd
|
||||
return ("`initctl list | grep 'ceph-osd ' | "
|
||||
"awk 'NR=={} {{ print $2 }}' | "
|
||||
"grep -o '[0-9]*'`".format(index + 1))
|
||||
|
||||
def get_ceph_pools(self, sentry_unit):
|
||||
"""Return a dict of ceph pools from a single ceph unit, with
|
||||
@ -528,7 +535,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
msg = ('{} `{}` returned {} '
|
||||
'{}'.format(sentry_unit.info['unit_name'],
|
||||
cmd, code, output))
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
|
||||
# Example output: 0 data,1 metadata,2 rbd,3 cinder,4 glance,
|
||||
for pool in str(output).split(','):
|
||||
@ -554,7 +561,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
msg = ('{} `{}` returned {} '
|
||||
'{}'.format(sentry_unit.info['unit_name'],
|
||||
cmd, code, output))
|
||||
raise RuntimeError(msg)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
return json.loads(output)
|
||||
|
||||
def get_ceph_pool_sample(self, sentry_unit, pool_id=0):
|
||||
@ -571,10 +578,8 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
obj_count = df['pools'][pool_id]['stats']['objects']
|
||||
kb_used = df['pools'][pool_id]['stats']['kb_used']
|
||||
self.log.debug('Ceph {} pool (ID {}): {} objects, '
|
||||
'{} kb used'.format(pool_name,
|
||||
pool_id,
|
||||
obj_count,
|
||||
kb_used))
|
||||
'{} kb used'.format(pool_name, pool_id,
|
||||
obj_count, kb_used))
|
||||
return pool_name, obj_count, kb_used
|
||||
|
||||
def validate_ceph_pool_samples(self, samples, sample_type="resource pool"):
|
||||
@ -591,9 +596,8 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
original, created, deleted = range(3)
|
||||
if samples[created] <= samples[original] or \
|
||||
samples[deleted] >= samples[created]:
|
||||
msg = ('Ceph {} samples ({}) '
|
||||
return ('Ceph {} samples ({}) '
|
||||
'unexpected.'.format(sample_type, samples))
|
||||
return msg
|
||||
else:
|
||||
self.log.debug('Ceph {} samples (OK): '
|
||||
'{}'.format(sample_type, samples))
|
||||
|
Loading…
Reference in New Issue
Block a user