<commit_before>#!/usr/bin/env python
#Copyright (C) 2009-2010 :
#    <NAME>, <EMAIL>
#    <NAME>, <EMAIL>
#
#This file is part of Shinken.
#
#Shinken is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#Shinken is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU Affero General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with Shinken.  If not, see <http://www.gnu.org/licenses/>.


""" Config is the class to read, load and manipulate the user
 configuration. It read a main cfg (nagios.cfg) and get all informations
 from it. It create objects, make link between them, clean them, and cut
 them into independant parts. The main user of this is Arbiter, but schedulers
 use it too (but far less)"""

import re, string, copy, os, socket
import itertools
import time
import random

from shinken.timeperiod import Timeperiod, Timeperiods
from shinken.service import Service, Services
from shinken.command import Command, Commands
from shinken.resultmodulation import Resultmodulation, Resultmodulations
from shinken.escalation import Escalation, Escalations
from shinken.serviceescalation import Serviceescalation, Serviceescalations
from shinken.hostescalation import Hostescalation, Hostescalations
from shinken.host import Host, Hosts
from shinken.hostgroup import Hostgroup, Hostgroups
from shinken.realm import Realm, Realms
from shinken.contact import Contact, Contacts
from shinken.contactgroup import Contactgroup, Contactgroups
from shinken.notificationway import NotificationWay, NotificationWays
from shinken.servicegroup import Servicegroup, Servicegroups
from shinken.item import Item
from shinken.servicedependency import Servicedependency, Servicedependencies
from shinken.hostdependency import Hostdependency, Hostdependencies
from shinken.arbiterlink import ArbiterLink, ArbiterLinks
from shinken.schedulerlink import SchedulerLink, SchedulerLinks
from shinken.reactionnerlink import ReactionnerLink, ReactionnerLinks
from shinken.brokerlink import BrokerLink, BrokerLinks
from shinken.pollerlink import PollerLink, PollerLinks
from shinken.module import Module, Modules
from shinken.graph import Graph
from shinken.log import logger

from shinken.util import to_int, to_char, to_bool
from shinken.property import UnusedProp, BoolProp, IntegerProp, FloatProp, CharProp, StringProp, ListProp
#import psyco
#psyco.full()


