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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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')
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 8
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 12
0
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))
Ejemplo n.º 13
0
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))
Ejemplo n.º 14
0
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
Ejemplo n.º 15
0
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
Ejemplo n.º 16
0
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))
Ejemplo n.º 17
0
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