From 55418dcb6bc5dcc5a629aa4f816c78d36251301b Mon Sep 17 00:00:00 2001 From: Aleksandra Fedorova Date: Thu, 22 Jun 2017 19:57:33 +0200 Subject: [PATCH] Add script which compares two minions Script can be used to compare pillars, highstates and grains for two minions, or for two different snapshots of the salt states code. To use it for pull request verification one can: 1) checkout destination branch to dirA, 2) checkout pull-request branch to dirB, 3) setup minion config /opt/minionA/minion to use states and pillars from dirA 4) setup minion config in /opt/minionB/minion to use states and pillars from dirB 5) put the same grains file into /opt/minionA/grains and /opt/minionB/grains 6) run the script This way you'll get the difference given by the salt states code changes applied to the same minion (i.e. grains input) --- utils/compare_minions.py | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 utils/compare_minions.py diff --git a/utils/compare_minions.py b/utils/compare_minions.py new file mode 100755 index 0000000..a03276d --- /dev/null +++ b/utils/compare_minions.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python + +import salt.config +import salt.loader + +import json +import json_delta + +try: + from colorama import Fore, Back, Style, init + init() +except ImportError: # fallback so that the imported classes always exist + class ColorFallback(): + def __getattr__(self, name): + return '' + Fore = Back = Style = ColorFallback() + + +def color_diff(diff): + """Colorize lines in the diff generator object""" + + for line in diff: + if line.startswith('+'): + yield Fore.GREEN + line + Fore.RESET + elif line.startswith('-'): + yield Fore.RED + line + Fore.RESET + else: + yield line + + +class Minion(): + + default_config = '/opt/{name}/minion' + + def __init__(self, name=None, config=None, **salt_params): + + if not config: + config = self.default_config.format(name=name) + self.config = config + + self.salt_params = salt_params + + __opts__ = salt.config.minion_config(self.config) + __grains__ = salt.loader.grains(__opts__) + __opts__['grains'] = __grains__ + __utils__ = salt.loader.utils(__opts__) + self.salt = salt.loader.minion_mods(__opts__, utils=__utils__) + + @property + def highstate(self): + return json.loads(json.dumps(self.salt['state.show_highstate'](**self.salt_params))) + + @property + def pillar_items(self): + return json.loads(json.dumps(self.salt['pillar.items'](**self.salt_params))) + + @property + def grains_items(self): + return json.loads(json.dumps(self.salt['grains.items']())) + + def __str__(self): + return json.dumps( + { + "highstate": self.highstate, + "pillar_items": self.pillar_items, + "grains_items": self.grains_items, + }, + indent=4 + ) + + +def compare_minions(minionA, minionB): + '''Generate colorized unified diff of two minions''' + + comparable_properties = [ + 'highstate', + 'pillar_items', + 'grains_items', + ] + + diffs = {} + for key in comparable_properties: + diffs[key] = color_diff( + json_delta.udiff( + getattr(minionA, key), + getattr(minionB, key), + ) + ) + + result = "\n".join( + [ + "\n".join( + [key.upper(), "\n".join(value)] + ) for key, value in diffs.items() + ] + ) + return result + + +if __name__ == "__main__": + + minionA = Minion('minionA') + minionB = Minion('minionB') + + print compare_minions(minionA, minionB)