77
def get_ubuntu_release_from_sentry(self, sentry_unit):
78
"""Get Ubuntu release codename from sentry unit.
80
:param sentry_unit: amulet sentry/service unit pointer
81
:returns: list of strings - release codename, failure message
84
cmd = 'lsb_release -cs'
85
release, code = sentry_unit.run(cmd)
87
self.log.debug('{} lsb_release: {}'.format(
88
sentry_unit.info['unit_name'], release))
90
msg = ('{} `{}` returned {} '
91
'{}'.format(sentry_unit.info['unit_name'],
93
if release not in self.ubuntu_releases:
94
msg = ("Release ({}) not found in Ubuntu releases "
95
"({})".format(release, self.ubuntu_releases))
73
98
def validate_services(self, commands):
76
Verify the specified services are running on the corresponding
99
"""Validate that lists of commands succeed on service units. Can be
100
used to verify system services are running on the corresponding
103
:param commands: dict with sentry keys and arbitrary command list vals
104
:returns: None if successful, Failure string message otherwise
106
self.log.debug('Checking status of system services...')
108
# /!\ DEPRECATION WARNING (beisner):
109
# New and existing tests should be rewritten to use
110
# validate_services_by_name() as it is aware of init systems.
111
self.log.warn('/!\\ DEPRECATION WARNING: use '
112
'validate_services_by_name instead of validate_services '
113
'due to init system differences.')
79
115
for k, v in six.iteritems(commands):
81
117
output, code = k.run(cmd)
86
122
return "command `{}` returned {}".format(cmd, str(code))
125
def validate_services_by_name(self, sentry_services):
126
"""Validate system service status by service name, automatically
127
detecting init system based on Ubuntu release codename.
129
:param sentry_services: dict with sentry keys and svc list values
130
:returns: None if successful, Failure string message otherwise
132
self.log.debug('Checking status of system services...')
134
# Point at which systemd became a thing
135
systemd_switch = self.ubuntu_releases.index('vivid')
137
for sentry_unit, services_list in six.iteritems(sentry_services):
138
# Get lsb_release codename from unit
139
release, ret = self.get_ubuntu_release_from_sentry(sentry_unit)
143
for service_name in services_list:
144
if (self.ubuntu_releases.index(release) >= systemd_switch or
145
service_name == "rabbitmq-server"):
147
cmd = 'sudo service {} status'.format(service_name)
148
elif self.ubuntu_releases.index(release) < systemd_switch:
150
cmd = 'sudo status {}'.format(service_name)
152
output, code = sentry_unit.run(cmd)
153
self.log.debug('{} `{}` returned '
154
'{}'.format(sentry_unit.info['unit_name'],
157
return "command `{}` returned {}".format(cmd, str(code))
89
160
def _get_config(self, unit, filename):
90
161
"""Get a ConfigParser object for parsing a unit's config file."""
91
162
file_contents = unit.file_contents(filename)
112
191
for k in expected.keys():
113
192
if not config.has_option(section, k):
114
193
return "section [{}] is missing option {}".format(section, k)
115
if config.get(section, k) != expected[k]:
195
actual = config.get(section, k)
197
if (isinstance(v, six.string_types) or
198
isinstance(v, bool) or
199
isinstance(v, six.integer_types)):
200
# handle explicit values
202
return "section [{}] {}:{} != expected {}:{}".format(
203
section, k, actual, k, expected[k])
204
# handle function pointers, such as not_null or valid_ip
116
206
return "section [{}] {}:{} != expected {}:{}".format(
117
section, k, config.get(section, k), k, expected[k])
207
section, k, actual, k, expected[k])
120
210
def _validate_dict_data(self, expected, actual):
322
414
def endpoint_error(self, name, data):
323
415
return 'unexpected endpoint data in {} - {}'.format(name, data)
417
def get_ubuntu_releases(self):
418
"""Return a list of all Ubuntu releases in order of release."""
419
_d = distro_info.UbuntuDistroInfo()
420
_release_list = _d.all
421
self.log.debug('Ubuntu release list: {}'.format(_release_list))
424
def file_to_url(self, file_rel_path):
425
"""Convert a relative file path to a file URL."""
426
_abs_path = os.path.abspath(file_rel_path)
427
return urlparse.urlparse(_abs_path, scheme='file').geturl()
429
def check_commands_on_units(self, commands, sentry_units):
430
"""Check that all commands in a list exit zero on all
431
sentry units in a list.
433
:param commands: list of bash commands
434
:param sentry_units: list of sentry unit pointers
435
:returns: None if successful; Failure message otherwise
437
self.log.debug('Checking exit codes for {} commands on {} '
438
'sentry units...'.format(len(commands),
440
for sentry_unit in sentry_units:
442
output, code = sentry_unit.run(cmd)
444
self.log.debug('{} `{}` returned {} '
445
'(OK)'.format(sentry_unit.info['unit_name'],
448
return ('{} `{}` returned {} '
449
'{}'.format(sentry_unit.info['unit_name'],
453
def get_process_id_list(self, sentry_unit, process_name):
454
"""Get a list of process ID(s) from a single sentry juju unit
455
for a single process name.
457
:param sentry_unit: Pointer to amulet sentry instance (juju unit)
458
:param process_name: Process name
459
:returns: List of process IDs
461
cmd = 'pidof {}'.format(process_name)
462
output, code = sentry_unit.run(cmd)
464
msg = ('{} `{}` returned {} '
465
'{}'.format(sentry_unit.info['unit_name'],
467
amulet.raise_status(amulet.FAIL, msg=msg)
468
return str(output).split()
470
def get_unit_process_ids(self, unit_processes):
471
"""Construct a dict containing unit sentries, process names, and
474
for sentry_unit, process_list in unit_processes.iteritems():
475
pid_dict[sentry_unit] = {}
476
for process in process_list:
477
pids = self.get_process_id_list(sentry_unit, process)
478
pid_dict[sentry_unit].update({process: pids})
481
def validate_unit_process_ids(self, expected, actual):
482
"""Validate process id quantities for services on units."""
483
self.log.debug('Checking units for running processes...')
484
self.log.debug('Expected PIDs: {}'.format(expected))
485
self.log.debug('Actual PIDs: {}'.format(actual))
487
if len(actual) != len(expected):
488
return ('Unit count mismatch. expected, actual: {}, '
489
'{} '.format(len(expected), len(actual)))
491
for (e_sentry, e_proc_names) in expected.iteritems():
492
e_sentry_name = e_sentry.info['unit_name']
493
if e_sentry in actual.keys():
494
a_proc_names = actual[e_sentry]
496
return ('Expected sentry ({}) not found in actual dict data.'
497
'{}'.format(e_sentry_name, e_sentry))
499
if len(e_proc_names.keys()) != len(a_proc_names.keys()):
500
return ('Process name count mismatch. expected, actual: {}, '
501
'{}'.format(len(expected), len(actual)))
503
for (e_proc_name, e_pids_length), (a_proc_name, a_pids) in \
504
zip(e_proc_names.items(), a_proc_names.items()):
505
if e_proc_name != a_proc_name:
506
return ('Process name mismatch. expected, actual: {}, '
507
'{}'.format(e_proc_name, a_proc_name))
509
a_pids_length = len(a_pids)
510
if e_pids_length != a_pids_length:
511
return ('PID count mismatch. {} ({}) expected, actual: '
512
'{}, {} ({})'.format(e_sentry_name, e_proc_name,
513
e_pids_length, a_pids_length,
516
self.log.debug('PID check OK: {} {} {}: '
517
'{}'.format(e_sentry_name, e_proc_name,
518
e_pids_length, a_pids))
521
def validate_list_of_identical_dicts(self, list_of_dicts):
522
"""Check that all dicts within a list are identical."""
524
for _dict in list_of_dicts:
525
hashes.append(hash(frozenset(_dict.items())))
527
self.log.debug('Hashes: {}'.format(hashes))
528
if len(set(hashes)) == 1:
529
self.log.debug('Dicts within list are identical')
531
return 'Dicts within list are not identical'