diff --git a/shinken/objects/config.py b/shinken/objects/config.py index da93fd9f0a..11314735cc 100644 --- a/shinken/objects/config.py +++ b/shinken/objects/config.py @@ -1469,7 +1469,7 @@ def explode(self): self.hostdependencies.explode(self.hostgroups) # print "Servicedependency" - self.servicedependencies.explode(self.hostgroups) + self.servicedependencies.explode(self.hostgroups, self.services) # Serviceescalations hostescalations will create new escalations self.serviceescalations.explode(self.escalations) diff --git a/shinken/objects/servicedependency.py b/shinken/objects/servicedependency.py index 36d25e4424..73552cf27b 100644 --- a/shinken/objects/servicedependency.py +++ b/shinken/objects/servicedependency.py @@ -122,7 +122,7 @@ def explode_hostgroup(self, sd, hostgroups): self.add_item(new_sd) # We create new servicedep if necessary (host groups and co) - def explode(self, hostgroups): + def explode(self, hostgroups, services): # The "old" services will be removed. All services with # more than one host or a host group will be in it srvdep_to_remove = [] @@ -143,7 +143,6 @@ def explode(self, hostgroups): hnames = [] if hasattr(sd, 'hostgroup_name'): hg_names = [n.strip() for n in sd.hostgroup_name.split(',')] - hg_names = [hg_name.strip() for hg_name in hg_names] for hg_name in hg_names: hg = hostgroups.find_by_name(hg_name) if hg is None: @@ -182,7 +181,7 @@ def explode(self, hostgroups): dep_hnames.extend([m.strip() for m in hg.members]) if not hasattr(sd, 'dependent_host_name'): - sd.dependent_host_name = getattr(sd, 'host_name', '') + sd.dependent_host_name = '' if sd.dependent_host_name != '': dep_hnames.extend([n.strip() for n in sd.dependent_host_name.split(',')]) @@ -193,6 +192,7 @@ def explode(self, hostgroups): dep_couples.append((dep_hname.strip(), dep_sname.strip())) # Create the new service deps from all of this. + removed_once = False for (dep_hname, dep_sname) in dep_couples: # the sons, like HTTP for (hname, sname) in couples: # the fathers, like MySQL new_sd = sd.copy() @@ -201,8 +201,49 @@ def explode(self, hostgroups): new_sd.dependent_host_name = dep_hname new_sd.dependent_service_description = dep_sname self.add_item(new_sd) - # Ok so we can remove the old one - srvdep_to_remove.append(id) + # Ok so we can remove the old one, once + if not removed_once: + removed_once = True + srvdep_to_remove.append(id) + + if dep_couples == []: + special_singles = {} + for sname in snames: + special_singles[sname] = set() + for service in services: + sname = service.service_description + if sname in snames: + special_singles[sname].add(service.host_name) + if hnames: + # host_names without dependent_host_names + # => host to host dependencies + for sname in snames: + # Should log something when a host does not + # match any service + special_singles[sname] &= set(hnames) + + special_dep_singles = {} + for sname in dep_snames: + special_dep_singles[sname] = set() + for service in services: + sname = service.service_description + if sname in dep_snames: + special_dep_singles[sname].add(service.host_name) + + for dep_sname in special_dep_singles: + for sname in special_singles: + hnames_in_both = special_singles[sname] + hnames_in_both &= special_dep_singles[dep_sname] + for hname in hnames_in_both: + new_sd = sd.copy() + new_sd.host_name = hname + new_sd.service_description = sname + new_sd.dependent_host_name = hname + new_sd.dependent_service_description = dep_sname + self.add_item(new_sd) + if not removed_once: + removed_once = True + srvdep_to_remove.append(id) self.delete_servicesdep_by_id(srvdep_to_remove) diff --git a/test/etc/servicedependency_auto_computing/commands.cfg b/test/etc/servicedependency_auto_computing/commands.cfg new file mode 100644 index 0000000000..dc10ea00c8 --- /dev/null +++ b/test/etc/servicedependency_auto_computing/commands.cfg @@ -0,0 +1,4 @@ +define command{ + command_name nothing + command_line /bin/true +} diff --git a/test/etc/servicedependency_auto_computing/hosts.cfg b/test/etc/servicedependency_auto_computing/hosts.cfg new file mode 100644 index 0000000000..49effaf60c --- /dev/null +++ b/test/etc/servicedependency_auto_computing/hosts.cfg @@ -0,0 +1,53 @@ +define host { + use generic-host + host_name h0 + address 127.0.0.1 +} + +define host { + use generic-host + host_name h1 + address 127.0.0.2 +} + +define host { + use generic-host + host_name h2 + address 127.0.0.3 +} + +define host { + use generic-host + host_name h3 + address 127.0.0.4 +} + +define host { + use generic-host + host_name h4 + address 127.0.0.5 +} + +define host { + use generic-host + host_name h5 + address 127.0.0.6 +} + +define host { + use generic-host + host_name h6 + address 127.0.0.7 +} + +define host { + use generic-host + host_name h7 + address 127.0.0.8 +} + +define host { + use generic-host + host_name h8 + address 127.0.0.9 +} diff --git a/test/etc/servicedependency_auto_computing/servicedependencies.cfg b/test/etc/servicedependency_auto_computing/servicedependencies.cfg new file mode 100644 index 0000000000..7e3f60e688 --- /dev/null +++ b/test/etc/servicedependency_auto_computing/servicedependencies.cfg @@ -0,0 +1,20 @@ +define servicedependency{ + service_description svc0 + dependent_service_description svc2 +} + +define servicedependency{ + service_description svc1 + dependent_service_description svc2, svc3 +} + +define servicedependency{ + service_description svc0 + dependent_service_description svc3 +} + +define servicedependency{ + host_name h6, h7 + service_description svc4 + dependent_service_description svc5 +} diff --git a/test/etc/servicedependency_auto_computing/services.cfg b/test/etc/servicedependency_auto_computing/services.cfg new file mode 100644 index 0000000000..00a3c2ee41 --- /dev/null +++ b/test/etc/servicedependency_auto_computing/services.cfg @@ -0,0 +1,67 @@ +define service{ + active_checks_enabled 1 + check_freshness 0 + check_interval 1 + check_period 24x7 + contact_groups test_contact + event_handler_enabled 1 + failure_prediction_enabled 1 + flap_detection_enabled 1 + is_volatile 0 + max_check_attempts 2 + name generic-service + notification_interval 1 + notification_options w,u,c,r,f,s + notification_period 24x7 + notifications_enabled 1 + obsess_over_service 1 + parallelize_check 1 + passive_checks_enabled 1 + process_perf_data 1 + register 0 + retain_nonstatus_information 1 + retain_status_information 1 + retry_interval 1 +} + +define service { + use generic-service + service_description svc0 + check_command nothing + host_name h0, h1, h2 +} + +define service { + use generic-service + service_description svc1 + check_command nothing + host_name h1, h2, h3, h4 +} + +define service { + use generic-service + service_description svc2 + check_command nothing + host_name h0, h1, h2, h3, h4, h5 +} + +define service { + use generic-service + service_description svc3 + check_command nothing + host_name h3, h4, h5 +} + +define service { + use generic-service + service_description svc4 + check_command nothing + host_name h6, h7, h8 +} + +define service { + use generic-service + service_description svc5 + check_command nothing + host_name h6, h7, h8 +} diff --git a/test/etc/shinken_servicedependency_auto_computing.cfg b/test/etc/shinken_servicedependency_auto_computing.cfg new file mode 100644 index 0000000000..47e43ab946 --- /dev/null +++ b/test/etc/shinken_servicedependency_auto_computing.cfg @@ -0,0 +1,112 @@ +accept_passive_host_checks=1 +accept_passive_service_checks=1 +additional_freshness_latency=15 +admin_email=shinken@localhost +admin_pager=shinken@localhost +auto_reschedule_checks=0 +auto_rescheduling_interval=30 +auto_rescheduling_window=180 +cached_host_check_horizon=15 +cached_service_check_horizon=15 +cfg_file=servicedependency_auto_computing/hosts.cfg +cfg_file=servicedependency_auto_computing/services.cfg +cfg_file=servicedependency_auto_computing/servicedependencies.cfg +cfg_file=servicedependency_auto_computing/commands.cfg +cfg_file=standard/commands.cfg +cfg_file=standard/contacts.cfg +cfg_file=standard/timeperiods.cfg +check_external_commands=1 +check_for_orphaned_hosts=1 +check_for_orphaned_services=1 +check_host_freshness=0 +check_result_path=var/spool/checkresults +check_result_reaper_frequency=10 +check_service_freshness=1 +command_check_interval=-1 +command_file=var/shinken.cmd +daemon_dumps_core=0 +date_format=iso8601 +debug_file=var/shinken.debug +debug_level=112 +debug_verbosity=1 +enable_embedded_perl=0 +enable_environment_macros=1 +enable_event_handlers=1 +enable_flap_detection=0 +enable_notifications=1 +enable_predictive_host_dependency_checks=1 +enable_predictive_service_dependency_checks=1 +event_broker_options=-1 +event_handler_timeout=30 +execute_host_checks=1 +execute_service_checks=1 +external_command_buffer_slots=4096 +high_host_flap_threshold=20 +high_service_flap_threshold=20 +host_check_timeout=30 +host_freshness_check_interval=60 +host_inter_check_delay_method=s +illegal_macro_output_chars=`~\$&|'"<> +illegal_object_name_chars=`~!\$%^&*|'"<>?,()= +interval_length=60 +lock_file=var/shinken.pid +log_archive_path=var/archives +log_event_handlers=1 +log_external_commands=1 +log_file=var/shinken.log +log_host_retries=1 +log_initial_states=1 +log_notifications=1 +log_passive_checks=1 +log_rotation_method=d +log_service_retries=1 +low_host_flap_threshold=5 +low_service_flap_threshold=5 +max_check_result_file_age=3600 +max_check_result_reaper_time=30 +max_concurrent_checks=0 +max_debug_file_size=1000000 +max_host_check_spread=30 +max_service_check_spread=30 +shinken_group=shinken +shinken_user=shinken +notification_timeout=30 +object_cache_file=var/objects.cache +obsess_over_hosts=0 +obsess_over_services=0 +ocsp_timeout=5 +p1_file=/usr/local/shinken/bin/p1.pl +passive_host_checks_are_soft=0 +perfdata_timeout=5 +precached_object_file=var/objects.precache +process_performance_data=1 +resource_file=resource.cfg +retain_state_information=1 +retained_contact_host_attribute_mask=0 +retained_contact_service_attribute_mask=0 +retained_host_attribute_mask=0 +retained_process_host_attribute_mask=0 +retained_process_service_attribute_mask=0 +retained_service_attribute_mask=0 +retention_update_interval=60 +service_check_timeout=60 +service_freshness_check_interval=60 +service_inter_check_delay_method=s +service_interleave_factor=s +sleep_time=0.25 +soft_state_dependencies=0 +state_retention_file=var/retention.dat +status_file=var/status.dat +status_update_interval=5 +temp_file=tmp/shinken.tmp +temp_path=var/tmp +translate_passive_host_checks=0 +use_aggressive_host_checking=0 +use_embedded_perl_implicitly=0 +use_large_installation_tweaks=0 +use_regexp_matching=0 +use_retained_program_state=1 +use_retained_scheduling_info=1 +use_syslog=0 +use_true_regexp_matching=0 +enable_problem_impacts_states_change=1 diff --git a/test/test_servicedependency_auto_computing.py b/test/test_servicedependency_auto_computing.py new file mode 100644 index 0000000000..01d2812c76 --- /dev/null +++ b/test/test_servicedependency_auto_computing.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# Copyright (C) 2009-2014: +# Gabes Jean, naparuba@gmail.com +# Gerhard Lausser, Gerhard.Lausser@consol.de +# +# 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 . + +# +# This file is used to test reading and processing of config files +# + +from shinken_test import ShinkenTest, unittest + + +class TestServiceDependencyAutoComputing(ShinkenTest): + + def setUp(self): + self.setup_with_file('etc/shinken_servicedependency_auto_computing.cfg') + if hasattr(self, 'sched'): + # There are no common host between svc0 and svc3 therefore + # no servicedependency can be created and shinken should + # print the error msg : + # "[Shinken] [items] Service svc3 not found for host + # [Shinken] [items] Service svc0 not found for host + # [Shinken] servicedependencies conf incorrect!!" + self.fail("The configuration should not be correct") + + def test_matching_svc(self): + services = self.conf.services + svcs = [] + svcs.append(services.find_srv_by_name_and_hostname('h0', 'svc0')) + svcs.append(services.find_srv_by_name_and_hostname('h1', 'svc0')) + svcs.append(services.find_srv_by_name_and_hostname('h2', 'svc0')) + for svc in svcs: + self.assertIsNotNone(svc) + dep_svcs = [info_dep[0] for info_dep in svc.act_depend_of_me] + self.assertNotEqual(dep_svcs, []) + for dep_svc in dep_svcs: + self.assertEqual(dep_svc.host_name, svc.host_name) + self.assertEqual(dep_svc.service_description, 'svc2') + + def test_multiple_srv_description(self): + services = self.conf.services + expected_dep_svc = {'h1': ['svc2'], 'h2': ['svc2'], + 'h3': ['svc2', 'svc3'], 'h4': ['svc2', 'svc3']} + svcs = [] + svcs.append(services.find_srv_by_name_and_hostname('h1', 'svc1')) + svcs.append(services.find_srv_by_name_and_hostname('h2', 'svc1')) + svcs.append(services.find_srv_by_name_and_hostname('h3', 'svc1')) + svcs.append(services.find_srv_by_name_and_hostname('h4', 'svc1')) + for svc in svcs: + self.assertIsNotNone(svc) + dep_svcs = [info_dep[0] for info_dep in svc.act_depend_of_me] + description = expected_dep_svc[svc.host_name] + for dep_svc in dep_svcs: + self.assertTrue(dep_svc.service_description in description) + description.remove(dep_svc.service_description) + self.assertEqual(description, []) + + def test_host_to_host_dependency(self): + services = self.conf.services + servicedependencies = self.conf.servicedependencies + svcs = [] + svcs.append(services.find_srv_by_name_and_hostname('h6', 'svc4')) + svcs.append(services.find_srv_by_name_and_hostname('h7', 'svc4')) + for svc in svcs: + self.assertIsNotNone(svc) + dep_svcs = [info_dep[0] for info_dep in svc.act_depend_of_me] + self.assertNotEqual(dep_svcs, []) + for dep_svc in dep_svcs: + self.assertEqual(dep_svc.host_name, svc.host_name) + self.assertEqual(dep_svc.service_description, 'svc5') + + # Check that host8 was not caught in the process + h8_svc4 = services.find_srv_by_name_and_hostname('h8', 'svc4') + + self.assertIsNotNone(h8_svc4) + self.assertFalse(h8_svc4.act_depend_of_me) + + +if __name__ == '__main__': + unittest.main()