Blob Blame History Raw
#!/usr/bin/python3

# For description and usage, see the argparse options at the end of the file

import argparse
import os
from sys import exit

import yaml


def read_file_skip_jinja(filename):
    ''' reads a file and returns its content, except lines starting with '{%' '''
    non_jinja_lines = []

    with open(filename) as f:
        for line in f.read().split('\n'):
            if not ('{%' in line or '{{' in line or '{#' in line):
                non_jinja_lines.append(line)

    return '\n'.join(non_jinja_lines)


def get_roles_of_one_minion(minion):
    if not minion.endswith('.sls'):
      if '.' not in minion and '_' not in minion:
        minion = f'{minion}.infra.opensuse.org'
      minion = minion.replace('.', '_') + '.sls'
    file = f'pillar/id/{minion}'
    try:
        content = read_file_skip_jinja(file)
    except FileNotFoundError:
        print(f'File {file} not found.')
        return []
    try:
        roles = yaml.safe_load(content)['roles']
    except KeyError:
        roles = []

    return roles


def get_minions_with_role(role):
    minions = []
    for sls in os.listdir('pillar/id'):
      if sls.endswith('.sls'):
          for item in get_roles_of_one_minion(sls):
              if item == role:
                  minions.append(os.path.splitext(sls)[0].replace('_', '.'))

    return sorted(minions)


def get_roles(with_base=False):
    roles = []

    for sls in os.listdir('pillar/id'):
        if sls == 'README.md':
            continue

        _roles = get_roles_of_one_minion(sls)
        for item in _roles:
            roles.append(item)

    roles = sorted(set(roles))

    if with_base:
      roles = ['base'] + roles

    return roles


def get_roles_including(query):
    """
    Return roles including the specified string in their state SLS file.
    """
    roles = []
    for role in get_roles():
        role2 = role.replace('.', '/')
        file = f'salt/role/{role2}.sls'
        if not os.path.isfile(file):
          file = f'salt/role/{role2}/init.sls'
        role_sls = read_file_skip_jinja(file)
        if query in role_sls:
            roles.append(role)
    return roles


def print_roles():
    parser = argparse.ArgumentParser('Collects all the roles that are assigned to a minion, and returns them as a python array, a yaml list or a plain list (parsable by bash)')
    parser.add_argument('-o', '--out', choices=['bash', 'python', 'yaml'], help='Select different output format. Options: bash (default), python, yaml')
    parser.add_argument('-i', '--including', help='Only print roles including the specified string in their state file.')
    parser.add_argument('-m', '--minion', help='Only print roles assigned to the specified minion.')
    parser.add_argument('-r', '--role', help='Print minions the specified role is assigned to.')
    args = parser.parse_args()

    if args.including and args.minion:
      print('Combining --including and --minion is not supported. But you can send a patch for it. :)')
      exit(1)

    if ( args.including or args.minion ) and args.role:
      print('Combining --role with --including or --minion is not possible.')
      exit(1)

    if args.including:
        roles = get_roles_including(args.including)
    elif args.minion:
        roles = get_roles_of_one_minion(args.minion)
    elif args.role:
        roles = get_minions_with_role(args.role)
    else:
        roles = get_roles()
    if args.out == 'python':
        print(roles)
    elif args.out == 'yaml':
        print(yaml.dump({'roles': roles}, default_flow_style=False))
    else:
        print('\n'.join(roles))


if __name__ == "__main__":
    print_roles()