def update_hardware_info(machine, new_cpus, new_ram): """updates cpus and ram of machines""" results = query_machines(machine) if not results: exit_with_error(f'[INFO] No matching machines found.') # update machines, one by one for r in results: try: update = {} if new_cpus is not None: update['cpu_count'] = new_cpus if new_ram is not None: update['memory'] = new_ram * 1024 session().Machine.update(system_id=r.system_id, **update) print(f'[{r.system_id}] [{r.hostname}]' f' [OK] Updated hardware information: {update}') except MaaSError as e: exit_with_error( f'[{r.system_id}] [{r.hostname}] [ERROR] MaaS: {e}') print('Done. Refresh machine list with "jmt_refresh".')
def get_juju_status(f_name): """parses Juju status JSON and returns as dict""" try: with open(f_name, 'r') as fin: return json.load(fin) except json.JSONDecodeError as e: exit_with_error(f'[EXCEPTION] Invalid input file: {e}')
def update_domain_name(machines, new_domain): """updates domain name of machines""" if not machines: exit_with_error('[ERROR] You did not specify any machines.') results = query_machines(machines) if not results: exit_with_error(f'[INFO] No matching machines found.') try: # create domain name if needed all_domains = session().Domains.read() names = [d['name'] for d in all_domains] if new_domain not in names: print(f'[INFO] Domain {new_domain} does not exist, creating...') session().Domains.create(name=new_domain, authoritative=True) except MaaSError as e: exit_with_error(f'[ERROR] MaaS: {e}') # update machines, one by one for r in results: try: session().Machine.update(system_id=r.system_id, domain=new_domain) print(f'[{r.system_id}] [{r.hostname}] [OK] Set to {new_domain}') except MaaSError as e: exit_with_error( f'[{r.system_id}] [{r.hostname}] [ERROR] MaaS: {e}') print('Done. Refresh machine list with "mjt_refresh".')
def add_tags(new_tag, machines): """adds tags to machines""" if not machines: exit_with_error('[ERROR] You did not specify any machines.') results = query_machines(machines) if not results: exit_with_error(f'[INFO] No matching machines found.') try: # create tag name if needed all_tags = session().Tags.read() names = [t['name'] for t in all_tags] if new_tag not in names: print(f'[INFO] Tag {new_tag} does not exist, creating...') session().Tags.create(name=new_tag, description='Helper tag for nagios checks') except MaaSError as e: exit_with_error(f'[ERROR] MaaS: {e}') # updates machines, one by one for r in results: try: session().Tag.update_nodes(name=new_tag, system_id=r.system_id) print(f'[{r.system_id}] [{r.hostname}] [OK] Added tag {new_tag}') except MaaSError as e: exit_with_error( f'[{r.system_id}] [{r.hostname}] [ERROR] MaaS: {e}') print('Done. Refresh machine list with "mjt_refresh".')
def refresh_db(): """gets data from server and update cache""" print('Getting information from MaaS.') # Retrieves list of machines try: s = session() machines = s.Machines.read() powers = s.Machines.power_parameters() except MaaSError as e: exit_with_error(f'Could not GET machines: {e}') # Updates database info new_data = [] for m in machines: try: system_id = m.get('system_id', 'UNKNOWN') m_power = powers[system_id] if is_virtual_machine(m_power): print(f'[{system_id}] [{m.get("hostname")}]' f' [INFO] skipping, virtual machine') continue new_data.append( dict(power_address=m_power.get('power_address', ''), power_user=m_power.get('power_user', ''), power_pass=m_power.get('power_pass', ''), fqdn=m['fqdn'], domain=m['domain']['name'], hostname=m['hostname'], system_id=system_id, ip_addresses=', '.join(m['ip_addresses']), cpus=m['cpu_count'], ram=m['memory'] // 1024, tags=','.join(m['tag_names']))) except KeyError as e: print(f'[{system_id}] [ERROR] Missing information: {e}') # Adds new data to the database print(f'Updating the database: "{Config.sqlite_db}"') MaaSCache.insert(new_data).on_conflict_replace().execute() db.commit() print('Done.')
def get_machine(system_id): """asks MaaS for information and print it out""" try: m = session().Machine.read(system_id=system_id) except MaaSError as e: exit_with_error(f'[{system_id}] [ERROR] {e}') print(json.dumps({ 'system_id': system_id, 'ip_addresses': ','.join(m['ip_addresses']), 'tags': ','.join(m['tag_names']), 'hostname': m['hostname'], 'domain': m['domain']['name'], 'fqdn': m['fqdn'], 'status': m['status_name'] }, indent=2))
def update_host_name(machine, new_hostname): """updates host name of machine""" results = query_machines([machine]) if not results: exit_with_error(f'[INFO] No matching machines found.') # update machines, one by one r = results[0] try: session().Machine.update(system_id=r.system_id, hostname=new_hostname) print(f'[{r.system_id}] [{r.hostname}] [OK]' f' Updated hostname to {new_hostname}') except MaaSError as e: exit_with_error(f'[{r.system_id}] [{r.hostname}] [ERROR] MaaS: {e}') print('Done. Refresh machine list with "mjt_refresh".')
def parse_pynag_services(f_name): """parses Nagios hosts. Returns them as a {'ip_address': 'host_name'} dict""" with open(f_name, 'r') as fin: pynag = fin.read() result = defaultdict(lambda: []) try: for line in pynag.split('\n'): if not line: continue hostname, service = line.split('|') result[hostname].append(service) except (TypeError, ValueError) as e: exit_with_error(f'[EXCEPTION] Invalid Nagios hosts format: {e}') return result
def parse_pynag_hosts(f_name): """parses Nagios hosts. Returns them as a {'ip_address': 'host_name'} dict""" with open(f_name, 'r') as fin: pynag = fin.read() result = {} try: for line in pynag.split('\n'): if not line: continue address, hostname = line.split('|') result[address] = hostname except (TypeError, ValueError) as e: exit_with_error(f'[EXCEPTION] Invalid Nagios hosts format: {e}') return result
def ipmi_sel(cmd, machines): """lists or clear SEL of @machines""" results = query_machines(machines) if not results: exit_with_error(f'[INFO] No matching machines found.') # update machines, one by one for r in results: print(f'## [{r.system_id}] [{r.hostname}]') command_line = [ 'ipmi-sel', '-h', r.power_address, '-u', r.power_user, '-p', r.power_pass ] if cmd == 'clear': command_line.append('--clear') subprocess.run(command_line)
def main(): """parses arguments and run proper command""" parser = argparse.ArgumentParser( description='Manage node script results. ' 'See maasjuju_toolkit/maas/script_results.py for examples' ) parser.add_argument( 'command', choices=['list', 'suppress', 'delete', 'unsuppress', 'suppress_id', 'delete_id', 'unsuppress_id'], help='What to do' ) parser.add_argument( 'machines', type=str, default=[], nargs='*' ) parser.add_argument( '--script-id', type=str ) for x in ['Installation', 'Passed', 'Commissioning', 'Testing', 'Skipped', 'Aborted']: parser.add_argument( f'--no-{x.lower()}', const=x, action='append_const', dest='skip', help=f'Ignore {x} script results' ) # set skip categories, always skip running tests args = parser.parse_args() if args.skip is None: skip = {'Running'} else: skip = {'Running', *args.skip} # check if script id is required if args.command.endswith('_id') and args.script_id is None: exit_with_error('[ERROR] No script id passed') script_results(args.command, args.machines, args.script_id, skip)
def get_script_results(machine, skip): """returns all script group results, along with result status""" if isinstance(machine, str): query = [machine] else: query = machine machines = query_machines(query) if not machines: exit_with_error(f'UNKNOWN: No matching machine: {machine}', code=3) api = session() results = {} for m in machines: scripts = api.NodeScriptResults.read(system_id=m.system_id) for s in scripts: if s['type_name'] in skip or s['status_name'] in skip: continue if m.system_id not in results: results[m.system_id] = {} suppressed = [r for r in s['results'] if r['suppressed']] ids = ','.join([str(r['id']) for r in s['results']]) results[m.system_id].update({ s['id']: { 'type': s['type_name'], 'status': s['status_name'], 'total': len(s['results']), 'suppressed': len(suppressed), 'individual_ids': ids } }) return results
def script_results(command, machine, script_id, skip): """calls appropriate command""" if command == 'list': print(json.dumps(get_script_results(machine, skip), indent=2)) elif command in ['suppress', 'unsuppress']: set_suppressed(machine, skip, bool(command == 'suppress')) elif command in ['suppress_id', 'unsuppress_id']: if machine == '': exit_with_error('ERROR: No machine name') if script_id == '': exit_with_error('ERROR: No script id') set_suppressed_id(machine, script_id, bool(command == 'suppress_id')) elif command == 'delete': delete_results(machine, skip) elif command == 'delete_id': delete_result_id(machine, script_id) else: exit_with_error(f'Unknown command: {command}')
def nagios_juju_deps(juju_status, pynag_hosts, pynag_services, subnet, outfile): """generates Nagios host and service dependencies and writes to outfile""" hosts = parse_pynag_hosts(pynag_hosts) services = parse_pynag_services(pynag_services) juju = get_juju_status(juju_status) output = '' try: # p_address, p_host: IP address and hostname of parent host # d_address, d_host: IP address and hostname of dependent host for machine in juju['machines'].values(): p_host = None for p_address in machine['ip-addresses']: p_host = hosts.get(p_address) if p_host is not None: break if p_host is None: print('[WARN] [{}] Unknown Nagios host: ({})'.format( machine['display-name'], machine['ip-addresses'])) cons = machine.get('containers') if cons: print('[WARN] [{}] Has {} containers. Will create'.format( machine['display-name'], len(cons))) ip_address = None for ip in machine['ip-addresses']: if IPAddress(ip_address) in IPNetwork(subnet): ip_address = ip print(f'Will use IP address {ip_address}') break if not ip_address: print('[WARN] [{}] No IPs in {}'.format( machine['display-name'], subnet)) print('Choose which IP address to use:') while ip_address not in machine['ip-addresses']: ip_address = input('> ') output += (HOST_TEMPLATE.replace( '{COMMENT}', 'Auto created by mjt_juju_nagios_deps').replace( '{DISPLAY_NAME}', machine['display-name']).replace( '{IP_ADDRESS}', ip_address)) p_host = machine['display-name'] else: print('[WARN] [{}] has no containers, skipping'.format( machine['display-name'])) if WELL_KNOWN_SERVICE not in services.get(p_host, []): print('[WARN] [{}] Service {} does not exist'.format( p_host, WELL_KNOWN_SERVICE)) try: containers = machine['containers'] except KeyError: continue for name, container in containers.items(): d_host = None for d_address in container['ip-addresses']: d_host = hosts.get(d_address) if d_host is not None: break if d_host is None: print('[WARN] [{}] Unknown Nagios host ({})'.format( container['instance-id'], container['ip-addresses'])) continue # adds host dependencies output += (HOST_DEPENDENCY_TEMPLATE.replace( '{COMMENT}', f'({container["instance-id"]}) ' f'-> {machine["display-name"]}').replace( '{PARENT}', p_host).replace('{CHILD}', d_host)) for service in services.get(d_host, []): output += (SERVICE_DEPENDENCY_TEMPLATE.replace( '{COMMENT}', f'({container["instance-id"]}/{service}) -> ' f'{machine["display-name"]}/{WELL_KNOWN_SERVICE}' ).replace('{PARENT_HOST}', p_host).replace( '{PARENT_SERVICE}', WELL_KNOWN_SERVICE).replace( '{DEPENDENT_HOST}', d_host).replace('{DEPENDENT_SERVICE}', service)) except KeyError as e: exit_with_error( f'[EXCEPTION] Input data is not JSON-formatted Juju status: {e}') try: with open(outfile, 'w') as f_out: f_out.write(output) print('[SUCCESS] Configuration was written to', outfile) except OSError as e: print(f'[EXCEPTION] Writing to {outfile} failed: {e}')