Skip to content

Commit

Permalink
Enh: auto-computing of hosts for servicedependencies when none defined
Browse files Browse the repository at this point in the history
What:
    Create dependencies between services host by host without the need
    to write long and dull configuration files.

Why:
    Because servicedependencies are rather useful but really complicated
    to correctly setup right now, especially on large clusters.
    Currently the host_name/dependent_host_name and
    hostgroup_name/dependent_hostgroup_name properties do not allow users
    to define host by host servicedependencies or service to service
    dependencies at once.

How:
    Users will just need to define the service and the dependent_service
    descriptions in the servicedependency configuration.
    The function Servicedependency.explode will then compute the hosts
    having both the dependent_service and the service depended on, and for
    these hosts a servicedependency will be created.

Example:
    With this configuration:

        define host{
            ...
            host_name   h0
        }

        define host{
            ...
            host_name   h1
        }

        define service{
            ...
            service_description svc0
            host_name           h0, h1
        }

        define service{
            ...
            service_description svc1
            host_name           h0, h1
        }

        define servicedependency{
            service_description svc0
            dependent_service_description svc1
        }

    We will create these servicedependencies:

        define servicedependency{
            service_description             svc0
            dependent_service_description   svc1
            host_name                       h0
        }

        define servicedependency{
            service_description             svc0
            dependent_service_description   svc1
            host_name                       h1
        }

And:
    - Tests for the new feature

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   shinken/objects/config.py
#	modified:   shinken/objects/servicedependency.py
#	new file:   test/etc/servicedependency_auto_computing/commands.cfg
#	new file:   test/etc/servicedependency_auto_computing/hosts.cfg
#	new file:   test/etc/servicedependency_auto_computing/servicedependencies.cfg
#	new file:   test/etc/servicedependency_auto_computing/services.cfg
#	new file:   test/etc/shinken_servicedependency_auto_computing.cfg
#	new file:   test/test_servicedependency_auto_computing.py
#
  • Loading branch information
Quentin Bouget committed Jul 20, 2015
1 parent 16c7080 commit 1231522
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 3 deletions.
2 changes: 1 addition & 1 deletion shinken/objects/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
38 changes: 36 additions & 2 deletions shinken/objects/servicedependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down Expand Up @@ -166,6 +166,15 @@ def explode(self, hostgroups):
if not hasattr(sd, 'dependent_hostgroup_name') and hasattr(sd, 'hostgroup_name'):
sd.dependent_hostgroup_name = sd.hostgroup_name

if couples == []:
special_singles = {}
for sname in snames:
special_singles[sname] = []
for service in services:
sname = service.service_description
if sname in snames:
special_singles[sname].append(service.host_name)

# Now the dep part (the sons)
dep_hnames = []
if hasattr(sd, 'dependent_hostgroup_name'):
Expand All @@ -191,6 +200,15 @@ def explode(self, hostgroups):
for dep_sname in dep_snames:
dep_couples.append((dep_hname.strip(), dep_sname.strip()))

if dep_couples == []:
special_dep_singles = {}
for sname in dep_snames:
special_dep_singles[sname] = []
for service in services:
sname = service.service_description
if sname in dep_snames:
special_dep_singles[sname].append(service.host_name)

# Create the new service deps from all of this.
removed_once = False
for (dep_hname, dep_sname) in dep_couples: # the sons, like HTTP
Expand All @@ -203,9 +221,25 @@ def explode(self, hostgroups):
self.add_item(new_sd)
# Ok so we can remove the old one, once
if not removed_once:
removed_once = true
removed_once = True
srvdep_to_remove.append(id)

if dep_couples == [] or couples == []:
for dep_sname in special_dep_singles:
for sname in special_singles:
hnames_in_both = set(special_singles[sname]) &\
set(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)

def linkify(self, hosts, services, timeperiods):
Expand Down
4 changes: 4 additions & 0 deletions test/etc/servicedependency_auto_computing/commands.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
define command{
command_name nothing
command_line /bin/true
}
35 changes: 35 additions & 0 deletions test/etc/servicedependency_auto_computing/hosts.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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
}
14 changes: 14 additions & 0 deletions test/etc/servicedependency_auto_computing/servicedependencies.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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
}
53 changes: 53 additions & 0 deletions test/etc/servicedependency_auto_computing/services.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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
}
112 changes: 112 additions & 0 deletions test/etc/shinken_servicedependency_auto_computing.cfg
Original file line number Diff line number Diff line change
@@ -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
75 changes: 75 additions & 0 deletions test/test_servicedependency_auto_computing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python
# Copyright (C) 2009-2014:
# Gabes Jean, [email protected]
# Gerhard Lausser, [email protected]
#
# 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/>.

#
# 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, [])


if __name__ == '__main__':
unittest.main()

0 comments on commit 1231522

Please sign in to comment.