~jjo/charms/trusty/nova-cloud-controller/fix-haproxy-cfg-for-neutron-api-lp1476394

« back to all changes in this revision

Viewing changes to tests/charmhelpers/contrib/amulet/utils.py

  • Committer: Corey Bryant
  • Date: 2015-07-17 12:45:29 UTC
  • mfrom: (174.2.1 nova-cloud-controller)
  • Revision ID: corey.bryant@canonical.com-20150717124529-r9ozdyr4sro5ync1
[corey.bryant,trivial] Sync charm-helpers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# You should have received a copy of the GNU Lesser General Public License
15
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
16
16
 
 
17
import amulet
17
18
import ConfigParser
18
19
import distro_info
19
20
import io
173
174
 
174
175
           Verify that the specified section of the config file contains
175
176
           the expected option key:value pairs.
 
177
 
 
178
           Compare expected dictionary data vs actual dictionary data.
 
179
           The values in the 'expected' dictionary can be strings, bools, ints,
 
180
           longs, or can be a function that evaluates a variable and returns a
 
181
           bool.
176
182
           """
177
183
        self.log.debug('Validating config file data ({} in {} on {})'
178
184
                       '...'.format(section, config_file,
185
191
        for k in expected.keys():
186
192
            if not config.has_option(section, k):
187
193
                return "section [{}] is missing option {}".format(section, k)
188
 
            if config.get(section, k) != expected[k]:
 
194
 
 
195
            actual = config.get(section, k)
 
196
            v = expected[k]
 
197
            if (isinstance(v, six.string_types) or
 
198
                    isinstance(v, bool) or
 
199
                    isinstance(v, six.integer_types)):
 
200
                # handle explicit values
 
201
                if actual != v:
 
202
                    return "section [{}] {}:{} != expected {}:{}".format(
 
203
                           section, k, actual, k, expected[k])
 
204
            # handle function pointers, such as not_null or valid_ip
 
205
            elif not v(actual):
189
206
                return "section [{}] {}:{} != expected {}:{}".format(
190
 
                       section, k, config.get(section, k), k, expected[k])
 
207
                       section, k, actual, k, expected[k])
191
208
        return None
192
209
 
193
210
    def _validate_dict_data(self, expected, actual):
195
212
 
196
213
           Compare expected dictionary data vs actual dictionary data.
197
214
           The values in the 'expected' dictionary can be strings, bools, ints,
198
 
           longs, or can be a function that evaluate a variable and returns a
 
215
           longs, or can be a function that evaluates a variable and returns a
199
216
           bool.
200
217
           """
201
218
        self.log.debug('actual: {}'.format(repr(actual)))
206
223
                if (isinstance(v, six.string_types) or
207
224
                        isinstance(v, bool) or
208
225
                        isinstance(v, six.integer_types)):
 
226
                    # handle explicit values
209
227
                    if v != actual[k]:
210
228
                        return "{}:{}".format(k, actual[k])
 
229
                # handle function pointers, such as not_null or valid_ip
211
230
                elif not v(actual[k]):
212
231
                    return "{}:{}".format(k, actual[k])
213
232
            else:
406
425
        """Convert a relative file path to a file URL."""
407
426
        _abs_path = os.path.abspath(file_rel_path)
408
427
        return urlparse.urlparse(_abs_path, scheme='file').geturl()
 
428
 
 
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.
 
432
 
 
433
        :param commands:  list of bash commands
 
434
        :param sentry_units:  list of sentry unit pointers
 
435
        :returns: None if successful; Failure message otherwise
 
436
        """
 
437
        self.log.debug('Checking exit codes for {} commands on {} '
 
438
                       'sentry units...'.format(len(commands),
 
439
                                                len(sentry_units)))
 
440
        for sentry_unit in sentry_units:
 
441
            for cmd in commands:
 
442
                output, code = sentry_unit.run(cmd)
 
443
                if code == 0:
 
444
                    self.log.debug('{} `{}` returned {} '
 
445
                                   '(OK)'.format(sentry_unit.info['unit_name'],
 
446
                                                 cmd, code))
 
447
                else:
 
448
                    return ('{} `{}` returned {} '
 
449
                            '{}'.format(sentry_unit.info['unit_name'],
 
450
                                        cmd, code, output))
 
451
        return None
 
452
 
 
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.
 
456
 
 
457
        :param sentry_unit: Pointer to amulet sentry instance (juju unit)
 
458
        :param process_name: Process name
 
459
        :returns: List of process IDs
 
460
        """
 
461
        cmd = 'pidof {}'.format(process_name)
 
462
        output, code = sentry_unit.run(cmd)
 
463
        if code != 0:
 
464
            msg = ('{} `{}` returned {} '
 
465
                   '{}'.format(sentry_unit.info['unit_name'],
 
466
                               cmd, code, output))
 
467
            amulet.raise_status(amulet.FAIL, msg=msg)
 
468
        return str(output).split()
 
469
 
 
470
    def get_unit_process_ids(self, unit_processes):
 
471
        """Construct a dict containing unit sentries, process names, and
 
472
        process IDs."""
 
473
        pid_dict = {}
 
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})
 
479
        return pid_dict
 
480
 
 
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))
 
486
 
 
487
        if len(actual) != len(expected):
 
488
            return ('Unit count mismatch.  expected, actual: {}, '
 
489
                    '{} '.format(len(expected), len(actual)))
 
490
 
 
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]
 
495
            else:
 
496
                return ('Expected sentry ({}) not found in actual dict data.'
 
497
                        '{}'.format(e_sentry_name, e_sentry))
 
498
 
 
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)))
 
502
 
 
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))
 
508
 
 
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,
 
514
                                                 a_pids))
 
515
                else:
 
516
                    self.log.debug('PID check OK: {} {} {}: '
 
517
                                   '{}'.format(e_sentry_name, e_proc_name,
 
518
                                               e_pids_length, a_pids))
 
519
        return None
 
520
 
 
521
    def validate_list_of_identical_dicts(self, list_of_dicts):
 
522
        """Check that all dicts within a list are identical."""
 
523
        hashes = []
 
524
        for _dict in list_of_dicts:
 
525
            hashes.append(hash(frozenset(_dict.items())))
 
526
 
 
527
        self.log.debug('Hashes: {}'.format(hashes))
 
528
        if len(set(hashes)) == 1:
 
529
            self.log.debug('Dicts within list are identical')
 
530
        else:
 
531
            return 'Dicts within list are not identical'
 
532
 
 
533
        return None