def test_icinga1_queries(): # Test getting correct results when querying Icinga1 for multiple key-value pairs config = Icinga1Config() service1 = { 'host_name': 'host1', 'object_type': ObjectType.SERVICE, 'check_command': 'test_check', 'notifications_enabled': True } service2 = { 'host_name': 'host2', 'object_type': ObjectType.SERVICE, 'check_command': 'test_check', 'notifications_enabled': True } service3 = { 'host_name': 'host2', 'object_type': ObjectType.SERVICE, 'check_command': 'test_check2', 'notifications_enabled': True } config._objects = [service1, service2, service3] result = config.get_services(host_name=service1['host_name']) assert len(result) == 1 assert result[0] == service1 result = config.get_services(host_name='host2', check_command=service3['check_command']) assert len(result) == 1 assert result[0] == service3
def migrate_host_notification_states(simulate=True, suffix=MIGRATION_COMMENT_SUFFIX, hostname=None): """ Migrate enable_notifications for hosts. States are only migrated if host notifications are enabled but host status notifications are disabled. @param simulate: Simulate (don't perform requests to Icinga2) @type simulate: bool @param suffix: Suffix to append to Icinga2 notes @type suffix: str @param hostname: Host (leave empty for all hosts) @type hostname: str :return: """ icinga1 = Icinga1Config() icinga2 = Icinga2Config() icinga2_hosts = [host['name'] for host in icinga2.get_hosts()] icinga1_host_dict = icinga1.get_hosts_dict() if hostname: hostnames = [hostname] else: hostnames = icinga1_host_dict.keys() icinga1_status_dict = icinga1.get_hoststatus_by_host() for hostname in hostnames: if hostname not in icinga1_host_dict: logger.error("Host {} not available in Icinga1".format(hostname)) elif hostname not in icinga2_hosts: logger.error("Host {} not available in Icinga2".format(hostname)) else: host_enabled = icinga1_host_dict[hostname][ 'notifications_enabled'] == '1' host_status_enabled = icinga1_status_dict[hostname][ 'notifications_enabled'] == '1' if host_enabled and not host_status_enabled: logger.debug(icinga1_status_dict[hostname]) logger.info( "Disabling notifications for host: {}".format(hostname)) if not simulate: try: response = icinga2.set_host_notifications( hostname, False, notes='Migrated notification state from Icinga1' + suffix) logger.debug(response) except Exception as error: logger.error( "Could not migrate host notification state for host {}. " "Error: {}".format(hostname, error)) return icinga1_status_dict
def compare_downtimes(icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ List all downtimes of Icinga1/2, ordered per host :param icinga1: Icinga1Config :param icinga2: Icinga2Config :return: """ output = sys.stdout sort_by = 'start_time' icinga1_downtimes = \ sorted(icinga1.hostdowntimes + icinga1.servicedowntimes, key=lambda x: x[sort_by]) icinga2_downtimes = sorted(icinga2.get_downtimes(), key=lambda x: x['attrs'][sort_by]) icinga1_hosts = [downtime['host_name'] for downtime in icinga1_downtimes] icinga2_hosts = [ downtime['attrs']['host_name'] for downtime in icinga2_downtimes ] for host in set(icinga1_hosts + icinga2_hosts): downtimes_1 = [ downtime for downtime in icinga1_downtimes if downtime['host_name'] == host ] downtimes_2 = [ downtime for downtime in icinga2_downtimes if downtime['attrs']['host_name'] == host ] if downtimes_1 or downtimes_2: output.write(host + '\n') if downtimes_1: output.write("Icinga1:\n") for downtime in downtimes_1: start_time_stamp = datetime.utcfromtimestamp( int(downtime['start_time'])) end_time_stamp = datetime.utcfromtimestamp( int(downtime['start_time'])) output.write("Comment: {}, Start: {}, End: {}\n".format( downtime['comment'], format_date(start_time_stamp), format_date(end_time_stamp))) output.write('\n') if downtimes_2: output.write("Icinga2:\n") for downtime in downtimes_2: start_time_stamp = datetime.utcfromtimestamp( int(downtime['attrs']['start_time'])) end_time_stamp = datetime.utcfromtimestamp( int(downtime['attrs']['start_time'])) output.write("Comment: {}, Start: {}, End: {}\n".format( downtime['attrs']['comment'], format_date(start_time_stamp), format_date(end_time_stamp))) output.write('\n')
def compare_contacts(icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ Compare contacts. - check if emails from Icinga1 are existing in Icinga2 - check if emails and phone numbers (=pager) are correct - check if hosts/services have been assigned the same contacts :param icinga1: Icinga1Config :param icinga2: Icinga2Config :return: compare result """ diff = defaultdict(list) icinga1_contacts = icinga1.contacts icinga2_contacts = icinga2.get_users() for icinga1_contact in icinga1_contacts: icinga2_contact = [ icinga2_contact for icinga2_contact in icinga2_contacts if icinga2_contact['attrs']['name'] == icinga1_contact['contact_name'] ] if not icinga2_contact: print( "Contact missing in Icinga2: alias:'{}', contact_name:'{}', email:'{}'" .format(icinga1_contact.get('alias', ''), icinga1_contact['contact_name'], icinga1_contact.get('email', ''))) if icinga2_contact and 'pager' in icinga1_contact.keys(): icinga2_contact = icinga2_contact[0] icinga1_pager = re.sub(r'^00', '', icinga1_contact['pager']) icinga2_pager = icinga2_contact['attrs']['pager'] if icinga1_pager != icinga2_pager: print( "Wrong pager information for {}. Icinga1: {}, Icinga2: {}". format(icinga1_contact['contact_name'], icinga1_pager, icinga2_pager)) diff['wrong_pager'].append((icinga1_contact['contact_name'], icinga1_pager, icinga2_pager)) icinga1_mail = icinga1_contact.get('email', None) icinga2_mail = ndict(icinga2_contact)['attrs']['email'] if icinga1_mail and icinga1_mail != icinga2_mail: print( "Wrong email information for {}. Icinga1: {}, Icinga2: {}". format(icinga1_contact['contact_name'], icinga1_mail, icinga2_mail)) diff['wrong_email'].append((icinga1_contact['contact_name'], icinga1_mail, icinga2_mail)) return diff
def compare_hosts(icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ Compare hosts: * check if hosts exist in Icinga2 * compare if SLA is set in Icinga2 if set in Icinga1 * Compare check_interval * Compare max_check_attempts * Compare retry_interval :param icinga1: Icinga1Config :param icinga2: Icinga2Config :return: """ icinga1_hosts = icinga1.get_hosts_dict() icinga2_hosts = icinga2.get_hosts_dict() result = defaultdict(list) compare_attributes = [ 'check_interval', 'max_check_attempts', 'retry_interval' ] for hostname in sorted(icinga1_hosts): if hostname not in icinga2_hosts: result['missing'].append(hostname) print("{}: {}".format('Missing in Icinga2', hostname)) else: icinga1_host = icinga1_hosts[hostname] icinga2_host = icinga2_hosts[hostname] if icinga1_host.get('notes') == 'no-sla' \ and not ndict(icinga2_host)['attrs']['vars']['nosla']: result['nosla'].append(hostname) print("{}: {} (Icinga1: {}, Icinga2: {})".format( "Different SLA: ", hostname, icinga1_host.get('notes') == 'no-sla', 'nosla' in icinga2_host['attrs']['vars'])) for attrib in compare_attributes: icinga1_attrib = float(icinga1_host[attrib]) icinga2_attrib = float(icinga2_host['attrs'][attrib]) if attrib in ['retry_interval', 'check_interval']: icinga1_attrib *= 60 if icinga1_attrib != icinga2_attrib: result[attrib].append((hostname, icinga1_host[attrib], icinga2_host['attrs'][attrib])) print("{}: {} (Icinga1: {}, Icinga2: {})".format( "Different {} ".format(attrib), hostname, icinga1_host[attrib], icinga2_host['attrs'][attrib])) return result
def test_object_parser(): config = Icinga1Config(objects_files=sample_objects, status_files=sample_status) assert config.objects[0] == { 'object_type': 'host', 'host_name': 'awesome.host', 'address': '10.3.147.5', 'monitoring_source': 'monitoring01', } assert config.objects[1] == { 'object_type': 'service', 'host_name': 'awesome.host', 'service_description': 'Zabbix Agent TCP', 'monitoring_source': 'monitoring01' }
def migrate_host_acknowledgements(simulate=True, suffix=MIGRATION_COMMENT_SUFFIX, hostname=None): """ Migrate all host acknowledgements or just only acks related to one hostname. :param simulate: simulate, don't write :type simulate: bool :param suffix: Suffix to append to acknowledgements to distinguish from existing acks :type suffix: str :param hostname: hostname to migrate acks for :type hostname: str :return: """ result = [] icinga1 = Icinga1Config() icinga2 = Icinga2Config() migrate_count = 0 if hostname: icinga1_host_acks = [ ack for ack in icinga1.host_acknowledgements if ack['host_name'] == hostname ] else: icinga1_host_acks = [ack for ack in icinga1.host_acknowledgements] for ack in icinga1_host_acks: logger.debug(ack) host_name = ack['host_name'] if simulate: logger.info("Would acknowledge host: {}".format(host_name)) else: if icinga2.get_acknowledgements(host_name=host_name, author=ack['author'], comment=ack['comment_data'] + suffix): logger.warning( "Acknowledgement {} already exists - skipping".format(ack)) else: result = icinga2.acknowledge_host(host_name=host_name, author=ack['author'], comment=ack['comment_data'] + suffix) if result: migrate_count += 1 logger.info("Migrated {} host acknowledgements".format(migrate_count)) return result
def compare_acknowledged_problems(output=None, icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ List all acknowledged problems per host as JSON result :param output: :param icinga1: Icinga1Config :param icinga2: Icinga2Config :return: """ icinga1_acks = [ status for status in icinga1.status if status.get('problem_has_been_acknowledged', '0') == '1' ] icinga2_acks = icinga2.get_acknowledgements() all_hosts = [ack['host_name'] for ack in icinga1_acks] + \ [status['attrs']['host_name'] for status in icinga2_acks] if output: f = open(output, 'w') else: f = sys.stdout for host in set(all_hosts): acks_1 = [ack for ack in icinga1_acks if ack['host_name'] == host] acks_2 = [ ack for ack in icinga2_acks if ack['attrs']['host_name'] == host ] if acks_1 or acks_2: f.write(host + '\n') if acks_1: f.write("Icinga1:\n") for ack in acks_1: f.write(json.dumps(ack, indent=4)) f.write('\n') if acks_2: f.write("Icinga2:\n") for ack in acks_2: f.write(json.dumps(ack, indent=4)) f.write('\n')
def test_status_parser(): expected = { 'author': 'Jon Doe', 'comment': '74864', 'downtime_id': '18597', 'duration': '157766400', 'end_time': '1633859009', 'entry_time': '1476092629', 'fixed': '1', 'host_name': 'awesome.host', 'is_in_effect': '1', 'object_type': 'hostdowntime', 'start_time': '1476092609', 'trigger_time': '1476092629', 'triggered_by': '0', 'monitoring_source': 'monitoring01' } config = Icinga1Config(objects_files=sample_objects, status_files=sample_status) assert expected in config.status
def pretty_print_services(stream=sys.stdout, hostname=None, icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ Pretty print all services of Icinga1 and Icinga2. :param stream: file handle, leave empty to print :param hostname: hostname :param icinga1: Icinga1Config :param icinga2: Icinga2Config :return: """ if hostname: services_1_all = icinga1.get_services_by_hostname(host_name=hostname) services_2_all = icinga2.get_services_by_hostname(host_name=hostname) else: services_1_all = icinga1.get_services_by_hostname() services_2_all = icinga2.get_services_by_hostname() hostnames = [hostname] if hostname else list(services_1_all.keys()) for hostname in hostnames: print(hostname, file=stream) print(' Icinga1:', file=stream) for service in services_1_all.get(hostname, []): print(' ' + service['check_command_extracted'] + ':', file=stream) pretty_service = yaml.dump(service, None, default_flow_style=False) print(textwrap.indent(pretty_service, ' '), file=stream) print(' Icinga2:', file=stream) for service in services_2_all.get(hostname, []): print(' ' + service['check_command_extracted'] + ':', file=stream) pretty_service = yaml.dump(service, None, default_flow_style=False) print(textwrap.indent(pretty_service, ' '), file=stream)
def migrate_service_acknowledgements(simulate=True, suffix=MIGRATION_COMMENT_SUFFIX, hostname=None): """ Migrate all service acknowledgements or only service acknowledgements related to one hostname. :param simulate: simulate, don't write :type simulate: bool :param suffix: Suffix to append to acknowledgements to distinguish from existing acks :type suffix: str :param hostname: hostname to migrate acks for :type hostname: str :return: """ result = [] icinga1 = Icinga1Config() icinga2 = Icinga2Config() icinga1_services = icinga1.get_services_by_hostname() icinga2_services_all = icinga2.get_services_by_hostname() icinga2_hosts = icinga2.get_hosts_dict() migrate_count = 0 if hostname: icinga1_acks = [ ack for ack in icinga1.service_acknowledgements if ack['host_name'] == hostname ] else: icinga1_acks = [ack for ack in icinga1.service_acknowledgements] for ack in icinga1_acks: ack_hostname = ack['host_name'] if ack_hostname not in icinga2_hosts: logger.error( "Host not available in Icinga2: '{}'".format(ack_hostname)) continue services_icinga1 = \ [service for service in icinga1_services[ack_hostname] if service.get('service_description', None) == ack['service_description']] assert len(services_icinga1) == 1 service_icinga1 = services_icinga1[0] services_icinga2 = [ service for service in icinga2_services_all[ack_hostname] if service['check_command_extracted'] == service_icinga1['check_command_extracted'] or ndict(service)['attrs']['vars']['comment'] == service_icinga1['check_command_extracted'] ] if not services_icinga2: logger.error("Could not find Service: {}!{}".format( ack_hostname, service_icinga1['check_command_extracted'])) else: icinga2_service_name = services_icinga2[0]['attrs']['name'] if simulate: logger.info("Would acknowledge service: {}!{}".format( ack_hostname, service_icinga1['check_command_extracted'])) else: if icinga2.get_acknowledgements( host_name=ack_hostname, author=ack['author'], service_name=icinga2_service_name, comment=ack['comment_data'] + suffix): logger.warning( "Acknowledgement {} already exists - skipping".format( ack)) else: result = icinga2.acknowledge_service( host_name=ack_hostname, service_name=icinga2_service_name, author=ack['author'], comment=ack['comment_data'] + suffix) if result: migrate_count += 1 logger.info("Migrated {} service acknowledgements".format(migrate_count)) return result
def migrate_service_downtimes(simulate=True, suffix=MIGRATION_COMMENT_SUFFIX, hostname=None): """ Migrate service downtimes :param simulate: don't send requests to API :param suffix: downtime comment suffix :type simulate: bool :param hostname: hostname :return: """ icinga1 = Icinga1Config() icinga2 = Icinga2Config() icinga2_hosts = icinga2.get_hosts_dict() icinga1_services = icinga1.get_services_by_hostname() icinga2_services = icinga2.get_services_by_hostname() downtimes = [ dt for dt in icinga1.servicedowntimes if 'daily' not in dt['comment'].lower() and 'weekly' not in dt['comment'].lower() ] if hostname: downtimes = [ downtime for downtime in downtimes if downtime['host_name'] == hostname ] logger.info("Got {} service downtimes to migrate.".format(len(downtimes))) migrate_count = 0 for downtime in downtimes: dt_hostname = downtime['host_name'] author = downtime['author'] comment = downtime['comment'] + suffix start_time = int(downtime['start_time']) end_time = int(downtime['end_time']) duration = int(downtime['duration']) fixed = True if downtime['fixed'] == '1' else False if dt_hostname not in icinga2_hosts: logger.error( "Host '{}' not in Icinga2, skipping".format(dt_hostname)) continue service_icinga1 = [ service for service in icinga1_services[dt_hostname] if service['service_description'] == downtime['service_description'] ][0] services_icinga2 = [ service for service in icinga2_services[dt_hostname] if service['check_command_extracted'] == service_icinga1['check_command_extracted'] or ndict(service)['attrs']['vars']['comment'] == service_icinga1.get('check_command') ] if not services_icinga2: logger.error("Service not found: {}!{}".format( dt_hostname, downtime['service_description'])) continue else: if len(services_icinga2) > 1: logger.error( "AMBIGUOUS - host: {}, downtime service description: '{}'". format(dt_hostname, downtime['service_description'])) for service in services_icinga2: logger.error( "AMBIGUOUS - icinga2: service_name:'{}', display_name:'{}', check_command:'{}', check_command_extracted:'{}'" .format(service['name'], service['attrs']['display_name'], service['attrs']['check_command'], service['check_command_extracted'])) service_icinga2 = services_icinga2[0] logger.debug("Found service: {}".format(service_icinga2)) service_name = service_icinga2['attrs']['name'] # query service_name downtime_filter = { 'host_name': dt_hostname, 'service_name': service_name, 'downtime_author': author, 'downtime_comment': comment, 'downtime_start_time': start_time, 'downtime_end_time': end_time, 'downtime_duration': duration, 'downtime_fixed': fixed, } try: downtime_exists = icinga2.get_downtimes(**downtime_filter) != [] except Icinga2ApiException: downtime_exists = False if downtime_exists: logger.warning("Downtime {} already exists, skipping.".format( downtime_filter)) elif not simulate: migrate_count += 1 icinga2.schedule_service_downtime(**downtime_filter) else: logger.warning( "Would migrate downtime: {}".format(downtime_filter)) logger.info("Migrated {} service downtimes".format(migrate_count))
def migrate_host_downtimes(simulate=True, suffix=MIGRATION_COMMENT_SUFFIX, hostname=None): """ Migrate host downtimes :param simulate: don't send requests to API :param suffix: downtime comment suffix :type simulate: bool :param hostname: hostname :return: """ icinga1 = Icinga1Config() icinga2 = Icinga2Config() icinga2_hosts = icinga2.get_hosts_dict() downtimes = [ dt for dt in icinga1.hostdowntimes if 'daily' not in dt['comment'].lower() and 'weekly' not in dt['comment'].lower() ] if hostname: downtimes = [ downtime for downtime in downtimes if downtime['host_name'] == hostname ] logger.info("Got {} host downtimes to migrate.".format(len(downtimes))) migrate_count = 0 for downtime in downtimes: dt_hostname = downtime['host_name'] author = downtime['author'] comment = downtime['comment'] + suffix start_time = int(downtime['start_time']) end_time = int(downtime['end_time']) duration = int(downtime['duration']) fixed = True if downtime['fixed'] == '1' else False if dt_hostname not in icinga2_hosts: logger.error( "Host '{}' not in Icinga2, skipping".format(dt_hostname)) continue downtime_filter = { 'host_name': dt_hostname, 'author': author, 'comment': comment, 'start_time': start_time, 'end_time': end_time, 'duration': duration, 'fixed': fixed, } try: downtime_exists = icinga2.get_downtimes(**downtime_filter) != [] except Icinga2ApiException: downtime_exists = False if downtime_exists: logger.warning("Downtime {} already exists, skipping.".format( downtime_filter)) elif not simulate: migrate_count += 1 icinga2.schedule_host_downtime(**downtime_filter) logger.info("Migrated {} host downtimes".format(migrate_count))
def compare_services(output=None, hostname=None, icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ Compare Services retrieved from Icinga1 (cache file) and Icinga2 (REST API) per host. Compares: * Service exists in Icinga2 * notification state is the same (enable_notifications) * no-sla attribute is the same * notes-url is the same Example output: 10.3.147.6 Icinga1 services missing in Icinga2: check_http SSH Icinga2 services missing in Icinga1: check_ssh 10.3.147.15 Icinga1 services missing in Icinga2: SSH Icinga2 services missing in Icinga1: check_ssh :param output: output filename (write to stdout if None) :type output: str :param hostname: Hostname :param icinga1: Icinga1Config :param icinga2: Icinga2Config :return: """ logger.info("Retrieving Icinga1 services") icinga1_services_all = icinga1.get_services_by_hostname() icinga1_service_status_all = icinga1.get_servicestatus_by_host() if hostname: icinga1_service_status_all = { hostname: icinga1_service_status_all[hostname] } icinga2_hosts = icinga2.get_hosts_dict().keys() if hostname: icinga2_hosts = [hostname] logger.info("Got {} hosts from Icinga2 API".format(len(icinga2_hosts))) diff = {} logger.info("Retrieving Icinga2 services from API") icinga2_services_all = icinga2.get_services_by_hostname() icinga2_services_count = sum([ len(icinga2_services_all[key]) for key in icinga2_services_all.keys() ]) logger.info( "Got {} services from Icinga2 API".format(icinga2_services_count)) total_diff = 0 if output: f = open(output, 'w') else: f = sys.stdout for icinga2_hostname in icinga2_hosts: icinga1_services = icinga1_services_all[icinga2_hostname] icinga1_service_states = icinga1_service_status_all[icinga2_hostname] icinga2_services = icinga2_services_all[icinga2_hostname] # Filter out nrpe checks on Icinga2 icinga2_services = [ service for service in icinga2_services if service['attrs']['name'] != 'nrpe-health' ] # compare extracted check commands icinga1_check_commands = [ service['check_command_extracted'] for service in icinga1_services ] icinga2_check_commands = [ service['check_command_extracted'] for service in icinga2_services ] # Check services missing by using extracted check_command as key icinga1_missing_icinga2 = \ [check_command for check_command in icinga1_check_commands if check_command not in icinga2_check_commands] # Filter checks with same comment for icinga2_comment in [ ndict(service)['attrs']['vars']['comment'] for service in icinga2_services ]: try: icinga1_missing_icinga2.remove(icinga2_comment) except ValueError: pass # Compare notification state if service exists in both systems notification_states = {} nosla_diff = {} notesurl_diff = {} for check_command in icinga1_check_commands + icinga2_check_commands: service_icinga1 = [ service for service in icinga1_services if service['check_command_extracted'] == check_command ] service_icinga2 = [ service for service in icinga2_services if service['check_command_extracted'] == check_command ] if service_icinga1 and service_icinga2: icinga1_description = service_icinga1[0]['service_description'] icinga1_check_command = service_icinga1[0]['check_command'] key = '{} - {}'.format(icinga1_check_command, icinga1_description) # Use Icinga1 enable_notifications from state if exists, otherwise from config icinga1_state = [ state for state in icinga1_service_states if state['check_command'] == icinga1_check_command and state['service_description'] == icinga1_description ] if icinga1_state: icinga1_notifications_enabled = \ icinga1_state[0]['notifications_enabled'] == '1' else: icinga1_notifications_enabled = service_icinga1[0][ 'enable_notifications'] # If state was modified at runtime, use original state from config try: icinga2_notifications_enabled = service_icinga2[0][ 'attrs']['original_attributes']['enable_notifications'] except KeyError: icinga2_notifications_enabled = service_icinga2[0][ 'attrs']['enable_notifications'] if icinga1_notifications_enabled != icinga2_notifications_enabled: notification_states[key] = (icinga1_notifications_enabled, icinga2_notifications_enabled) # compare sla icinga1_nosla = service_icinga1[0].get('notes', False) == 'no-sla' icinga2_nosla = False try: icinga2_nosla = service_icinga2[0]['attrs']['vars'][ 'nosla'] except: pass if icinga1_nosla != icinga2_nosla: nosla_diff[key] = (icinga1_nosla, icinga2_nosla) # compare notes url. no diff if confluence entry is in Icinga2 icinga1_notes = service_icinga1[0].get('notes_url', '') icinga2_notes = service_icinga2[0]['attrs']['notes_url'] # Write results to file if icinga1_missing_icinga2 or notification_states or nosla_diff: f.write('\n' + icinga2_hostname + '\n') diff[icinga2_hostname] = {} diff[icinga2_hostname]['host_name'] = icinga2_hostname if icinga1_missing_icinga2: diff[icinga2_hostname][ 'icinga1_missing_icinga2'] = icinga1_missing_icinga2 f.write("Icinga1 services missing in Icinga2:\n") f.writelines([cmd + '\n' for cmd in icinga1_missing_icinga2]) f.write('\n') if notification_states: f.write("Different notification states:\n") diff[icinga2_hostname]['notification_states'] = [] for key in notification_states: diff[icinga2_hostname]['notification_states'].extend({ 'command': key, 'icinga1': notification_states[key][0], 'icinga2': notification_states[key][1] }) f.write( "Service: {0}, Icinga1: {1}, Icinga2: {2}\n".format( key, notification_states[key][0], notification_states[key][1])) if nosla_diff: f.write("Different SLA:\n") diff[icinga2_hostname]['nosla_diff'] = [] for key in nosla_diff: diff[icinga2_hostname]['nosla_diff'].extend({ 'command': key, 'icinga1': nosla_diff[key][0], 'icinga2': nosla_diff[key][1] }) f.write( "Service: {0}, Icinga1: {1}, Icinga2: {2}\n".format( key, nosla_diff[key][0], nosla_diff[key][1])) if notesurl_diff: f.write("Different notes_url:\n") diff[icinga2_hostname]['notesurl_diff'] = [] for key in notesurl_diff: diff[icinga2_hostname]['notesurl_diff'].extend({ 'command': key, 'icinga1': notesurl_diff[key][0], 'icinga2': notesurl_diff[key][1] }) f.write( "Service: {0}, Icinga1: {1}, Icinga2: {2}\n".format( key, notesurl_diff[key][0], notesurl_diff[key][1])) total_diff += len(icinga1_missing_icinga2 + list(notification_states.keys())) logger.info('Found {} service differences'.format(total_diff)) if output: with open(output + '.json', 'w') as jsonfile: logger.info("Wrote JSON file: {}".format(jsonfile.name)) json.dump(diff, jsonfile) return diff
def compare_service_contacts( icinga1=Icinga1Config(), icinga2=Icinga2Config(), hostname=None): """ Check if service contacts have been migrated correctly :param icinga1: Icinga1Config :param icinga2: Icinga2Config :param hostname: hostname :return: """ icinga1_services = icinga1.get_services(hostname) icinga2_services = icinga2.get_services_by_hostname(host_name=hostname) icinga2_service_notifications = icinga2.get_service_notification_contacts( hostname) icinga2_contact_excludes = [] diff = [] services_iterator = icinga1_services if not hostname: bar = progressbar.ProgressBar() services_iterator = bar(services_iterator) for icinga1_service in services_iterator: service_name = "{}!{}".format( icinga1_service['host_name'], icinga1_service['check_command_extracted']) icinga1_contacts = [] if icinga1_service.get('contacts'): icinga1_contacts = sorted([ contact.strip() for contact in icinga1_service.get('contacts').split(',') ]) icinga2_service = [ _ for _ in icinga2_services.get(icinga1_service['host_name'], []) if _['check_command_extracted'] == icinga1_service['check_command_extracted'] or ndict(_)['attrs'] ['vars']['comment'] == icinga1_service.get('check_command') ] if icinga2_service: icinga2_service = icinga2_service[0] key = '{}!{}'.format(icinga1_service['host_name'], icinga2_service['attrs']['name']) if key in icinga2_service_notifications: service_notifications = icinga2_service_notifications[key] icinga2_contacts = [ user['name'] for user in service_notifications['users'] if user['name'] not in icinga2_contact_excludes ] icinga2_contacts = sorted(list(set(icinga2_contacts))) else: icinga2_contacts = [] if icinga1_contacts != ['dummy' ] and icinga1_contacts != icinga2_contacts: print( "Different notify users for service: {}. Icinga1:{}, Icinga2: {}" .format(service_name, icinga1_contacts, icinga2_contacts)) logger.debug("{}".format(icinga1_service)) logger.debug("{}".format(icinga2_service)) if icinga1_service.get('contacts') == 'dummy': # Skip if notifications in Icinga2 are disabled if icinga1_service['contacts'] == 'dummy' \ and icinga1_service['contact_groups'] == 'dummy' \ and not icinga2_service['attrs']['enable_notifications']: continue diff.append((icinga1_service['host_name'], icinga1_service['check_command_extracted'])) print("Wrong notification settings for service: {}".format( service_name)) else: logger.warning( "Service missing in Icinga2: {}".format(service_name)) return diff
def migrate_service_notification_states(simulate=True, suffix=MIGRATION_COMMENT_SUFFIX, hostname=None): """ Migrate enable_notifications for service states. Should be run after host notification states have been migrated. States are only migrated if: * Icinga1 service notifications are enabled but service status notifications are disabled * Icinga1 host notifications are enabled * Icinga2 service exists * Icinga2 service notifications are enabled Should be run *after* migrate_host_notification_states @param simulate: Simulate (don't perform requests to Icinga2) @type simulate: bool @param suffix: Suffix to append to Icinga2 notes @type suffix: str @param hostname: Host (leave empty for all hosts) @type hostname: str :return: """ icinga1 = Icinga1Config() icinga2 = Icinga2Config() services_icinga1 = icinga1.get_services_by_hostname() services_icinga2 = icinga2.get_services_by_hostname() icinga2_hosts = icinga2.get_hosts_dict() icinga1_status_dict = icinga1.get_servicestatus_by_host() if hostname: hostnames = [hostname] else: hostnames = [host['host_name'] for host in icinga1.get_hosts()] count = 0 for hostname in hostnames: if hostname not in icinga2_hosts: logger.error( "Host not available in Icinga2: '{}'".format(hostname)) continue services = services_icinga1[hostname] service_states = icinga1_status_dict[hostname] for service in services: logger.debug(service['check_command']) logger.debug(pretty_print_dict(service, ' ')) service_state = [ state for state in service_states if state['check_command'] == service['check_command'] ] if service_state: service_state = service_state[0] logger.debug("Got service state:\n{}".format( pretty_print_dict(service_state, ' '))) if service['notifications_enabled'] == '1' \ and service_state['notifications_enabled'] == '0': icinga2_service = [ x for x in services_icinga2[hostname] if x['check_command_extracted'] == service['check_command_extracted'] ] if len(icinga2_service) == 1: icinga2_service = icinga2_service[0] service_name = icinga2_service['attrs']['name'] if icinga2_service['attrs']['enable_notifications']: logger.debug( "{}!{}: disabling service notifications". format(hostname, service_name)) if not simulate: try: response = icinga2.set_service_notifications( hostname, service_name, False, notes= 'Migrated notification state from Icinga1' + suffix) logger.info( "Disabled service notifications for host {}, " "service {}".format( hostname, service_name)) count += 1 logger.debug(response) except Exception as error: logger.error( "Could not migrate host notification state " "for host {}, service {}. Error: {}". format(hostname, service_name, error)) else: logger.debug( "{}!{}: service notifications already disabled" .format(hostname, service_name)) elif len(icinga2_service) == 0: logger.error("Service {}!{} not found".format( hostname, service['check_command_extracted'])) logger.info("{} notifications disabled".format(count))
def compare_services_manual(hostname=None, icinga1=Icinga1Config(), icinga2=Icinga2Config()): """ Compare services that cannot be diffed since their check commands are ambiguous. :param hostname: :param icinga1: :param icinga2: :return: """ if hostname: icinga1_services_all = { hostname: icinga1.get_services(hostname=hostname) } icinga2_hostnames = [hostname] icinga2_services_all = { hostname: icinga2.get_services(host_name=hostname) } else: icinga1_services_all = icinga1.get_services_by_hostname() icinga2_hostnames = icinga2.get_hosts_dict().keys() icinga2_services_all = icinga2.get_services_by_hostname() for hostname in icinga2_hostnames: icinga1_services = icinga1_services_all[hostname] # find services with same check command check_commands = [ service['check_command_extracted'] for service in icinga1_services ] duplicate_check_commands = [ item for item, count in Counter(check_commands).items() if count > 1 ] if len(duplicate_check_commands): print("\n{}: found {} services: [{}]".format( hostname, len(duplicate_check_commands), ','.join(duplicate_check_commands))) icinga1_duplicate_check_cmd_services = [ service for service in icinga1_services if service['check_command_extracted'] in duplicate_check_commands ] icinga2_services = [ service for service in icinga2_services_all[hostname] if service['check_command_extracted'] in duplicate_check_commands ] print("Icinga1 - link: {}".format(icinga1.get_url(hostname))) for icinga1_service in icinga1_duplicate_check_cmd_services: print("\tcheck command: {}; SLA: {}; description: {}".format( icinga1_service['check_command'], icinga1_service.get('notes', False) == 'no-sla', icinga1_service['service_description'])) if icinga2_services: print("Icinga2 - link: {}".format(icinga2.get_url(hostname))) for icinga2_service in icinga2_services: icinga2_nosla = False try: icinga2_nosla = icinga2_service['attrs']['vars']['nosla'] except: pass print( "\textracted check command: {}; SLA: {}; display name: {}". format(icinga2_service['check_command_extracted'], icinga2_nosla, icinga2_service['attrs']['display_name']))
def test_downtimes(): config = Icinga1Config(objects_files=sample_objects, status_files=sample_status) downtime = config.hostdowntimes[0] config = Icinga1Config(objects_files=sample_objects, status_files=sample_status) downtime = config.servicedowntimes[0]
def test_object_broken_contact_parser(): # Cache file with empty contact value should be parsed correctly config = Icinga1Config(objects_files=sample_objects_broken, status_files=sample_status) assert config.objects[0]['contacts'] is None assert config.objects[0]['contact_groups'] is None