Theo Chatzimichos 5f6f74
#!/usr/bin/python3
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
# For description and usage, see the argparse options at the end of the file
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
import argparse
4da201
import re
Theo Chatzimichos 5f6f74
import sys
ac656a
from pathlib import Path
ac656a
Theo Chatzimichos 5f6f74
import yaml
ac656a
from get_roles import get_roles, get_roles_of_one_minion
ac656a
from ldap3 import ALL, Connection, Server
Theo Chatzimichos 5f6f74
ac656a
spn_regex = r'spn=([\w-]+)@infra\.opensuse\.org,o=heroes'  # is there a better way to fetch only the names from kani?
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
def get_admins_of_a_role(admins, role):
Theo Chatzimichos 5f6f74
    results = {}
Theo Chatzimichos 5f6f74
    all_roles = get_roles()
Theo Chatzimichos 5f6f74
    if role not in all_roles:
Theo Chatzimichos 5f6f74
        print('Role not found')
Theo Chatzimichos 5f6f74
        sys.exit(1)
Theo Chatzimichos 5f6f74
4da201
    conn.search('%s' % BASE_DN, '(cn=%s-admins)' % role, attributes=['cn', 'member'])
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
    try:
4da201
        members = conn.entries[0].member
Theo Chatzimichos 5f6f74
    except IndexError:
Theo Chatzimichos 5f6f74
        return results
Theo Chatzimichos 5f6f74
4da201
    for file in Path('pillar/id').glob('*.sls'):
4da201
        sls = file.name
4da201
        minion = file.stem.split('_')[0]
4da201
        roles = get_roles_of_one_minion(sls)
Theo Chatzimichos 5f6f74
        if role in roles:
Theo Chatzimichos 5f6f74
            for member in members:
4da201
                member = re.search(spn_regex, member).group(1)
Theo Chatzimichos 5f6f74
                results[member] = admins[member]
ac656a
                results[member]['roles'].append(f'{minion} ({role})')
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
    return results
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
def get_admins_of_a_server(admins, server):
Theo Chatzimichos 5f6f74
    results = {}
4da201
    domain = '.infra.opensuse.org'
4da201
    if server.endswith(domain):
4da201
        minion = server.replace(domain, '')
4da201
    else:
4da201
        minion = server
4da201
        server = f'{server}{domain}'
4da201
    server = f'{server.replace(".", "_")}.sls'
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
    try:
Theo Chatzimichos 4f2316
        roles = get_roles_of_one_minion(server)
Theo Chatzimichos 5f6f74
    except FileNotFoundError:
Theo Chatzimichos 5f6f74
        print('Server not found')
Theo Chatzimichos 5f6f74
        sys.exit(1)
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
    if roles:
Theo Chatzimichos 5f6f74
        for role in roles:
Theo Chatzimichos 5f6f74
            for admin, data in admins.items():
Theo Chatzimichos 5f6f74
                for group in data['groups']:
4da201
                    if group.split('-')[0] == role:
Theo Chatzimichos 5f6f74
                        results[admin] = data
ac656a
                        results[admin]['roles'].append(f'{minion} ({role})')
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
    return results
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
def output_nice(results):
Theo Chatzimichos 5f6f74
    RESULT_TMPL = '{:^30} {separ} {:^20} {separ} {:^30} {separ} {:^30}'
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
    print(RESULT_TMPL.format('SERVER (ROLE)', 'USERNAME', 'REAL NAME', 'EMAIL', separ='|'))
Theo Chatzimichos 5f6f74
    print(RESULT_TMPL.format('', '', '', '', separ='+').replace(' ', '-'))
Theo Chatzimichos 5f6f74
    for admin, data in sorted(results.items()):
Theo Chatzimichos 5f6f74
        for role in data['roles']:
Theo Chatzimichos 5f6f74
            print(RESULT_TMPL.format(role, admin, data['name'], data['mail'], separ='|'))
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
4da201
BASE_DN = 'o=heroes'
Theo Chatzimichos 5f6f74
admins = {}
Theo Chatzimichos 5f6f74
EXCLUDE_ACCOUNTS = ['admin', 'guest', 'mufasa', 'monitor', 'wiki']
Theo Chatzimichos 5f6f74
4da201
parser = argparse.ArgumentParser('Collects the admins of a server or of a role, and returns them in a nice output, or as a python dictionary or a yaml hash - will not operate without VPN connectivity!')
4da201
parser.add_argument('-o', '--out', choices=['nice', 'yaml', 'python'], help='Select the output format. Options: nice (the default nice output), python (as a python dictionary), yaml (as a yaml hash)')
Theo Chatzimichos 5f6f74
parser.add_argument('-r', '--role', nargs=1, help='Collect the admins of a given role')
4da201
parser.add_argument('-s', '--server', nargs=1, help='Collect the admins of a given server (short name or i.o.o FQDN)')
Theo Chatzimichos 5f6f74
args = parser.parse_args()
Theo Chatzimichos 5f6f74
4da201
ldap_server = Server('ldap.infra.opensuse.org', get_info=ALL, use_ssl=True)
Theo Chatzimichos 5f6f74
conn = Connection(ldap_server)
Theo Chatzimichos 5f6f74
conn.open()
Theo Chatzimichos 5f6f74
ldap_exclude_accounts = ''
Theo Chatzimichos 5f6f74
for account in EXCLUDE_ACCOUNTS:
Theo Chatzimichos 5f6f74
    ldap_exclude_accounts += '(uid=%s)' % account
4da201
conn.search('%s' % BASE_DN, '(&(uid=*)(!(|%s)))' % ldap_exclude_accounts, attributes=['uid', 'gecos', 'mail'])
Theo Chatzimichos 5f6f74
all_admins_from_ldap = conn.entries
Theo Chatzimichos 5f6f74
for admin in all_admins_from_ldap:
4da201
    if len(admin.gecos):
4da201
        admins[admin.uid[0]] = {'name': admin.gecos[0], 'mail': admin.mail[0] if len(admin.mail) else 'n/a', 'groups': [], 'roles': []}
Theo Chatzimichos 5f6f74
4da201
conn.search('%s' % BASE_DN, '(&(class=group)(cn=*-admins))', attributes=['cn', 'member'])
Theo Chatzimichos 5f6f74
all_admin_groups_from_ldap = conn.entries
Theo Chatzimichos 5f6f74
for group in all_admin_groups_from_ldap:
4da201
    members = group.member
Theo Chatzimichos 5f6f74
    for member in members:
4da201
        member = re.search(spn_regex, member).group(1)
4da201
        admins[member]['groups'].append(str(group.cn))
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
if args.role:
Theo Chatzimichos 5f6f74
    results = get_admins_of_a_role(admins, args.role[0])
Theo Chatzimichos 5f6f74
elif args.server:
Theo Chatzimichos 5f6f74
    results = get_admins_of_a_server(admins, args.server[0])
Theo Chatzimichos 5f6f74
else:
Theo Chatzimichos 5f6f74
    parser.print_help()
Theo Chatzimichos 5f6f74
    sys.exit(1)
Theo Chatzimichos 5f6f74
Theo Chatzimichos 5f6f74
if results:
Theo Chatzimichos 5f6f74
    if args.out == 'yaml':
Theo Chatzimichos 5f6f74
        print(yaml.dump(results, default_flow_style=False))
Theo Chatzimichos 5f6f74
    elif args.out == 'python':
Theo Chatzimichos 5f6f74
        print(results)
Theo Chatzimichos 5f6f74
    else:
Theo Chatzimichos 5f6f74
        output_nice(results)