class Config(Item):
    cache_path = "objects.cache"
    my_type = "config"

    # Properties:
    # *required : if True, there is not default, and the config must put them
    # *default: if not set, take this value
    # *pythonize : function call to
    # *class_inherit : (Service, 'blabla') : must set this propertie to the
    #  Service class with name blabla
    #  if (Service, None) : must set this properti to the Service class with
    #  same name
    # *unused : just to warn the user that the option he use is no more used
    #  in Shinken
    # *usage_text : if present, will print it to explain why it's no more useful
    properties = {
        'prefix': StringProp(
            default='/usr/local/shinken/'),
        'log_file': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the log broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'object_cache_file': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the status_dat broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'precached_object_file': UnusedProp(
            text='Shinken is faster enough to do not need precached object file.'),
        'resource_file': StringProp(
            default='/tmp/ressources.txt'),
        'temp_file': UnusedProp(
            text=' temporary files are not used in the shinken architecture.'),
        'status_file': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the status_dat broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'status_update_interval': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the status_dat broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'shinken_user': StringProp(
            default='shinken'),
        'shinken_group': StringProp(
            default='shinken'),
        'enable_notifications': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'execute_service_checks': BoolProp(
            default='1',
            class_inherit=[(Service, 'execute_checks')]),
        'accept_passive_service_checks': BoolProp(
            default='1',
            class_inherit=[(Service, 'accept_passive_checks')]),
        'execute_host_checks': BoolProp(
            default='1',
            class_inherit=[(Host, 'execute_checks')]),
        'accept_passive_host_checks': BoolProp(
            default='1',
            class_inherit=[(Host, 'accept_passive_checks')]),
        'enable_event_handlers': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'log_rotation_method': CharProp(
            default='d'),
        'log_archive_path': StringProp(
            default='/usr/local/shinken/var/archives'),
        'check_external_commands': BoolProp(
            default='1'),
        'command_check_interval': UnusedProp(
            text='anoter value than look always the file is useless, so we fix it.'),
        'command_file': StringProp(
            default='/tmp/command.cmd'),
        'external_command_buffer_slots': UnusedProp(
            text='We do not limit the ewxternal command slot.'),
        'check_for_updates': UnusedProp(
            text='network administrators will never allow such communication between server and the external world. Use your distribution packet manager to know if updates are available or go to the http://www.shinken-monitoring.org website instead.'),
        'bare_update_checks': UnusedProp(
            text=None),
        'lock_file': StringProp(
            default='/usr/local/shinken/var/arbiterd.pid'),
        'retain_state_information': UnusedProp(
            text='sorry, retain state information will not be implemented because it is useless.'),
        'state_retention_file': StringProp(
            default=''),
        'retention_update_interval': IntegerProp(
            default='0'),
        'use_retained_program_state': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'use_retained_scheduling_info': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_host_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_service_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_process_host_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_process_service_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_contact_host_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_contact_service_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'use_syslog': BoolProp(
            default='0'),
        'log_notifications': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'log_service_retries': BoolProp(
            default='1',
            class_inherit=[(Service, 'log_retries')]),
        'log_host_retries': BoolProp(
            default='1',
            class_inherit=[(Host, 'log_retries')]),
        'log_event_handlers': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'log_initial_states': BoolProp(
            default='1'),
        'log_external_commands': BoolProp(
            default='1'),
        'log_passive_checks': BoolProp(
            default='1'),
        'global_host_event_handler': StringProp(
            default='',
            class_inherit=[(Host, 'global_event_handler')]),
        'global_service_event_handler': StringProp(
            default='',
            class_inherit=[(Service, 'global_event_handler')]),
        'sleep_time': UnusedProp(
            text='this deprecated option is useless in the shinken way of doing.'),
        'service_inter_check_delay_method': UnusedProp(
            text='This option is useless in the Shinken scheduling. The only way is the smart way.'),
        'max_service_check_spread': IntegerProp(
            default='30',
            class_inherit=[(Service, 'max_check_spread')]),
        'service_interleave_factor': UnusedProp(
            text='This option is useless in the Shinken scheduling because it use a random distribution for initial checks.'),
        'max_concurrent_checks': UnusedProp(
            text='Limiting the max concurrent checks is not helful to got a good running monitoring server.'),
        'check_result_reaper_frequency': UnusedProp(
            text='Shinken do not use reaper process.'),
        'max_check_result_reaper_time': UnusedProp(
            text='Shinken do not use reaper process.'),
        'check_result_path': UnusedProp(
            text='Shinken use in memory returns, not check results on flat file.'),
        'max_check_result_file_age': UnusedProp(
            text='Shinken do not use flat file check resultfiles.'),
        'host_inter_check_delay_method': UnusedProp(
            text='This option is unused in the Shinken scheduling because distribution of the initial check is a random one.'),
        'max_host_check_spread': IntegerProp(
            default='30',
            class_inherit=[(Host, 'max_check_spread')]),
        'interval_length': IntegerProp(
            default='60',
            class_inherit=[(Host, None), (Service, None)]),
        'auto_reschedule_checks': BoolProp(
            managed=False,
            default='1'),
        'auto_rescheduling_interval': IntegerProp(
             managed=False,
             default='1'),
        'auto_rescheduling_window': IntegerProp(
             managed=False,
            default='180'),
        'use_aggressive_host_checking': UnusedProp(
            text='Host agressive checking is an heritage from Nagios 1 and is really useless now.'),
        'translate_passive_host_checks': BoolProp(
             managed=False,
            default='1'),
        'passive_host_checks_are_soft': BoolProp(
             managed=False,
            default='1'),
        'enable_predictive_host_dependency_checks': BoolProp(
             managed=False,
            default='1',
            class_inherit=[(Host, 'enable_predictive_dependency_checks')]),
        'enable_predictive_service_dependency_checks': StringProp(
             managed=False,
            default='1'),
        'cached_host_check_horizon': IntegerProp(
            default='0',
            class_inherit=[(Host, 'cached_check_horizon')]),
        'cached_service_check_horizon': IntegerProp(
            default='0',
            class_inherit=[(Service, 'cached_check_horizon')]),
        'use_large_installation_tweaks': BoolProp(
            default='0',
            class_inherit=[(Host, None), (Service, None)]),
        'free_child_process_memory': UnusedProp(
            text='this option is automatic in Python processes'),
        'child_processes_fork_twice': UnusedProp(
            text='fork twice is not use.'),
        'enable_environment_macros': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'enable_flap_detection': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'low_service_flap_threshold': IntegerProp(
            default='25',
            class_inherit=[(Service, 'low_flap_threshold')]),
        'high_service_flap_threshold': IntegerProp(
            default='50',
            class_inherit=[(Service, 'high_flap_threshold')]),
        'low_host_flap_threshold': IntegerProp(
            default='25',
            class_inherit=[(Host, 'low_flap_threshold')]),
        'high_host_flap_threshold': IntegerProp(
            default='50',
            class_inherit=[(Host, 'high_flap_threshold')]),
        'soft_state_dependencies': BoolProp(
             managed=False,
            default='0'),
        'service_check_timeout': IntegerProp(
            default='10',
            class_inherit=[(Service, 'check_timeout')]),
        'host_check_timeout': IntegerProp(
            default='10',
            class_inherit=[(Host, 'check_timeout')]),
        'event_handler_timeout': IntegerProp(
            default='10',
            class_inherit=[(Host, None), (Service, None)]),
        'notification_timeout': IntegerProp(
            default='5',
            class_inherit=[(Host, None), (Service, None)]),
        'ocsp_timeout': IntegerProp(
            default='5',
            class_inherit=[(Service, None)]),
        'ochp_timeout': IntegerProp(
            default='5',
            class_inherit=[(Host, None)]),
        'perfdata_timeout': IntegerProp(
            default='2',
            class_inherit=[(Host, None), (Service, None)]),
        'obsess_over_services': BoolProp(
            default='0',
            class_inherit=[(Service, 'obsess_over')]),
        'ocsp_command': StringProp(
            default='',
            class_inherit=[(Service, None)]),
        'obsess_over_hosts': BoolProp(
            default='0',
            class_inherit=[(Host, 'obsess_over')]),
        'ochp_command': StringProp(
            default='',
            class_inherit=[(Host, None)]),
        'process_performance_data': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'host_perfdata_command': StringProp(
            default='',
            class_inherit=[(Host, 'perfdata_command')]),
        'service_perfdata_command': StringProp(
            default='',
            class_inherit=[(Service, 'perfdata_command')]),
        'host_perfdata_file': StringProp(
            default='',
            class_inherit=[(Host, 'perfdata_file')]),
        'service_perfdata_file': StringProp(
            default='',
            class_inherit=[(Service, 'perfdata_file')]),
        'host_perfdata_file_template': StringProp(
            default='/tmp/host.perf',
            class_inherit=[(Host, 'perfdata_file_template')]),
        'service_perfdata_file_template': StringProp(
            default='/tmp/host.perf',
            class_inherit=[(Service, 'perfdata_file_template')]),
        'host_perfdata_file_mode': CharProp(
            default='a',
            class_inherit=[(Host, 'perfdata_file_mode')]),
        'service_perfdata_file_mode': CharProp(
            default='a',
            class_inherit=[(Service, 'perfdata_file_mode')]),
        'host_perfdata_file_processing_interval': IntegerProp(
             managed=False,
            default='15'),
        'service_perfdata_file_processing_interval': IntegerProp(
             managed=False,
            default='15'),
        'host_perfdata_file_processing_command': StringProp(
             managed=False,
            default='',
            class_inherit=[(Host, 'perfdata_file_processing_command')]),
        'service_perfdata_file_processing_command': StringProp(
             managed=False,
            default=None),
        'check_for_orphaned_services': BoolProp(
            default='1',
            class_inherit=[(Service, 'check_for_orphaned')]),
        'check_for_orphaned_hosts': BoolProp(
            default='1',
            class_inherit=[(Host, 'check_for_orphaned')]),
        'check_service_freshness': BoolProp(
            default='1',
            class_inherit=[(Service, 'check_freshness')]),
        'service_freshness_check_interval': IntegerProp(
            default='60'),
        'check_host_freshness': BoolProp(
            default='1',
            class_inherit=[(Host, 'check_freshness')]),
        'host_freshness_check_interval': IntegerProp(
            default='60'),
        'additional_freshness_latency': IntegerProp(
            default='15',
            class_inherit=[(Host, None), (Service, None)]),
        'enable_embedded_perl': BoolProp(
            help='It will surely never be managed, but it should not be useful with poller performances.',
             managed=False,
            default='1'),
        'use_embedded_perl_implicitly': BoolProp(
             managed=False,
            default='0'),
        'date_format': StringProp(
            managed=False,
            default=None),
        'use_timezone': StringProp(
            default='',
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'illegal_object_name_chars': StringProp(
            default="""`~!$%^&*"|'<>?,()=""",
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'illegal_macro_output_chars': StringProp(
            default='',
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'use_regexp_matching': BoolProp(
            help=' if you go some host or service definition like prod*, it will surely failed from now, sorry.',
            managed=False,
            default='0'),
        'use_true_regexp_matching': BoolProp(
            managed=False,
            default=None),
        'admin_email': UnusedProp(
            text='sorry, not yet implemented.'),
        'admin_pager': UnusedProp(
            text='sorry, not yet implemented.'),
        'event_broker_options': UnusedProp(
            text='event broker are replaced by modules with a real configuration template.'),
        'broker_module': StringProp(
            default=''),
        'debug_file': UnusedProp(
            text=None),
        'debug_level': UnusedProp(
            text=None),
        'debug_verbosity': UnusedProp(
            text=None),
        'max_debug_file_size': UnusedProp(
            text=None),
        #'$USERn$ : {'required':False, 'default':''} # Add at run in __init__
        
        # SHINKEN SPECIFIC
        'idontcareaboutsecurity': BoolProp(
            default='0'),
        'flap_history': IntegerProp(
            default='20',
            class_inherit=[(Host, None), (Service, None)]),
        'max_plugins_output_length': IntegerProp(
            default='8192',
            class_inherit=[(Host, None), (Service, None)]),

        # Enable or not the notice about old Nagios parameters
        'disable_old_nagios_parameters_whining': BoolProp(
            default='0'),
        
        # Now for problem/impact states changes
        'enable_problem_impacts_states_change': BoolProp(
            default='0',
            class_inherit=[(Host, None), (Service, None)]),

        # More a running value in fact
        'resource_macros_names': StringProp(
            default=[])
    }

    macros = {
        'PREFIX' : 'prefix',
        'MAINCONFIGFILE' : '',
        'STATUSDATAFILE' : '',
        'COMMENTDATAFILE' : '',
        'DOWNTIMEDATAFILE' : '',
        'RETENTIONDATAFILE' : '',
        'OBJECTCACHEFILE' : '',
        'TEMPFILE' : '',
        'TEMPPATH' : '',
        'LOGFILE' : '',
        'RESOURCEFILE' : '',
        'COMMANDFILE' : '',
        'HOSTPERFDATAFILE' : '',
        'SERVICEPERFDATAFILE' : '',
        'ADMINEMAIL' : '',
        'ADMINPAGER' : ''
        #'USERn' : '$USERn$' # Add at run in __init__
        }


    #We create dict of objects
    #Type: 'name in objects' : {Class of object, Class of objects,
    #'property for self for the objects(config)'
    types_creations = {
        'timeperiod' : (Timeperiod, Timeperiods, 'timeperiods'),
        'service' : (Service, Services, 'services'),
        'servicegroup' : (Servicegroup, Servicegroups, 'servicegroups'),
        'command' : (Command, Commands, 'commands'),
        'host' : (Host, Hosts, 'hosts'),
        'hostgroup' : (Hostgroup, Hostgroups, 'hostgroups'),
        'contact' : (Contact, Contacts, 'contacts'),
        'contactgroup' : (Contactgroup, Contactgroups, 'contactgroups'),
        'notificationway' : (NotificationWay, NotificationWays, 'notificationways'),
        'servicedependency' : (Servicedependency, Servicedependencies, 'servicedependencies'),
        'hostdependency' : (Hostdependency, Hostdependencies, 'hostdependencies'),
        'arbiter' : (ArbiterLink, ArbiterLinks, 'arbiterlinks'),
        'scheduler' : (SchedulerLink, SchedulerLinks, 'schedulerlinks'),
        'reactionner' : (ReactionnerLink, ReactionnerLinks, 'reactionners'),
        'broker' : (BrokerLink, BrokerLinks, 'brokers'),
        'poller' : (PollerLink, PollerLinks, 'pollers'),
        'realm' : (Realm, Realms, 'realms'),
        'module' : (Module, Modules, 'modules'),
        'resultmodulation' : (Resultmodulation, Resultmodulations, 'resultmodulations'),
        'escalation' : (Escalation, Escalations, 'escalations'),
        'serviceescalation' : (Serviceescalation, Serviceescalations, 'serviceescalations'),
        'hostescalation' : (Hostescalation, Hostescalations, 'hostescalations'),
        }

    #This tab is used to transform old parameters name into new ones
    #so from Nagios2 format, to Nagios3 ones
    old_properties = {
        'nagios_user' : 'shinken_user',
        'nagios_group' : 'shinken_group'
        }



    def __init__(self):
        self.params = {}
        self.resource_macros_names = []
        #By default the conf is correct
        self.conf_is_correct = True
        #We tag the conf with a magic_hash, a random value to
        #idify this conf
        random.seed(time.time())
        self.magic_hash = random.randint(1, 100000)



    def fill_usern_macros(cls):
        """ Fill all USERN macros with value of properties"""
        #Now the ressource file part
        properties = cls.properties
        macros = cls.macros
        for n in xrange(1, 256):
            n = str(n)
            properties['$USER'+n+'$'] = StringProp(default='')
            macros['USER'+n] = '$USER'+n+'$'
    #Set this a Class method
    fill_usern_macros = classmethod(fill_usern_macros)


    # We've got macro in the resource file and we want
    # to update our MACRO dict with it
    def fill_resource_macros_names_macros(self):
        """ fill the macro dict will all value
        from self.resource_macros_names"""
        macros = self.__class__.macros
        for macro_name in self.resource_macros_names:
            macros[macro_name] = '$'+macro_name+'$'
   

    def load_params(self, params):
        for elt in params:
            elts = elt.split('=')
            if len(elts) == 1: #error, there is no = !
                self.conf_is_correct = False
                print "Error : the parameter %s is malformed! (no = sign)" % elts[0]
            else:
                self.params[elts[0]] = elts[1]
                setattr(self, elts[0], elts[1])
                #Maybe it's a variable as $USER$ or $ANOTHERVATRIABLE$
                #so look at the first character. If it's a $, it's a variable
                #and if it's end like it too
                if elts[0][0] == '$' and elts[0][-1] == '$':
                    macro_name = elts[0][1:-1]
                    self.resource_macros_names.append(macro_name)



    def _cut_line(self, line):
        #punct = '"#$%&\'()*+/<=>?@[\\]^`{|}~'
        tmp = re.split("[" + string.whitespace + "]+" , line)
        r = [elt for elt in tmp if elt != '']
        return r


    def read_config(self, files):
        #just a first pass to get the cfg_file and all files in a buf
        res = ''

        for file in files:
            #We add a \n (or \r\n) to be sure config files are separated
            #if the previous does not finish with a line return
            res += os.linesep
            print "Opening configuration file", file
            try:
                fd = open(file)
                buf = fd.readlines()
                fd.close()
                config_base_dir = os.path.dirname(file)
            except IOError, exp:
                logger.log("Error: Cannot open config file '%s' for reading: %s" % (file, exp))
                #The configuration is invalid because we have a bad file!
                self.conf_is_correct = False
                continue

            for line in buf:
                res += line
                line = line[:-1]
                if re.search("^cfg_file", line) or re.search("^resource_file", line):
                    elts = line.split('=')
                    if os.path.isabs(elts[1]):
                        cfg_file_name = elts[1]
                    else:
                        cfg_file_name = os.path.join(config_base_dir, elts[1])
                    try:
                        fd = open(cfg_file_name)
                        logger.log("Processing object config file '%s'" % cfg_file_name)
                        res += fd.read()
                        #Be sure to add a line return so we won't mix files
                        res += '\n'
                        fd.close()
                    except IOError, exp:
                        logger.log("Error: Cannot open config file '%s' for reading: %s" % (cfg_file_name, exp))
                    #The configuration is invalid because we have a bad file!
                        self.conf_is_correct = False
                elif re.search("^cfg_dir", line):<commit_msg>Fix: (Kristoffer Moegle) remove \r\n in unix box for read files taht were done uner windows.<commit_after>#!/usr/bin/env python
#Copyright (C) 2009-2010 :
#    <NAME>, <EMAIL>
#    <NAME>, <EMAIL>
#
#This file is part of Shinken.
#
#Shinken is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#Shinken is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU Affero General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with Shinken.  If not, see <http://www.gnu.org/licenses/>.


""" Config is the class to read, load and manipulate the user
 configuration. It read a main cfg (nagios.cfg) and get all informations
 from it. It create objects, make link between them, clean them, and cut
 them into independant parts. The main user of this is Arbiter, but schedulers
 use it too (but far less)"""

import re, string, copy, os, socket
import itertools
import time
import random

from shinken.timeperiod import Timeperiod, Timeperiods
from shinken.service import Service, Services
from shinken.command import Command, Commands
from shinken.resultmodulation import Resultmodulation, Resultmodulations
from shinken.escalation import Escalation, Escalations
from shinken.serviceescalation import Serviceescalation, Serviceescalations
from shinken.hostescalation import Hostescalation, Hostescalations
from shinken.host import Host, Hosts
from shinken.hostgroup import Hostgroup, Hostgroups
from shinken.realm import Realm, Realms
from shinken.contact import Contact, Contacts
from shinken.contactgroup import Contactgroup, Contactgroups
from shinken.notificationway import NotificationWay, NotificationWays
from shinken.servicegroup import Servicegroup, Servicegroups
from shinken.item import Item
from shinken.servicedependency import Servicedependency, Servicedependencies
from shinken.hostdependency import Hostdependency, Hostdependencies
from shinken.arbiterlink import ArbiterLink, ArbiterLinks
from shinken.schedulerlink import SchedulerLink, SchedulerLinks
from shinken.reactionnerlink import ReactionnerLink, ReactionnerLinks
from shinken.brokerlink import BrokerLink, BrokerLinks
from shinken.pollerlink import PollerLink, PollerLinks
from shinken.module import Module, Modules
from shinken.graph import Graph
from shinken.log import logger

from shinken.util import to_int, to_char, to_bool
from shinken.property import UnusedProp, BoolProp, IntegerProp, FloatProp, CharProp, StringProp, ListProp
#import psyco
#psyco.full()


class Config(Item):
    cache_path = "objects.cache"
    my_type = "config"

    # Properties:
    # *required : if True, there is not default, and the config must put them
    # *default: if not set, take this value
    # *pythonize : function call to
    # *class_inherit : (Service, 'blabla') : must set this propertie to the
    #  Service class with name blabla
    #  if (Service, None) : must set this properti to the Service class with
    #  same name
    # *unused : just to warn the user that the option he use is no more used
    #  in Shinken
    # *usage_text : if present, will print it to explain why it's no more useful
    properties = {
        'prefix': StringProp(
            default='/usr/local/shinken/'),
        'log_file': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the log broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'object_cache_file': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the status_dat broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'precached_object_file': UnusedProp(
            text='Shinken is faster enough to do not need precached object file.'),
        'resource_file': StringProp(
            default='/tmp/ressources.txt'),
        'temp_file': UnusedProp(
            text=' temporary files are not used in the shinken architecture.'),
        'status_file': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the status_dat broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'status_update_interval': UnusedProp(
            text='This parameter is not longer take from the main file, but must be defined in the status_dat broker module instead. But Shinken will create you one if there are no present and use this parameter in it, so no worry.'),
        'shinken_user': StringProp(
            default='shinken'),
        'shinken_group': StringProp(
            default='shinken'),
        'enable_notifications': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'execute_service_checks': BoolProp(
            default='1',
            class_inherit=[(Service, 'execute_checks')]),
        'accept_passive_service_checks': BoolProp(
            default='1',
            class_inherit=[(Service, 'accept_passive_checks')]),
        'execute_host_checks': BoolProp(
            default='1',
            class_inherit=[(Host, 'execute_checks')]),
        'accept_passive_host_checks': BoolProp(
            default='1',
            class_inherit=[(Host, 'accept_passive_checks')]),
        'enable_event_handlers': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'log_rotation_method': CharProp(
            default='d'),
        'log_archive_path': StringProp(
            default='/usr/local/shinken/var/archives'),
        'check_external_commands': BoolProp(
            default='1'),
        'command_check_interval': UnusedProp(
            text='anoter value than look always the file is useless, so we fix it.'),
        'command_file': StringProp(
            default='/tmp/command.cmd'),
        'external_command_buffer_slots': UnusedProp(
            text='We do not limit the ewxternal command slot.'),
        'check_for_updates': UnusedProp(
            text='network administrators will never allow such communication between server and the external world. Use your distribution packet manager to know if updates are available or go to the http://www.shinken-monitoring.org website instead.'),
        'bare_update_checks': UnusedProp(
            text=None),
        'lock_file': StringProp(
            default='/usr/local/shinken/var/arbiterd.pid'),
        'retain_state_information': UnusedProp(
            text='sorry, retain state information will not be implemented because it is useless.'),
        'state_retention_file': StringProp(
            default=''),
        'retention_update_interval': IntegerProp(
            default='0'),
        'use_retained_program_state': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'use_retained_scheduling_info': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_host_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_service_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_process_host_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_process_service_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_contact_host_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'retained_contact_service_attribute_mask': UnusedProp(
            text='We do not think such an option is interesting to manage.'),
        'use_syslog': BoolProp(
            default='0'),
        'log_notifications': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'log_service_retries': BoolProp(
            default='1',
            class_inherit=[(Service, 'log_retries')]),
        'log_host_retries': BoolProp(
            default='1',
            class_inherit=[(Host, 'log_retries')]),
        'log_event_handlers': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'log_initial_states': BoolProp(
            default='1'),
        'log_external_commands': BoolProp(
            default='1'),
        'log_passive_checks': BoolProp(
            default='1'),
        'global_host_event_handler': StringProp(
            default='',
            class_inherit=[(Host, 'global_event_handler')]),
        'global_service_event_handler': StringProp(
            default='',
            class_inherit=[(Service, 'global_event_handler')]),
        'sleep_time': UnusedProp(
            text='this deprecated option is useless in the shinken way of doing.'),
        'service_inter_check_delay_method': UnusedProp(
            text='This option is useless in the Shinken scheduling. The only way is the smart way.'),
        'max_service_check_spread': IntegerProp(
            default='30',
            class_inherit=[(Service, 'max_check_spread')]),
        'service_interleave_factor': UnusedProp(
            text='This option is useless in the Shinken scheduling because it use a random distribution for initial checks.'),
        'max_concurrent_checks': UnusedProp(
            text='Limiting the max concurrent checks is not helful to got a good running monitoring server.'),
        'check_result_reaper_frequency': UnusedProp(
            text='Shinken do not use reaper process.'),
        'max_check_result_reaper_time': UnusedProp(
            text='Shinken do not use reaper process.'),
        'check_result_path': UnusedProp(
            text='Shinken use in memory returns, not check results on flat file.'),
        'max_check_result_file_age': UnusedProp(
            text='Shinken do not use flat file check resultfiles.'),
        'host_inter_check_delay_method': UnusedProp(
            text='This option is unused in the Shinken scheduling because distribution of the initial check is a random one.'),
        'max_host_check_spread': IntegerProp(
            default='30',
            class_inherit=[(Host, 'max_check_spread')]),
        'interval_length': IntegerProp(
            default='60',
            class_inherit=[(Host, None), (Service, None)]),
        'auto_reschedule_checks': BoolProp(
            managed=False,
            default='1'),
        'auto_rescheduling_interval': IntegerProp(
             managed=False,
             default='1'),
        'auto_rescheduling_window': IntegerProp(
             managed=False,
            default='180'),
        'use_aggressive_host_checking': UnusedProp(
            text='Host agressive checking is an heritage from Nagios 1 and is really useless now.'),
        'translate_passive_host_checks': BoolProp(
             managed=False,
            default='1'),
        'passive_host_checks_are_soft': BoolProp(
             managed=False,
            default='1'),
        'enable_predictive_host_dependency_checks': BoolProp(
             managed=False,
            default='1',
            class_inherit=[(Host, 'enable_predictive_dependency_checks')]),
        'enable_predictive_service_dependency_checks': StringProp(
             managed=False,
            default='1'),
        'cached_host_check_horizon': IntegerProp(
            default='0',
            class_inherit=[(Host, 'cached_check_horizon')]),
        'cached_service_check_horizon': IntegerProp(
            default='0',
            class_inherit=[(Service, 'cached_check_horizon')]),
        'use_large_installation_tweaks': BoolProp(
            default='0',
            class_inherit=[(Host, None), (Service, None)]),
        'free_child_process_memory': UnusedProp(
            text='this option is automatic in Python processes'),
        'child_processes_fork_twice': UnusedProp(
            text='fork twice is not use.'),
        'enable_environment_macros': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'enable_flap_detection': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'low_service_flap_threshold': IntegerProp(
            default='25',
            class_inherit=[(Service, 'low_flap_threshold')]),
        'high_service_flap_threshold': IntegerProp(
            default='50',
            class_inherit=[(Service, 'high_flap_threshold')]),
        'low_host_flap_threshold': IntegerProp(
            default='25',
            class_inherit=[(Host, 'low_flap_threshold')]),
        'high_host_flap_threshold': IntegerProp(
            default='50',
            class_inherit=[(Host, 'high_flap_threshold')]),
        'soft_state_dependencies': BoolProp(
             managed=False,
            default='0'),
        'service_check_timeout': IntegerProp(
            default='10',
            class_inherit=[(Service, 'check_timeout')]),
        'host_check_timeout': IntegerProp(
            default='10',
            class_inherit=[(Host, 'check_timeout')]),
        'event_handler_timeout': IntegerProp(
            default='10',
            class_inherit=[(Host, None), (Service, None)]),
        'notification_timeout': IntegerProp(
            default='5',
            class_inherit=[(Host, None), (Service, None)]),
        'ocsp_timeout': IntegerProp(
            default='5',
            class_inherit=[(Service, None)]),
        'ochp_timeout': IntegerProp(
            default='5',
            class_inherit=[(Host, None)]),
        'perfdata_timeout': IntegerProp(
            default='2',
            class_inherit=[(Host, None), (Service, None)]),
        'obsess_over_services': BoolProp(
            default='0',
            class_inherit=[(Service, 'obsess_over')]),
        'ocsp_command': StringProp(
            default='',
            class_inherit=[(Service, None)]),
        'obsess_over_hosts': BoolProp(
            default='0',
            class_inherit=[(Host, 'obsess_over')]),
        'ochp_command': StringProp(
            default='',
            class_inherit=[(Host, None)]),
        'process_performance_data': BoolProp(
            default='1',
            class_inherit=[(Host, None), (Service, None)]),
        'host_perfdata_command': StringProp(
            default='',
            class_inherit=[(Host, 'perfdata_command')]),
        'service_perfdata_command': StringProp(
            default='',
            class_inherit=[(Service, 'perfdata_command')]),
        'host_perfdata_file': StringProp(
            default='',
            class_inherit=[(Host, 'perfdata_file')]),
        'service_perfdata_file': StringProp(
            default='',
            class_inherit=[(Service, 'perfdata_file')]),
        'host_perfdata_file_template': StringProp(
            default='/tmp/host.perf',
            class_inherit=[(Host, 'perfdata_file_template')]),
        'service_perfdata_file_template': StringProp(
            default='/tmp/host.perf',
            class_inherit=[(Service, 'perfdata_file_template')]),
        'host_perfdata_file_mode': CharProp(
            default='a',
            class_inherit=[(Host, 'perfdata_file_mode')]),
        'service_perfdata_file_mode': CharProp(
            default='a',
            class_inherit=[(Service, 'perfdata_file_mode')]),
        'host_perfdata_file_processing_interval': IntegerProp(
             managed=False,
            default='15'),
        'service_perfdata_file_processing_interval': IntegerProp(
             managed=False,
            default='15'),
        'host_perfdata_file_processing_command': StringProp(
             managed=False,
            default='',
            class_inherit=[(Host, 'perfdata_file_processing_command')]),
        'service_perfdata_file_processing_command': StringProp(
             managed=False,
            default=None),
        'check_for_orphaned_services': BoolProp(
            default='1',
            class_inherit=[(Service, 'check_for_orphaned')]),
        'check_for_orphaned_hosts': BoolProp(
            default='1',
            class_inherit=[(Host, 'check_for_orphaned')]),
        'check_service_freshness': BoolProp(
            default='1',
            class_inherit=[(Service, 'check_freshness')]),
        'service_freshness_check_interval': IntegerProp(
            default='60'),
        'check_host_freshness': BoolProp(
            default='1',
            class_inherit=[(Host, 'check_freshness')]),
        'host_freshness_check_interval': IntegerProp(
            default='60'),
        'additional_freshness_latency': IntegerProp(
            default='15',
            class_inherit=[(Host, None), (Service, None)]),
        'enable_embedded_perl': BoolProp(
            help='It will surely never be managed, but it should not be useful with poller performances.',
             managed=False,
            default='1'),
        'use_embedded_perl_implicitly': BoolProp(
             managed=False,
            default='0'),
        'date_format': StringProp(
            managed=False,
            default=None),
        'use_timezone': StringProp(
            default='',
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'illegal_object_name_chars': StringProp(
            default="""`~!$%^&*"|'<>?,()=""",
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'illegal_macro_output_chars': StringProp(
            default='',
            class_inherit=[(Host, None), (Service, None), (Contact, None)]),
        'use_regexp_matching': BoolProp(
            help=' if you go some host or service definition like prod*, it will surely failed from now, sorry.',
            managed=False,
            default='0'),
        'use_true_regexp_matching': BoolProp(
            managed=False,
            default=None),
        'admin_email': UnusedProp(
            text='sorry, not yet implemented.'),
        'admin_pager': UnusedProp(
            text='sorry, not yet implemented.'),
        'event_broker_options': UnusedProp(
            text='event broker are replaced by modules with a real configuration template.'),
        'broker_module': StringProp(
            default=''),
        'debug_file': UnusedProp(
            text=None),
        'debug_level': UnusedProp(
            text=None),
        'debug_verbosity': UnusedProp(
            text=None),
        'max_debug_file_size': UnusedProp(
            text=None),
        #'$USERn$ : {'required':False, 'default':''} # Add at run in __init__
        
        # SHINKEN SPECIFIC
        'idontcareaboutsecurity': BoolProp(
            default='0'),
        'flap_history': IntegerProp(
            default='20',
            class_inherit=[(Host, None), (Service, None)]),
        'max_plugins_output_length': IntegerProp(
            default='8192',
            class_inherit=[(Host, None), (Service, None)]),

        # Enable or not the notice about old Nagios parameters
        'disable_old_nagios_parameters_whining': BoolProp(
            default='0'),
        
        # Now for problem/impact states changes
        'enable_problem_impacts_states_change': BoolProp(
            default='0',
            class_inherit=[(Host, None), (Service, None)]),

        # More a running value in fact
        'resource_macros_names': StringProp(
            default=[])
    }

    macros = {
        'PREFIX' : 'prefix',
        'MAINCONFIGFILE' : '',
        'STATUSDATAFILE' : '',
        'COMMENTDATAFILE' : '',
        'DOWNTIMEDATAFILE' : '',
        'RETENTIONDATAFILE' : '',
        'OBJECTCACHEFILE' : '',
        'TEMPFILE' : '',
        'TEMPPATH' : '',
        'LOGFILE' : '',
        'RESOURCEFILE' : '',
        'COMMANDFILE' : '',
        'HOSTPERFDATAFILE' : '',
        'SERVICEPERFDATAFILE' : '',
        'ADMINEMAIL' : '',
        'ADMINPAGER' : ''
        #'USERn' : '$USERn$' # Add at run in __init__
        }


    #We create dict of objects
    #Type: 'name in objects' : {Class of object, Class of objects,
    #'property for self for the objects(config)'
    types_creations = {
        'timeperiod' : (Timeperiod, Timeperiods, 'timeperiods'),
        'service' : (Service, Services, 'services'),
        'servicegroup' : (Servicegroup, Servicegroups, 'servicegroups'),
        'command' : (Command, Commands, 'commands'),
        'host' : (Host, Hosts, 'hosts'),
        'hostgroup' : (Hostgroup, Hostgroups, 'hostgroups'),
        'contact' : (Contact, Contacts, 'contacts'),
        'contactgroup' : (Contactgroup, Contactgroups, 'contactgroups'),
        'notificationway' : (NotificationWay, NotificationWays, 'notificationways'),
        'servicedependency' : (Servicedependency, Servicedependencies, 'servicedependencies'),
        'hostdependency' : (Hostdependency, Hostdependencies, 'hostdependencies'),
        'arbiter' : (ArbiterLink, ArbiterLinks, 'arbiterlinks'),
        'scheduler' : (SchedulerLink, SchedulerLinks, 'schedulerlinks'),
        'reactionner' : (ReactionnerLink, ReactionnerLinks, 'reactionners'),
        'broker' : (BrokerLink, BrokerLinks, 'brokers'),
        'poller' : (PollerLink, PollerLinks, 'pollers'),
        'realm' : (Realm, Realms, 'realms'),
        'module' : (Module, Modules, 'modules'),
        'resultmodulation' : (Resultmodulation, Resultmodulations, 'resultmodulations'),
        'escalation' : (Escalation, Escalations, 'escalations'),
        'serviceescalation' : (Serviceescalation, Serviceescalations, 'serviceescalations'),
        'hostescalation' : (Hostescalation, Hostescalations, 'hostescalations'),
        }

    #This tab is used to transform old parameters name into new ones
    #so from Nagios2 format, to Nagios3 ones
    old_properties = {
        'nagios_user' : 'shinken_user',
        'nagios_group' : 'shinken_group'
        }



    def __init__(self):
        self.params = {}
        self.resource_macros_names = []
        #By default the conf is correct
        self.conf_is_correct = True
        #We tag the conf with a magic_hash, a random value to
        #idify this conf
        random.seed(time.time())
        self.magic_hash = random.randint(1, 100000)



    def fill_usern_macros(cls):
        """ Fill all USERN macros with value of properties"""
        #Now the ressource file part
        properties = cls.properties
        macros = cls.macros
        for n in xrange(1, 256):
            n = str(n)
            properties['$USER'+n+'$'] = StringProp(default='')
            macros['USER'+n] = '$USER'+n+'$'
    #Set this a Class method
    fill_usern_macros = classmethod(fill_usern_macros)


    # We've got macro in the resource file and we want
    # to update our MACRO dict with it
    def fill_resource_macros_names_macros(self):
        """ fill the macro dict will all value
        from self.resource_macros_names"""
        macros = self.__class__.macros
        for macro_name in self.resource_macros_names:
            macros[macro_name] = '$'+macro_name+'$'
   

    def load_params(self, params):
        for elt in params:
            elts = elt.split('=')
            if len(elts) == 1: #error, there is no = !
                self.conf_is_correct = False
                print "Error : the parameter %s is malformed! (no = sign)" % elts[0]
            else:
                self.params[elts[0]] = elts[1]
                setattr(self, elts[0], elts[1])
                #Maybe it's a variable as $USER$ or $ANOTHERVATRIABLE$
                #so look at the first character. If it's a $, it's a variable
                #and if it's end like it too
                if elts[0][0] == '$' and elts[0][-1] == '$':
                    macro_name = elts[0][1:-1]
                    self.resource_macros_names.append(macro_name)



    def _cut_line(self, line):
        #punct = '"#$%&\'()*+/<=>?@[\\]^`{|}~'
        tmp = re.split("[" + string.whitespace + "]+" , line)
        r = [elt for elt in tmp if elt != '']
        return r


    def read_config(self, files):
        #just a first pass to get the cfg_file and all files in a buf
        res = ''

        for file in files:
            #We add a \n (or \r\n) to be sure config files are separated
            #if the previous does not finish with a line return
            res += os.linesep
            print "Opening configuration file", file
            try:
                fd = open(file)
                buf = fd.readlines()
                fd.close()
                config_base_dir = os.path.dirname(file)
            except IOError, exp:
                logger.log("Error: Cannot open config file '%s' for reading: %s" % (file, exp))
                #The configuration is invalid because we have a bad file!
                self.conf_is_correct = False
                continue

            for line in buf:
		if os.name != 'nt':
			line = line.replace("\r\n", "\n")
                res += line
                line = line[:-1]
                if re.search("^cfg_file", line) or re.search("^resource_file", line):
                    elts = line.split('=')
                    if os.path.isabs(elts[1]):
                        cfg_file_name = elts[1]
                    else:
                        cfg_file_name = os.path.join(config_base_dir, elts[1])
                    try:
                        fd = open(cfg_file_name)
                        logger.log("Processing object config file '%s'" % cfg_file_name)
                        res += fd.read()
                        #Be sure to add a line return so we won't mix files
                        res += '\n'
                        fd.close()
                    except IOError, exp:
                        logger.log("Error: Cannot open config file '%s' for reading: %s" % (cfg_file_name, exp))
                    #The configuration is invalid because we have a bad file!
                        self.conf_is_correct = False
                elif re.search("^cfg_dir", line):
{'fraction_non_alphanumeric': 0.10947638448770182, 'fraction_numerical': 0.00467783310698657, 'mean_word_length': 2.490486536309171, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4073246', 'n_tokens_mistral': 14970, 'n_tokens_neox': 14489, 'n_words': 4044}