=pod /****************************************************************************** * Icinga 2 * * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software Foundation * * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ =cut package Icinga2::Convert; use strict; #use Icinga2; use Data::Dumper; use File::Find; use Storable qw(dclone); use feature 'say'; our $dbg_lvl = 1; ################################################################################ ## Validation ################################################################################# sub obj_1x_is_template { my $obj_1x = shift; if (defined($obj_1x->{'register'})) { if ($obj_1x->{'register'} == 0) { return 1; } } return 0; } sub obj_1x_uses_template { my $obj_1x = shift; if (defined($obj_1x->{'use'})) { return 1; } return 0; } # check if notification object exists (2.x only) sub obj_2x_notification_exists { my $objs = shift; my $obj_type = 'notification'; my $obj_attr = '__I2CONVERT_NOTIFICATION_NAME'; # this must be set outside, no matter if template or not XXX my $obj_val = shift; #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); if ($obj->{$obj_attr} eq $obj_val) { #debug("Found object: ".Dumper($obj)); return 1; } } return 0; } # check if command object exists (2.x only) sub obj_2x_command_exists { my $objs = shift; my $obj_type = 'command'; my $obj_attr = '__I2CONVERT_COMMAND_NAME'; my $obj_val = shift; #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); if ($obj->{$obj_attr} eq $obj_val) { #debug("Found object: ".Dumper($obj)); return 1; } } return 0; } ################################################################################ # Migration ################################################################################# ################################################################################# # Get Object Helpers ################################################################################# # get host object by attr 'host_name' sub obj_get_host_obj_by_host_name { my $objs = shift; my $obj_type = 'host'; my $obj_attr = 'host_name'; my $obj_val = shift; #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); if ($obj->{$obj_attr} eq $obj_val) { #debug("Found object: ".Dumper($obj)); return $obj; } } return undef; } # get service object by attr 'host_name' and 'service_description' sub obj_get_service_obj_by_host_name_service_description { my $objs = shift; my $obj_type = 'service'; my $obj_attr_host = shift; my $obj_attr_service = shift; my $obj_val_host = shift; my $obj_val_service = shift; #debug("My objects hive: ".Dumper($objs)); #Icinga2::Utils::debug("Checking for service with host_name=$obj_val_host service_description=$obj_val_service "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr_host}); next if !defined($obj->{$obj_attr_service}); #Icinga2::Utils::debug("Getting attr $obj_attr_host/$obj_attr_service and val $obj_val_host/$obj_val_service"); if (($obj->{$obj_attr_host} eq $obj_val_host) && ($obj->{$obj_attr_service} eq $obj_val_service)) { #Icinga2::Utils::debug("Found object: ".Dumper($obj)); return $obj; } } return undef; } # get contact object by attr 'contact_name' sub obj_get_contact_obj_by_contact_name { my $objs = shift; my $obj_type = shift; my $obj_attr = shift; my $obj_val = shift; #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); if ($obj->{$obj_attr} eq $obj_val) { #debug("Found object: ".Dumper($obj)); return $obj; } } return undef; } # get user object by attr 'user_name' sub obj_get_user_obj_by_user_name { my $objs = shift; my $obj_type = 'user'; my $obj_attr = 'user_name'; my $obj_val = shift; #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); if ($obj->{$obj_attr} eq $obj_val) { #debug("Found object: ".Dumper($obj)); return $obj; } } return undef; } # get template object by attr 'name' sub obj_get_tmpl_obj_by_tmpl_name { my $objs = shift; my $obj_tmpl_type = shift; my $obj_attr_tmpl_name = shift; #debug("My objects hive: ".Dumper($objs)); #Icinga2::Utils::debug("Checking for template name with $obj_attr_tmpl_name"); foreach my $obj_key (keys %{@$objs{$obj_tmpl_type}}) { my $obj = @$objs{$obj_tmpl_type}->{$obj_key}; next if !defined($obj->{'name'}); # XXX it would be safe, but we cannot garantuee it here, so better check before if we want a template or not if ($obj->{'name'} eq $obj_attr_tmpl_name) { #Icinga2::Utils::debug("Found object: ".Dumper($obj)); return $obj; } } return undef; } # get hostgroup object by attr 'hostgroup_name' sub obj_get_hostgroup_obj_by_hostgroup_name { my $objs = shift; my $obj_type = 'hostgroup'; my $obj_attr = 'hostgroup_name'; my $obj_val = shift; #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); if ($obj->{$obj_attr} eq $obj_val) { #debug("Found object: ".Dumper($obj)); return $obj; } } return undef; } ################################################################################# # Get Object Attribute Helpers ################################################################################# # get host_names by attr 'hostgroup_name' sub obj_get_hostnames_arr_by_hostgroup_name { my $objs = shift; my $obj_type = 'host'; my $obj_attr = 'hostgroups'; my $obj_val = shift; my @host_names = (); #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); foreach my $hg (@{$obj->{$obj_attr}}) { if ($hg eq $obj_val) { #debug("Found object: ".Dumper($obj)); push @host_names, $obj->{'host_name'}; } } } return @host_names; } sub obj_get_usernames_arr_by_usergroup_name { my $objs = shift; my $obj_type = 'user'; my $obj_attr = 'usergroups'; my $obj_val = shift; my @user_names = (); #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); foreach my $user (@{$obj->{$obj_attr}}) { if ($user eq $obj_val) { #debug("Found object: ".Dumper($obj)); push @user_names, $obj->{'user_name'}; } } } return @user_names; } # used after relinking all services with servicegroups sub obj_2x_get_service_arr_by_servicegroup_name { my $objs_2x = shift; my $objs = $objs_2x; my $obj_type = 'service'; my $obj_attr = 'servicegroups'; my $obj_val = shift; my @service_names = (); #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; # this there's no attr, try template tree my @servicegroups = (); my $host_name = $obj->{'__I2CONVERT_SERVICE_HOSTNAME'}; # skip invalid resolved objects if (!defined($host_name)) { #say Dumper("missing host name..."); next; } if (defined($obj->{$obj_attr}) && scalar(@{$obj->{$obj_attr}} > 0)) { push @servicegroups, @{$obj->{$obj_attr}}; #say Dumper("$obj_attr ========== found in object $obj->{'__I2CONVERT_SERVICE_HOSTNAME'}:$obj->{'__I2CONVERT_SERVICEDESCRIPTION'}"); #say Dumper(@servicegroups); } else { #say Dumper("START ------------------------"); #say Dumper($obj); my @service_sgs = obj_2x_get_service_servicegroups($objs_2x,$obj,$host_name,$obj_attr); #say Dumper(@service_sgs); if (scalar @service_sgs > 0) { push @servicegroups, @service_sgs; #say Dumper("$obj_attr ========== found in template tree $obj->{'__I2CONVERT_SERVICE_HOSTNAME'}:$obj->{'__I2CONVERT_SERVICEDESCRIPTION'}"); #say Dumper(@servicegroups); #say Dumper($obj); } #say Dumper("END ------------------------"); } #debug("Getting attr $obj_attr and val $obj_val"); # check if servicegroup_name is in the array of servicegroups for this processed service foreach my $servicegroup (@servicegroups) { # skip templates next if ($obj->{'__I2CONVERT_SERVICE_IS_TEMPLATE'} == 1); if ($servicegroup eq $obj_val) { #debug("Found object: ".Dumper($obj)); my $service_name; $service_name->{'__I2CONVERT_SERVICE_HOSTNAME'} = $obj->{'__I2CONVERT_SERVICE_HOSTNAME'}; $service_name->{'__I2CONVERT_SERVICEDESCRIPTION'} = $obj->{'__I2CONVERT_SERVICEDESCRIPTION'}; push @service_names, $service_name; } } } return @service_names; } sub obj_1x_get_all_hostnames_arr { my $objs = shift; my $obj_type = 'host'; my $obj_attr = 'host_name'; my $obj_val = '*'; my @host_names = (); #debug("My objects hive: ".Dumper($objs)); #debug("Checking for type=$obj_type attr=$obj_attr val=$obj_val "); foreach my $obj_key (keys %{@$objs{$obj_type}}) { my $obj = @$objs{$obj_type}->{$obj_key}; next if !defined($obj->{$obj_attr}); #debug("Getting attr $obj_attr and val $obj_val"); push @host_names, $obj->{$obj_attr}; } return @host_names; } # get host_name from object sub obj_1x_get_host_host_name { my $objs_1x = shift; my $obj_1x = shift; my $host_name = ""; # if this object is invalid, bail early return undef if !defined($obj_1x); # first, check if we already got a host_name here in our struct (recursion safety) return $obj_1x->{'__I2CONVERT_HOSTNAME'} if defined($obj_1x->{'__I2CONVERT_HOSTNAME'}); delete $obj_1x->{'__I2CONVERT_HOSTNAME'}; # if this object got what we want, return (it can be recursion and a template!) if(defined($obj_1x->{'host_name'})) { $obj_1x->{'__I2CONVERT_HOSTNAME'} = $obj_1x->{'host_name'}; return $obj_1x->{'__I2CONVERT_HOSTNAME'}; } # we don't have a host name, should we look into a template? # make sure _not_ to use if (defined($obj_1x->{'__I2CONVERT_USES_TEMPLATE'}) && $obj_1x->{'__I2CONVERT_USES_TEMPLATE'} == 1) { # get the object referenced as template - this is an array of templates, loop (funny recursion here) foreach my $obj_1x_template (@{$obj_1x->{'__I2CONVERT_TEMPLATE_NAMES'}}) { # get the template object associated with by its unique 'name' attr my $obj_1x_tmpl = obj_get_tmpl_obj_by_tmpl_name($objs_1x, 'host', $obj_1x_template); # now recurse into ourselves and look for a possible service_description $host_name = obj_1x_get_host_host_name($objs_1x,$obj_1x_tmpl); # bail here if search did not unveil anything next if(!defined($host_name)); # get the host_name and return - first template wins $obj_1x->{'__I2CONVERT_HOSTNAME'} = $host_name; return $obj_1x->{'__I2CONVERT_HOSTNAME'}; } } # no template used, and no host name - broken object, ignore it else { return undef; } # we should never hit here return undef; } # get host_name(s) from service object sub obj_1x_get_service_host_name_arr { # service objects may contain comma seperated host lists (ugly as ...) my $objs_1x = shift; my $obj_1x = shift; my @host_name = (); # if this object is invalid, bail early return undef if !defined($obj_1x); # first, check if we already got a host_name here in our struct (recursion safety) return $obj_1x->{'__I2CONVERT_HOSTNAMES'} if defined($obj_1x->{'__I2CONVERT_HOSTNAMES'}); delete $obj_1x->{'__I2CONVERT_HOSTNAMES'}; # if this object got what we want, return (it can be recursion and a template!) if(defined($obj_1x->{'host_name'})) { #print "DEBUG: found $obj_1x->{'host_name'}\n"; # convert to array delete($obj_1x->{'__I2CONVERT_HOSTNAMES'}); # check if host_name is a wildcard, or a possible comma seperated list # using object tricks - http://docs.icinga.org/latest/en/objecttricks.html#objecttricks-service if ($obj_1x->{'host_name'} =~ /^\*$/) { @host_name = obj_1x_get_all_hostnames_arr($objs_1x); } else { @host_name = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x->{'host_name'}, ',', 1); } push @{$obj_1x->{'__I2CONVERT_HOSTNAMES'}}, @host_name; #print "DEBUG: @{$obj_1x->{'__I2CONVERT_HOSTNAMES'}}"; return @host_name; } # we don't have a host name, should we look into a template? # make sure _not_ to use if (defined($obj_1x->{'__I2CONVERT_USES_TEMPLATE'}) && $obj_1x->{'__I2CONVERT_USES_TEMPLATE'} == 1) { # get the object referenced as template - this is an array of templates, loop (funny recursion here) foreach my $obj_1x_template (@{$obj_1x->{'__I2CONVERT_TEMPLATE_NAMES'}}) { #say Dumper($obj_1x_template); # get the template object associated with by its unique 'name' attr my $obj_1x_tmpl = obj_get_tmpl_obj_by_tmpl_name($objs_1x, 'service', $obj_1x_template); # now recurse into ourselves and look for all possible hostnames in array @host_name = obj_1x_get_service_host_name_arr($objs_1x,$obj_1x_tmpl); #print "DEBUG: from tmpl $obj_1x_template: " . join(" ", @host_name) . "\n"; # bail here if search did not unveil anything next if(!@host_name); # get the host_name and return - first template wins # convert to array delete($obj_1x->{'__I2CONVERT_HOSTNAMES'}); push @{$obj_1x->{'__I2CONVERT_HOSTNAMES'}}, @host_name; return @host_name; } } # no template used, and no host name - broken object, ignore it else { return undef; } # we should never hit here return undef; } # get service_description from object sub obj_1x_get_service_service_description { my $objs_1x = shift; my $obj_1x = shift; my $host_name = shift; my $service_description = ""; # if this object is invalid, bail early return undef if !defined($obj_1x); # first, check if we already got a service_description here in our struct (recursion safety) return $obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'} if defined($obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'}); delete $obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'}; # if this object got what we want, return (it can be recursion and a template!) if(defined($obj_1x->{'service_description'})) { $obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'} = $obj_1x->{'service_description'}; return $obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'}; } # we don't have a service description, should we look into a template? # make sure _not_ to use if (defined($obj_1x->{'__I2CONVERT_USES_TEMPLATE'}) && $obj_1x->{'__I2CONVERT_USES_TEMPLATE'} == 1) { # get the object referenced as template - this is an array of templates, loop (funny recursion here) foreach my $obj_1x_template (@{$obj_1x->{'__I2CONVERT_TEMPLATE_NAMES'}}) { # get the template object associated with by its unique 'name' attr my $obj_1x_tmpl = obj_get_tmpl_obj_by_tmpl_name($objs_1x, 'service', $obj_1x_template); # now recurse into ourselves and look for a possible service_description $service_description = obj_1x_get_service_service_description($objs_1x,$obj_1x_tmpl,$host_name); # we must pass the host_name # bail here if search did not unveil anything next if(!defined($service_description)); # get the service description and return - first template wins $obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'} = $service_description; return $obj_1x->{'__I2CONVERT_SERVICEDESCRIPTION'}; } } # no template used, and not service description - broken object, ignore it else { return undef; } # we should never hit here return undef; } # get service_description from object sub obj_1x_get_service_attr { my $objs_1x = shift; my $obj_1x = shift; my $host_name = shift; my $search_attr = shift; my $service_attr = ""; # if this object is invalid, bail early return undef if !defined($obj_1x); # first, check if we already got a service_description here in our struct (recursion safety) return $obj_1x->{'__I2CONVERT_SEARCH_ATTR'} if defined($obj_1x->{'__I2CONVERT_SEARCH_ATTR'}); delete $obj_1x->{'__I2CONVERT_SEARCH_ATTR'}; # if this object got what we want, return (it can be recursion and a template!) if(defined($obj_1x->{$search_attr})) { $obj_1x->{'__I2CONVERT_SEARCH_ATTR'} = $obj_1x->{$search_attr}; return $obj_1x->{$search_attr}; #return $obj_1x->{'__I2CONVERT_SEARCH_ATTR'}; } # we don't have the attribute, should we look into a template? # make sure _not_ to use if (defined($obj_1x->{'__I2CONVERT_USES_TEMPLATE'}) && $obj_1x->{'__I2CONVERT_USES_TEMPLATE'} == 1) { # get the object referenced as template - this is an array of templates, loop (funny recursion here) foreach my $obj_1x_template (@{$obj_1x->{'__I2CONVERT_TEMPLATE_NAMES'}}) { # get the template object associated with by its unique 'name' attr my $obj_1x_tmpl = obj_get_tmpl_obj_by_tmpl_name($objs_1x, 'service', $obj_1x_template); # now recurse into ourselves and look for a possible service_description $service_attr = obj_1x_get_service_attr($objs_1x,$obj_1x_tmpl,$host_name,$search_attr); # we must pass the host_name and search_attr #say Dumper($service_attr); # bail here if search did not unveil anything next if(!defined($service_attr)); # get the service attr and return - first template wins $obj_1x->{'__I2CONVERT_SEARCH_ATTR'} = $service_attr; return $service_attr; #return $obj_1x->{'__I2CONVERT_SEARCH_ATTR'}; } } # no template used, and not service description - broken object, ignore it else { return undef; } # we should never hit here return undef; } # get service_description from object sub obj_1x_get_contact_attr { my $objs_1x = shift; my $obj_1x = shift; my $search_attr = shift; my $contact_attr = ""; # if this object is invalid, bail early return undef if !defined($obj_1x); # first, check if we already got a attr here in our struct (recursion safety) return $obj_1x->{'__I2CONVERT_SEARCH_ATTR'} if defined($obj_1x->{'__I2CONVERT_SEARCH_ATTR'}); delete $obj_1x->{'__I2CONVERT_SEARCH_ATTR'}; # if this object got what we want, return (it can be recursion and a template!) if(defined($obj_1x->{$search_attr})) { $obj_1x->{'__I2CONVERT_SEARCH_ATTR'} = $obj_1x->{$search_attr}; return $obj_1x->{'__I2CONVERT_SEARCH_ATTR'}; } # we don't have the attribute, should we look into a template? # make sure _not_ to use if (defined($obj_1x->{'__I2CONVERT_USES_TEMPLATE'}) && $obj_1x->{'__I2CONVERT_USES_TEMPLATE'} == 1) { # get the object referenced as template - this is an array of templates, loop (funny recursion here) foreach my $obj_1x_template (@{$obj_1x->{'__I2CONVERT_TEMPLATE_NAMES'}}) { # get the template object associated with by its unique 'name' attr my $obj_1x_tmpl = obj_get_tmpl_obj_by_tmpl_name($objs_1x, 'contact', $obj_1x_template); # now recurse into ourselves and look for a possible contact attr $contact_attr = obj_1x_get_contact_attr($objs_1x,$obj_1x_tmpl,$search_attr); # we must pass the search_attr # bail here if search did not unveil anything next if(!defined($contact_attr)); # get the contact attr and return - first template wins $obj_1x->{'__I2CONVERT_SEARCH_ATTR'} = $contact_attr; return $obj_1x->{'__I2CONVERT_SEARCH_ATTR'}; } } # no template used, and attr - broken object, ignore it else { return undef; } # we should never hit here return undef; } # get servicegroups from object (already 2x and _array_ XXX) sub obj_2x_get_service_servicegroups { my $objs_2x = shift; my $obj_2x = shift; my $host_name = shift; my $search_attr = shift; my @service_groups; #say Dumper("in obj_2x_get_service_attr"); # if this object is invalid, bail early return undef if !defined($obj_2x); # if this object got what we want, return (it can be recursion and a template!) if(defined($obj_2x->{$search_attr}) && scalar(@{$obj_2x->{$search_attr}}) > 0) { #say Dumper("in obj_2x_get_service_attr. found "); #say Dumper($obj_2x->{$search_attr}); return @{$obj_2x->{$search_attr}}; } # we don't have the attribute, should we look into a template? # make sure _not_ to use if (defined($obj_2x->{'__I2CONVERT_USES_TEMPLATE'}) && $obj_2x->{'__I2CONVERT_USES_TEMPLATE'} == 1) { # get the object referenced as template - this is an array of templates, loop (funny recursion here) foreach my $obj_2x_template (@{$obj_2x->{'__I2CONVERT_TEMPLATE_NAMES'}}) { #say Dumper("in obj_2x_get_service_attr template"); #say Dumper($obj_2x_template); # get the template object associated with by its unique 'name' attr my $obj_2x_tmpl = obj_get_tmpl_obj_by_tmpl_name($objs_2x, 'service', $obj_2x_template); #say Dumper($obj_2x_tmpl); # now recurse into ourselves and look for a possible service_description push @service_groups, obj_2x_get_service_servicegroups($objs_2x,$obj_2x_tmpl,$host_name,$search_attr); # we must pass the host_name and search_attr #say Dumper($service_attr); # bail here if search did not unveil anything next if(scalar(@service_groups) == 0); # get the service attr and return - first template wins return @service_groups; } } # no template used, and not service description - broken object, ignore it else { return undef; } # we should never hit here return undef; } ################################################################################ # Conversion ################################################################################# # host|service_notification_commands are a comma seperated list w/o arguments sub convert_notificationcommand { my $objs_1x = shift; my $commands_1x = shift; my $obj_1x = shift; my $user_macros_1x = shift; my $command_name_1x; my @commands = (); my $notification_commands_2x = (); my $host_notification_commands; my $service_notification_commands; # bail early if this is not a valid contact object return undef if (!defined($obj_1x->{'contact_name'})); # bail early if required commands not available (not a valid 1.x object either) if (defined($obj_1x->{'host_notification_commands'})) { $host_notification_commands = $obj_1x->{'host_notification_commands'}; } else { # look in the template $host_notification_commands = obj_1x_get_contact_attr($objs_1x,$obj_1x,'host_notification_commands'); } if (defined($obj_1x->{'service_notification_commands'})) { $service_notification_commands = $obj_1x->{'service_notification_commands'}; } else { # look in the template $service_notification_commands = obj_1x_get_contact_attr($objs_1x,$obj_1x,'service_notification_commands'); } # a contact has a comma seperated list of notification commands by host and service my $all_notification_commands = {}; push @{$all_notification_commands->{'host'}}, split /,\s+/, $host_notification_commands; push @{$all_notification_commands->{'service'}}, split /,\s+/, $service_notification_commands; foreach my $obj_notification_command_key ( keys %{$all_notification_commands}) { # fetch all command names in array via type key my @notification_commands = @{$all_notification_commands->{$obj_notification_command_key}}; my $notification_command_type = $obj_notification_command_key; foreach my $notification_command (@notification_commands) { # now back in all command objects of 1.x foreach my $command_1x_key (keys %{$commands_1x}) { chomp $notification_command; # remove trailing spaces if ($commands_1x->{$command_1x_key}->{'command_name'} eq $notification_command) { # save the type (host, service) and then by command name $notification_commands_2x->{$notification_command_type}->{$notification_command} = Icinga2::Utils::escape_str($commands_1x->{$command_1x_key}->{'command_line'}); #say Dumper($commands_1x->{$command_1x_key}); } } } } return $notification_commands_2x; } # event_handler sub convert_eventhandler { my $commands_1x = shift; my $obj_1x = shift; my $user_macros_1x = shift; my $command_name_1x; my @commands = (); my $event_commands_2x = (); # bail early if required commands not available (not a valid 1.x object either) return if (!defined($obj_1x->{'event_handler'})); my $event_command = $obj_1x->{'event_handler'}; #say Dumper($event_command); # now back in all command objects of 1.x foreach my $command_1x_key (keys %{$commands_1x}) { chomp $event_command; # remove trailing spaces if ($commands_1x->{$command_1x_key}->{'command_name'} eq $event_command) { # save the command line and command name $event_commands_2x->{'command_name'} = $event_command; $event_commands_2x->{'command_line'} = Icinga2::Utils::escape_str($commands_1x->{$command_1x_key}->{'command_line'}); } } return $event_commands_2x; } # check_command accepts argument parameters, special treatment sub convert_checkcommand { my $commands_1x = shift; my $obj_1x = shift; #host or service my $user_macros_1x = shift; my $command_1x; my $command_2x = {}; #say Dumper($commands_1x); #say Dumper($obj_1x); # ignore objects without check_command (may defined in template!) return if (!defined($obj_1x->{'check_command'})); #debug("check_command: $obj_1x->{'check_command'}" ); # split by ! and take only the check command my ($real_command_name_1x, @command_args_1x) = split /!/, $obj_1x->{'check_command'}; # ignore objects with empty check_command attribute #return if (!defined($real_command_name_1x)); #debug("1x Command Name: $real_command_name_1x"); if (@command_args_1x) { #debug("1x Command Args: @command_args_1x"); } foreach my $command_1x_key (keys %{$commands_1x}) { #say Dumper($commands_1x->{$command_1x_key}->{'command_name'}); if ($commands_1x->{$command_1x_key}->{'command_name'} eq $real_command_name_1x) { #debug("Found: $real_command_name_1x"); # save the command_line and the $ARGn$ macros $command_2x->{'check_command'} = Icinga2::Utils::escape_str($commands_1x->{$command_1x_key}->{'command_line'}); $command_2x->{'check_command_name_1x'} = $real_command_name_1x; #Icinga2::Utils::debug("2x Command: $command_2x->{'check_command'}"); # detect $USERn$ macros and replace them too XXX - this should be a global macro? if ($commands_1x->{$command_1x_key}->{'command_line'} =~/\$(USER\d)\$/) { $command_2x->{'command_macros'}->{$1} = Icinga2::Utils::escape_str($user_macros_1x->{$1}); #debug("\$$1\$=$command_2x->{'macros'}->{$1}"); } # save all command args as macros (we'll deal later with them in service definitions) my $arg_cnt = 1; foreach my $command_arg_1x (@command_args_1x) { my $macro_name_2x = "ARG" . $arg_cnt; $command_2x->{'command_macros'}->{$macro_name_2x} = Icinga2::Utils::escape_str($command_arg_1x); #debug("\$$macro_name_2x\$=$command_2x->{'macros'}->{$macro_name_2x}"); $arg_cnt++; } } } return $command_2x; } # convert existing 1x objects into the 2x objects hive sub convert_2x { # v1 -> v2 # register 0 == template # use == inherits template # dependency == ... # escalation == ... # hashref is selectable by type first my $icinga2_cfg = shift; my $cfg_obj_1x = shift; my $cfg_obj_cache_1x = shift; my $user_macros_1x = shift; # build a new hashref with the actual 2.x config inside my $cfg_obj_2x = {}; my $command_obj_cnt = 0; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = '__I2CONVERT_COMMAND_DUMMY'; ###################################### # SERVICE # do the magic lookup for host_name/ # service_description for each object # only once ###################################### my $service_cnt = 0; foreach my $service_obj_1x_key (keys %{@$cfg_obj_1x{'service'}}) { #say Dumper(@$cfg_obj_1x{'service'}->{$service_obj_1x_key}); my $obj_1x_service = @$cfg_obj_1x{'service'}->{$service_obj_1x_key}; #################################################### # verify template is/use #################################################### $obj_1x_service->{'__I2CONVERT_IS_TEMPLATE'} = obj_1x_is_template($obj_1x_service); $obj_1x_service->{'__I2CONVERT_USES_TEMPLATE'} = obj_1x_uses_template($obj_1x_service); $obj_1x_service->{'__I2CONVERT_TEMPLATE_NAME'} = $obj_1x_service->{'name'}; # this can be a comma seperated list of templates my @service_templates = (); if(defined($obj_1x_service->{'use'})) { @service_templates = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_service->{'use'}, ',', 1); } push @{$obj_1x_service->{'__I2CONVERT_TEMPLATE_NAMES'}}, @service_templates; # add dependency to ITL template to objects if ($obj_1x_service->{'__I2CONVERT_IS_TEMPLATE'} == 0) { if(defined($icinga2_cfg->{'itl'}->{'service-template'}) && $icinga2_cfg->{'itl'}->{'service-template'} ne "") { push @{$obj_1x_service->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'service-template'}; $obj_1x_service->{'__I2CONVERT_USES_TEMPLATE'} = 1; } } #################################################### # get related host_name/service_description # used later in host->service resolval #################################################### # XXX even if the service object uses templates, we need to figure out its host_name/service_description in order to safely link hosts towards it my @host_names = obj_1x_get_service_host_name_arr($cfg_obj_1x, $obj_1x_service); #print "DEBUG: service @host_names\n"; delete($obj_1x_service->{'__I2CONVERT_HOSTNAMES'}); if(@host_names == 0) { # set a dummy value for postprocessing - we need the prepared 2.x service for later object tricks push @host_names, "__I2CONVERT_DUMMY"; } push @{$obj_1x_service->{'__I2CONVERT_HOSTNAMES'}}, @host_names; # if there is more than one host_name involved on the service object, clone it in a loop foreach my $service_host_name (@{$obj_1x_service->{'__I2CONVERT_HOSTNAMES'}}) { # we can only look up services with their uniqueness to the host_name $obj_1x_service->{'__I2CONVERT_SERVICEDESCRIPTION'} = obj_1x_get_service_service_description($cfg_obj_1x, $obj_1x_service, $service_host_name); #say Dumper($obj_1x_service); # skip non-template objects without a valid service description (we cannot tolerate 'name' here!) # XXX find a better way - we actually need all services in the list, even if __I2CONVERT_SERVICEDESCRIPTION is undef if (!defined($obj_1x_service->{'__I2CONVERT_SERVICEDESCRIPTION'}) && $obj_1x_service->{'__I2CONVERT_IS_TEMPLATE'} == 0) { #Icinga2::Utils::debug("Skipping invalid service object without service_description ".Dumper($obj_1x_service)); next; } #################################################### # clone service object into 2.x #################################################### $cfg_obj_2x->{'service'}->{$service_cnt} = dclone(@$cfg_obj_1x{'service'}->{$service_obj_1x_key}); # immediately overwrite the correct host_name - if there's no dummy value set for further processing if ($service_host_name !~ /__I2CONVERT_DUMMY/) { $cfg_obj_2x->{'service'}->{$service_cnt}->{__I2CONVERT_SERVICE_HOSTNAME} = $service_host_name; } #say Dumper($cfg_obj_2x->{'service'}->{$service_cnt}); #################################################### # map existing service attributes # same: # - display_name # - max_check_attempts # - action_url # - notes_url # - notes # - icon_image # - notes # change: # - servicegroups (commaseperated strings to array) # - check_command # - check_interval (X min -> Xm) + normal_check_interval # - retry_interval (X min -> Xm) + retry_check_interval # - notification_interval (X min -> Xm) # - check_period - XXX TODO # - notification_period - XXX TODO # - contacts => users XXX DO NOT DELETE contacts and contactgroups, they will be assembled later for notifications! # - #################################################### ########################################## # escape strings in attributes ########################################## if(defined($cfg_obj_2x->{'service'}->{$service_cnt}->{'action_url'})) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'action_url'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'service'}->{$service_cnt}->{'action_url'}); } if(defined($cfg_obj_2x->{'service'}->{$service_cnt}->{'notes_url'})) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'notes_url'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'service'}->{$service_cnt}->{'notes_url'}); } if(defined($cfg_obj_2x->{'service'}->{$service_cnt}->{'notes'})) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'notes'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'service'}->{$service_cnt}->{'notes'}); } if(defined($cfg_obj_2x->{'service'}->{$service_cnt}->{'icon_image'})) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'icon_image'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'service'}->{$service_cnt}->{'icon_image'}); } if(defined($cfg_obj_2x->{'service'}->{$service_cnt}->{'icon_image_alt'})) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'icon_image_alt'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'service'}->{$service_cnt}->{'icon_image_alt'}); } ########################################## # servicegroups ########################################## delete($cfg_obj_2x->{'service'}->{$service_cnt}->{'servicegroups'}); # debug # @{$cfg_obj_2x->{'service'}->{$service_cnt}->{'servicegroups'}} = (); if(defined($obj_1x_service->{'servicegroups'})) { # check if there's additive inheritance required, and save a flag if ($obj_1x_service->{'servicegroups'} =~ /^\+/) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'__I2_CONVERT_SG_ADD'} = 1; $obj_1x_service->{'servicegroups'} =~ s/^\+//; } # convert comma seperated list to array push @{$cfg_obj_2x->{'service'}->{$service_cnt}->{'servicegroups'}}, Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_service->{'servicegroups'}, ',', 1); #print "DEBUG: servicegroups " . join (" ", @{$cfg_obj_2x->{'service'}->{$service_cnt}->{'servicegroups'}}); } #say Dumper($cfg_obj_2x->{'service'}->{$service_cnt}->{__I2CONVERT_SERVICE_HOSTNAME}); #say Dumper($cfg_obj_2x->{'service'}->{$service_cnt}->{'servicegroups'}); ########################################## # check_interval ########################################## my $service_check_interval = undef; if(defined($obj_1x_service->{'normal_check_interval'})) { $service_check_interval = $obj_1x_service->{'normal_check_interval'}; } if(defined($obj_1x_service->{'check_interval'})) { $service_check_interval = $obj_1x_service->{'check_interval'}; } # we assume that 1.x kept 1m default interval, and map it if (defined($service_check_interval)) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'check_interval'} = $service_check_interval."m"; } ########################################## # retry_interval ########################################## my $service_retry_interval = undef; if(defined($obj_1x_service->{'retry_check_interval'})) { $service_retry_interval = $obj_1x_service->{'retry_check_interval'}; } if(defined($obj_1x_service->{'retry_interval'})) { $service_retry_interval = $obj_1x_service->{'retry_interval'}; } # we assume that 1.x kept 1m default interval, and map it if (defined($service_retry_interval)) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'retry_interval'} = $service_retry_interval."m"; } ########################################## # notification_interval ########################################## my $service_notification_interval = undef; if(defined($obj_1x_service->{'notification_interval'})) { $service_notification_interval = $obj_1x_service->{'notification_interval'}; } # we assume that 1.x kept 1m default interval, and map it if (defined($service_notification_interval)) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'notification_interval'} = $service_notification_interval."m"; } ########################################## # eventhandler ########################################## if (defined($obj_1x_service->{'event_handler'})) { my $service_event_command_2x = Icinga2::Convert::convert_eventhandler(@$cfg_obj_1x{'command'}, $obj_1x_service, $user_macros_1x); #say Dumper($service_event_command_2x); # XXX do not add duplicate event commands, they must remain unique by their check_command origin! if ((obj_2x_command_exists($cfg_obj_2x, $obj_1x_service->{'event_handler'}) != 1)) { # create a new EventCommand 2x object with the original name $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_TYPE'} = 'Event'; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = $service_event_command_2x->{'command_name'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_LINE'} = $service_event_command_2x->{'command_line'}; # use the ITL plugin check command template if(defined($icinga2_cfg->{'itl'}->{'eventcommand-template'}) && $icinga2_cfg->{'itl'}->{'eventcommand-template'} ne "") { push @{$cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'eventcommand-template'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } # our PK $command_obj_cnt++; } # the event_handler name of 1.x is still the unique command object name, so we just keep # in __I2_CONVERT_EVENTCOMMAND_NAME in our service object $cfg_obj_2x->{'service'}->{$service_cnt}->{'__I2_CONVERT_EVENTCOMMAND_NAME'} = $service_event_command_2x->{'command_name'}; } ########################################## # volatile is bool only ########################################## if (defined($obj_1x_service->{'is_volatile'})) { $cfg_obj_2x->{'service'}->{$service_cnt}->{'volatile'} = ($obj_1x_service->{'is_volatile'} > 0) ? 1 : 0; } ########################################## # map the service check_command to 2.x ########################################## my $service_check_command_2x = Icinga2::Convert::convert_checkcommand(@$cfg_obj_1x{'command'}, $obj_1x_service, $user_macros_1x); #say Dumper($service_check_command_2x); if (defined($service_check_command_2x->{'check_command_name_1x'})) { # XXX do not add duplicate check commands, they must remain unique by their check_command origin! if (obj_2x_command_exists($cfg_obj_2x, $service_check_command_2x->{'check_command_name_1x'}) != 1) { # create a new CheckCommand 2x object with the original name $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_TYPE'} = 'Check'; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = $service_check_command_2x->{'check_command_name_1x'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_LINE'} = $service_check_command_2x->{'check_command'}; # use the ITL plugin check command template if(defined($icinga2_cfg->{'itl'}->{'checkcommand-template'}) && $icinga2_cfg->{'itl'}->{'checkcommand-template'} ne "") { push @{$cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'checkcommand-template'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } # add the command macros to the command 2x object if(defined($service_check_command_2x->{'command_macros'})) { $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_MACROS'} = dclone($service_check_command_2x->{'command_macros'}); } # our PK $command_obj_cnt++; } # make sure service object still got the checkcommand assigned # the check command name of 1.x is still the unique command object name, so we just keep # in $service_check_command_2x->{'check_command'} the cut real check_command_name_1x delete($service_check_command_2x->{'check_command'}); $cfg_obj_2x->{'service'}->{$service_cnt}->{'__I2_CONVERT_CHECKCOMMAND_NAME'} = $service_check_command_2x->{'check_command_name_1x'}; } # XXX make sure to always add the service specific command arguments, since we have a n .. 1 relation here # add the command macros to the command 2x object if(defined($service_check_command_2x->{'command_macros'})) { @$cfg_obj_2x{'service'}->{$service_cnt}->{'__I2CONVERT_MACROS'} = dclone($service_check_command_2x->{'command_macros'}); } # our PK $service_cnt++; } } ###################################### # HOST # use => inherit template # register 0 => template # check_command => create a new service? ###################################### # "get all 'host' hashref as array in hashmap, and their keys to access it" foreach my $host_obj_1x_key (keys %{@$cfg_obj_1x{'host'}}) { #say Dumper(@$cfg_obj_1x{'host'}->{$host_obj_1x_key}); my $obj_1x_host = @$cfg_obj_1x{'host'}->{$host_obj_1x_key}; #################################################### # verify template is/use #################################################### $obj_1x_host->{'__I2CONVERT_IS_TEMPLATE'} = obj_1x_is_template($obj_1x_host); $obj_1x_host->{'__I2CONVERT_USES_TEMPLATE'} = obj_1x_uses_template($obj_1x_host); $obj_1x_host->{'__I2CONVERT_TEMPLATE_NAME'} = $obj_1x_host->{'name'}; # this can be a comma seperated list of templates my @host_templates = (); if(defined($obj_1x_host->{'use'})) { @host_templates = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_host->{'use'}, ',', 1); } push @{$obj_1x_host->{'__I2CONVERT_TEMPLATE_NAMES'}}, @host_templates; #################################################### # get the related host_name #################################################### # XXX even if the host object uses templates, we need to figure out its host_name in order to safely link services towards it $obj_1x_host->{'__I2CONVERT_HOSTNAME'} = obj_1x_get_host_host_name($cfg_obj_1x, $obj_1x_host); #################################################### # skip objects without a valid hostname #################################################### if (!defined($obj_1x_host->{'__I2CONVERT_HOSTNAME'}) && $obj_1x_host->{'__I2CONVERT_IS_TEMPLATE'} == 0) { #Icinga2::Utils::debug("Skipping invalid host object without host_name ".Dumper($obj_1x_host)); next; } #say Dumper($obj_1x_host); # # FIXME do that later on # primary host->service relation resolval #################################################### # Clone the existing object into 2.x #################################################### # save a copy with the valid ones $cfg_obj_2x->{'host'}->{$host_obj_1x_key} = dclone(@$cfg_obj_1x{'host'}->{$host_obj_1x_key}); #################################################### # map existing host attributes # same: # - max_check_attempts # - action_url # - notes_url # - notes # - icon_image # - statusmap_image # - notes # change: # - display_name (if alias is set, overwrites it) # - hostgroups (commaseperated strings to array) # - check_interval (X min -> Xm) + normal_check_interval # - retry_interval (X min -> Xm) + retry_check_interval # - notification_interval (X min -> Xm) # - check_period - XXX TODO # - notification_period - XXX TODO # - contacts => users XXX DO NOT DELETE contacts and contactgroups - they will be assembled later for notifications! # - # remove: # - check_command #################################################### ########################################## # macros (address*, etc) ########################################## if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'address'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'__I2CONVERT_MACROS'}->{'address'} = $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'address'}; } if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'address6'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'__I2CONVERT_MACROS'}->{'address6'} = $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'address6'}; } ########################################## # escape strings in attributes ########################################## if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'action_url'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'action_url'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'action_url'}); } if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notes_url'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notes_url'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notes_url'}); } if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notes'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notes'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notes'}); } if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'icon_image'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'icon_image'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'icon_image'}); } if(defined($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'icon_image_alt'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'icon_image_alt'} = Icinga2::Utils::escape_str($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'icon_image_alt'}); } #################################################### # display_name -> alias mapping #################################################### # if there was an host alias defined, make this the primary display_name for 2x if(defined($obj_1x_host->{'alias'})) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'display_name'} = Icinga2::Utils::escape_str($obj_1x_host->{'alias'}); } ########################################## # hostgroups ########################################## delete($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'hostgroups'}); if(defined($obj_1x_host->{'hostgroups'})) { # check if there's additive inheritance required, and save a flag if ($obj_1x_host->{'hostgroups'} =~ /^\+/) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'__I2_CONVERT_HG_ADD'} = 1; $obj_1x_host->{'hostgroups'} =~ s/^\+//; } # convert comma seperated list to array push @{$cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'hostgroups'}}, Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_host->{'hostgroups'}, ',', 1); #print "DEBUG: hostgroups " . join (" ", @{$cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'hostgroups'}}); } ########################################## # check_interval ########################################## my $host_check_interval = undef; if(defined($obj_1x_host->{'normal_check_interval'})) { $host_check_interval = $obj_1x_host->{'normal_check_interval'}; } if(defined($obj_1x_host->{'check_interval'})) { $host_check_interval = $obj_1x_host->{'check_interval'}; } # we assume that 1.x kept 1m default interval, and map it if (defined($host_check_interval)) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'check_interval'} = $host_check_interval."m"; } ########################################## # retry_interval ########################################## my $host_retry_interval = undef; if(defined($obj_1x_host->{'retry_check_interval'})) { $host_retry_interval = $obj_1x_host->{'retry_check_interval'}; } if(defined($obj_1x_host->{'retry_interval'})) { $host_retry_interval = $obj_1x_host->{'retry_interval'}; } # we assume that 1.x kept 1m default interval, and map it if (defined($host_retry_interval)) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'retry_interval'} = $host_retry_interval."m"; } ########################################## # notification_interval ########################################## my $host_notification_interval = undef; if(defined($obj_1x_host->{'notification_interval'})) { $host_notification_interval = $obj_1x_host->{'notification_interval'}; } # we assume that 1.x kept 1m default interval, and map it if (defined($host_notification_interval)) { $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'notification_interval'} = $host_notification_interval."m"; } if(defined($obj_1x_host->{'parents'})) { my @host_parents = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_host->{'parents'}, ',', 1); push @{$cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'__I2CONVERT_PARENT_HOSTNAMES'}}, @host_parents; } #################################################### # Icinga 2 Hosts don't have a check_command anymore # - get a similar service with command_name lookup # and link that service #################################################### my $host_check_command_2x = Icinga2::Convert::convert_checkcommand(@$cfg_obj_1x{'command'}, $obj_1x_host, $user_macros_1x); #say Dumper($host_check_command_2x); delete($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'check_command'}); if(defined($host_check_command_2x->{'check_command_name_1x'})) { # XXX TODO match on the command_name in available services for _this_ host later on. right on, just save it $cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'__I2CONVERT_HOSTCHECK_NAME'} = $host_check_command_2x->{'check_command_name_1x'}; } # XXX skip all host templates, they do not need to be linked with services! #if ($cfg_obj_2x->{'host'}->{$host_obj_1x_key}->{'__I2CONVERT_IS_TEMPLATE'} == 1) { # Icinga2::Utils::debug("Skipping host template for linking against service."); # next; #} # NOTE: the relation between host and services for 2x will be done later # this is due to the reason we may manipulate service objects later # e.g. when relinking the servicegroup members, etc # otherwise we would have to make sure to update 2 locations everytime } ###################################### # CONTACT => USER ###################################### my $user_cnt = 0; if (!@$cfg_obj_1x{'contact'}) { goto SKIP_CONTACTS; } foreach my $contact_obj_1x_key (keys %{@$cfg_obj_1x{'contact'}}) { my $obj_1x_contact = @$cfg_obj_1x{'contact'}->{$contact_obj_1x_key}; #################################################### # verify template is/use #################################################### $obj_1x_contact->{'__I2CONVERT_IS_TEMPLATE'} = obj_1x_is_template($obj_1x_contact); $obj_1x_contact->{'__I2CONVERT_USES_TEMPLATE'} = obj_1x_uses_template($obj_1x_contact); $obj_1x_contact->{'__I2CONVERT_TEMPLATE_NAME'} = $obj_1x_contact->{'name'}; # XXX makes sense when IS_TEMPLATE is set # this can be a comma seperated list of templates my @contact_templates = (); if(defined($obj_1x_contact->{'use'})) { @contact_templates = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_contact->{'use'}, ',', 1); } push @{$obj_1x_contact->{'__I2CONVERT_TEMPLATE_NAMES'}}, @contact_templates; #################################################### # get all notification commands #################################################### my $notification_commands_2x = Icinga2::Convert::convert_notificationcommand($cfg_obj_1x, @$cfg_obj_1x{'command'}, $obj_1x_contact, $user_macros_1x); #say Dumper($obj_1x_contact); #say Dumper($notification_commands_2x); #say Dumper("======================================"); # clone it into our users hash $cfg_obj_2x->{'user'}->{$contact_obj_1x_key} = dclone(@$cfg_obj_1x{'contact'}->{$contact_obj_1x_key}); # set our own __I2CONVERT_TYPE $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'__I2CONVERT_TYPE'} = "user"; ########################################## # macros (email, pager, address1..6) ########################################## if(defined($obj_1x_contact->{'email'})) { $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'__I2CONVERT_MACROS'}->{'email'} = $obj_1x_contact->{'email'}; } if(defined($obj_1x_contact->{'pager'})) { $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'__I2CONVERT_MACROS'}->{'pager'} = $obj_1x_contact->{'pager'}; } for(my $i=1;$i<=6;$i++) { my $address = "address$i"; if(defined($obj_1x_contact->{$address})) { $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'__I2CONVERT_MACROS'}->{$address} = $obj_1x_contact->{$address}; } } #################################################### # migrate renamed attributes #################################################### $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'user_name'} = $obj_1x_contact->{'contact_name'}; if(defined($obj_1x_contact->{'alias'})) { $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'display_name'} = Icinga2::Utils::escape_str($obj_1x_contact->{'alias'}); } delete($cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'usergroups'}); if(defined($obj_1x_contact->{'contactgroups'})) { # check if there's additive inheritance required, and save a flag if ($obj_1x_contact->{'contactgroups'} =~ /^\+/) { $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'__I2_CONVERT_UG_ADD'} = 1; $obj_1x_contact->{'contactgroups'} =~ s/^\+//; } push @{$cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'usergroups'}}, Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_contact->{'contactgroups'}, ',', 1); #print "DEBUG: usergroups " . join (" ", @{$cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'usergroups'}}); } # we need to rebuild that notification logic entirely for 2.x # do that later when all objects are processed and prepared (all relations?) #say Dumper($notification_commands_2x); $cfg_obj_2x->{'user'}->{$contact_obj_1x_key}->{'__I2CONVERT_NOTIFICATION_COMMANDS'} = $notification_commands_2x; } SKIP_CONTACTS: ###################################### # GROUPS ###################################### if (!@$cfg_obj_1x{'hostgroup'}) { goto SKIP_HOSTGROUPS; } # host->hostgroups and hostgroup-members relinked together foreach my $hostgroup_obj_1x_key (keys %{@$cfg_obj_1x{'hostgroup'}}) { my $obj_1x_hostgroup = @$cfg_obj_1x{'hostgroup'}->{$hostgroup_obj_1x_key}; # clone it into our hash $cfg_obj_2x->{'hostgroup'}->{$hostgroup_obj_1x_key} = dclone(@$cfg_obj_1x{'hostgroup'}->{$hostgroup_obj_1x_key}); if(defined($obj_1x_hostgroup->{'alias'})) { $cfg_obj_2x->{'hostgroup'}->{$hostgroup_obj_1x_key}->{'display_name'} = Icinga2::Utils::escape_str($obj_1x_hostgroup->{'alias'}); } #################################################### # check if host_groupname exists, if not, try to copy it from 'name' (no template inheritance possible in groups!) #################################################### if(!defined($obj_1x_hostgroup->{'hostgroup_name'})) { if(defined($obj_1x_hostgroup->{'name'})) { $cfg_obj_2x->{'hostgroup'}->{$hostgroup_obj_1x_key}->{'hostgroup_name'} = $obj_1x_hostgroup->{'name'}; } } #################################################### # check if there are members defined, we must re-link them in their host object again #################################################### if(defined($obj_1x_hostgroup->{'members'})) { my @hg_members = (); # check if members is a wildcard, or a possible comma seperated list # using object tricks - http://docs.icinga.org/latest/en/objecttricks.html#objecttricks-service # XXX better create a master template where all hosts inherit from, and use additive hostgroups attribute if ($obj_1x_hostgroup->{'members'} =~ /^\*$/) { @hg_members = obj_1x_get_all_hostnames_arr($cfg_obj_2x); } else { @hg_members = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_hostgroup->{'members'}, ',', 1); } foreach my $hg_member (@hg_members) { my $obj_2x_hg_member = obj_get_host_obj_by_host_name($cfg_obj_2x, $hg_member); #print "DEBUG: $hg_member found.\n"; push @{$obj_2x_hg_member->{'hostgroups'}}, $obj_1x_hostgroup->{'hostgroup_name'}; } } } SKIP_HOSTGROUPS: if (!@$cfg_obj_1x{'servicegroup'}) { goto SKIP_SERVICEGROUPS; } # service->servicegroups and servicegroup->members relinked together foreach my $servicegroup_obj_1x_key (keys %{@$cfg_obj_1x{'servicegroup'}}) { my $obj_1x_servicegroup = @$cfg_obj_1x{'servicegroup'}->{$servicegroup_obj_1x_key}; # clone it into our hash $cfg_obj_2x->{'servicegroup'}->{$servicegroup_obj_1x_key} = dclone(@$cfg_obj_1x{'servicegroup'}->{$servicegroup_obj_1x_key}); if(defined($obj_1x_servicegroup->{'alias'})) { $cfg_obj_2x->{'servicegroup'}->{$servicegroup_obj_1x_key}->{'display_name'} = Icinga2::Utils::escape_str($obj_1x_servicegroup->{'alias'}); } #################################################### # check if service_groupname exists, if not, try to copy it from 'name' (no template inheritance possible in groups!) #################################################### if(!defined($obj_1x_servicegroup->{'servicegroup_name'})) { if(defined($obj_1x_servicegroup->{'name'})) { $cfg_obj_2x->{'servicegroup'}->{$servicegroup_obj_1x_key}->{'servicegroup_name'} = $obj_1x_servicegroup->{'name'}; } } #################################################### # check if there are members defined, we must re-link them in their service object again #################################################### if(defined($obj_1x_servicegroup->{'members'})) { # host1,svc1,host2,svc2 is just an insane way of parsing stuff - do NOT sort here. my @sg_members = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_servicegroup->{'members'}, ',', 0); # just some safety for debugging if(@sg_members % 2 != 0) { Icinga2::Utils::debug("servicegroup $obj_1x_servicegroup->{'servicegroup_name'} members list not even: $obj_1x_servicegroup->{'members'}"); } #print "DEBUG: $obj_1x_servicegroup->{'servicegroup_name'}: @sg_members\n"; my $obj_2x_sg_member; while (scalar(@sg_members) > 0) { my $sg_member_host = shift(@sg_members); my $sg_member_service = shift(@sg_members); #print "DEBUG: Looking for $obj_1x_servicegroup->{'servicegroup_name'}: $sg_member_host/$sg_member_service\n"; # since we require the previously looked up unique hostname/service_description, we use the new values in 2x objects (__I2CONVERT_...) my $obj_2x_sg_member = obj_get_service_obj_by_host_name_service_description($cfg_obj_2x, "__I2CONVERT_SERVICE_HOSTNAME", "__I2CONVERT_SERVICEDESCRIPTION", $sg_member_host, $sg_member_service); #print "DEBUG: $sg_member_host,$sg_member_service found.\n"; push @{$obj_2x_sg_member->{'servicegroups'}}, $obj_1x_servicegroup->{'servicegroup_name'}; } #say Dumper($cfg_obj_2x->{'service'}); } } SKIP_SERVICEGROUPS: if (!@$cfg_obj_1x{'contactgroup'}) { goto SKIP_CONTACTGROUPS; } # contact->contactgroups and contactgroup->members relinked together foreach my $contactgroup_obj_1x_key (keys %{@$cfg_obj_1x{'contactgroup'}}) { my $obj_1x_contactgroup = @$cfg_obj_1x{'contactgroup'}->{$contactgroup_obj_1x_key}; # clone it into our hash $cfg_obj_2x->{'usergroup'}->{$contactgroup_obj_1x_key} = dclone(@$cfg_obj_1x{'contactgroup'}->{$contactgroup_obj_1x_key}); $cfg_obj_2x->{'usergroup'}->{$contactgroup_obj_1x_key}->{'__I2CONVERT_TYPE'} = "usergroup"; #################################################### # migrate renamed attributes #################################################### $cfg_obj_2x->{'usergroup'}->{$contactgroup_obj_1x_key}->{'usergroup_name'} = $obj_1x_contactgroup->{'contactgroup_name'}; if(defined($obj_1x_contactgroup->{'alias'})) { $cfg_obj_2x->{'usergroup'}->{$contactgroup_obj_1x_key}->{'display_name'} = Icinga2::Utils::escape_str($obj_1x_contactgroup->{'alias'}); } #################################################### # check if contact_groupname exists, if not, try to copy it from 'name' (no template inheritance possible in groups!) #################################################### if(!defined($obj_1x_contactgroup->{'contactgroup_name'})) { if(defined($obj_1x_contactgroup->{'name'})) { $cfg_obj_2x->{'usergroup'}->{$contactgroup_obj_1x_key}->{'usergroup_name'} = $obj_1x_contactgroup->{'name'}; } } #################################################### # check if there are members defined, we must re-link them in their host object again #################################################### if(defined($obj_1x_contactgroup->{'members'})) { my @cg_members = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_contactgroup->{'members'}, ',', 1); foreach my $cg_member (@cg_members) { my $obj_2x_cg_member = obj_get_contact_obj_by_contact_name($cfg_obj_2x, "user", "user_name", $cg_member); #print "DEBUG: $cg_member found.\n"; push @{$obj_2x_cg_member->{'usergroups'}}, $obj_1x_contactgroup->{'contactgroup_name'}; } } } SKIP_CONTACTGROUPS: ###################################### # TIMEPERIODS ###################################### if (!@$cfg_obj_1x{'timeperiod'}) { goto SKIP_TIMEPERIODS; } foreach my $timeperiod_obj_1x_key (keys %{@$cfg_obj_1x{'timeperiod'}}) { my $obj_1x_timeperiod = @$cfg_obj_1x{'timeperiod'}->{$timeperiod_obj_1x_key}; # clone it into our hash $cfg_obj_2x->{'timeperiod'}->{$timeperiod_obj_1x_key} = dclone(@$cfg_obj_1x{'timeperiod'}->{$timeperiod_obj_1x_key}); #################################################### # add dependency to ITL template to objects #################################################### if(defined($icinga2_cfg->{'itl'}->{'timeperiod-template'}) && $icinga2_cfg->{'itl'}->{'timeperiod-template'} ne "") { push @{$cfg_obj_2x->{'timeperiod'}->{$timeperiod_obj_1x_key}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'timeperiod-template'}; $cfg_obj_2x->{'timeperiod'}->{$timeperiod_obj_1x_key}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } #################################################### # display_name -> alias mapping #################################################### # if there was a timeperiod alias defined, make this the primary display_name for 2x if(defined($obj_1x_timeperiod->{'alias'})) { $cfg_obj_2x->{'timeperiod'}->{$timeperiod_obj_1x_key}->{'display_name'} = Icinga2::Utils::escape_str($obj_1x_timeperiod->{'alias'}); delete($cfg_obj_2x->{'timeperiod'}->{$timeperiod_obj_1x_key}->{'alias'}); } } SKIP_TIMEPERIODS: ###################################### # DEPENDENCIES ###################################### if (!@$cfg_obj_1x{'hostdependency'}) { goto SKIP_HOSTDEPS; } foreach my $hostdependency_obj_1x_key (keys %{@$cfg_obj_1x{'hostdependency'}}) { my $obj_1x_hostdependency = @$cfg_obj_1x{'hostdependency'}->{$hostdependency_obj_1x_key}; # clone it into our hash $cfg_obj_2x->{'hostdependency'}->{$hostdependency_obj_1x_key} = dclone(@$cfg_obj_1x{'hostdependency'}->{$hostdependency_obj_1x_key}); # 1. the single host_name entries # host_name is the master host (comma seperated list) # dependent_host_name is the child host (comma seperated list) my @master_host_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_hostdependency->{'host_name'}, ',', 1); my @child_host_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_hostdependency->{'dependent_host_name'}, ',', 1); # go through all child hosts, and push to the parents array foreach my $child_host_name (@child_host_names) { my $child_host_obj = obj_get_host_obj_by_host_name($cfg_obj_2x, $child_host_name); push @{$child_host_obj->{'__I2CONVERT_PARENT_HOSTNAMES'}}, @master_host_names; } # 2. the infamous group logic - let's loop because we're cool my @master_hostgroup_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_hostdependency->{'hostgroup_name'}, ',', 1); my @child_hostgroup_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_hostdependency->{'dependent_hostgroup_name'}, ',', 1); my @all_master_hostgroup_hostnames = (); # get all hosts as array for the master host groups foreach my $master_hostgroup_name (@master_hostgroup_names) { my @host_master_hostgroup_hostnames = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $master_hostgroup_name); push @all_master_hostgroup_hostnames, @host_master_hostgroup_hostnames; } # go through all child hostgroups and fetch their host objects, setting foreach my $child_hostgroup_name (@child_hostgroup_names) { my @host_child_hostgroup_hostnames = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $child_hostgroup_name); foreach my $host_child_hostgroup_hostname (@host_child_hostgroup_hostnames) { my $child_host_obj = obj_get_host_obj_by_host_name($cfg_obj_2x, $host_child_hostgroup_hostname); push @{$child_host_obj->{'__I2CONVERT_PARENT_HOSTNAMES'}}, @all_master_hostgroup_hostnames; } } } # XXX ugly but works SKIP_HOSTDEPS: if (!@$cfg_obj_1x{'servicedependency'}) { goto SKIP_SVCDEPS; } foreach my $servicedependency_obj_1x_key (keys %{@$cfg_obj_1x{'servicedependency'}}) { my $obj_1x_servicedependency = @$cfg_obj_1x{'servicedependency'}->{$servicedependency_obj_1x_key}; # clone it into our hash $cfg_obj_2x->{'servicedependency'}->{$servicedependency_obj_1x_key} = dclone(@$cfg_obj_1x{'servicedependency'}->{$servicedependency_obj_1x_key}); # 1. the single host_name / service_description entries # service_description is a string, while the host_name directive is still a comma seperated list my @master_host_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_servicedependency->{'host_name'}, ',', 1); my @child_host_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_servicedependency->{'dependent_host_name'}, ',', 1); my $master_service_description = $obj_1x_servicedependency->{'service_description'}; my $child_service_description = $obj_1x_servicedependency->{'dependent_service_description'}; # XXX object tricks allow more here # - comma seperated list of service descriptions on a single *host_name # - wildcard * for all services on a single *host_name # go through all child hosts, and get the service object by host_name and our single service_description foreach my $child_host_name (@child_host_names) { my $child_service_obj = obj_get_service_obj_by_host_name_service_description($cfg_obj_2x, "__I2CONVERT_SERVICE_HOSTNAME", "__I2CONVERT_SERVICEDESCRIPTION", $child_host_name, $child_service_description); # stash all master dependencies onto the child service foreach my $master_host_name (@master_host_names) { # use some calculated unique key here (no, i will not split the string later! we are perl, we can do hashes) my $master_key = $master_host_name."-".$master_service_description; $child_service_obj->{'__I2CONVERT_PARENT_SERVICES'}->{$master_key}->{'host'} = $master_host_name; $child_service_obj->{'__I2CONVERT_PARENT_SERVICES'}->{$master_key}->{'service'} = $master_service_description; } } # 2. the infamous group logic - but only for hostgroups here my @master_hostgroup_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_servicedependency->{'hostgroup_name'}, ',', 1); my @child_hostgroup_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_servicedependency->{'dependent_hostgroup_name'}, ',', 1); my @all_master_hostgroup_hostnames = (); # get all hosts as array for the master host groups foreach my $master_hostgroup_name (@master_hostgroup_names) { my @host_master_hostgroup_hostnames = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $master_hostgroup_name); push @all_master_hostgroup_hostnames, @host_master_hostgroup_hostnames; } #say Dumper($obj_1x_servicedependency); #say " DEBUG: all master hg hostnames: ".Dumper(@all_master_hostgroup_hostnames); # go through all child hostgroups and fetch their host objects, setting foreach my $child_hostgroup_name (@child_hostgroup_names) { my @host_child_hostgroup_hostnames = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $child_hostgroup_name); # child hostgroup members #say " DEBUG: child hg hostnames: ".Dumper(@host_child_hostgroup_hostnames); foreach my $host_child_hostgroup_hostname (@host_child_hostgroup_hostnames) { my $child_service_obj = obj_get_service_obj_by_host_name_service_description($cfg_obj_2x, "__I2CONVERT_SERVICE_HOSTNAME", "__I2CONVERT_SERVICEDESCRIPTION", $host_child_hostgroup_hostname, $child_service_description); # now loop through all master hostgroups and get their hosts foreach my $master_hostgroup_name (@master_hostgroup_names) { my @host_master_hostgroup_names = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $master_hostgroup_name); # master hostgroup members foreach my $host_master_hostgroup_hostname (@host_master_hostgroup_names) { # use some calculated unique key here (no, i will not split the string later! we are perl, we can do hashes) my $master_key = $host_master_hostgroup_hostname."-".$master_service_description; $child_service_obj->{'__I2CONVERT_PARENT_SERVICES'}->{$master_key}->{'host'} = $host_master_hostgroup_hostname; # XXX 5th foreach. awesome! $child_service_obj->{'__I2CONVERT_PARENT_SERVICES'}->{$master_key}->{'service'} = $master_service_description; } } } } } # XXX ugly but works SKIP_SVCDEPS: ###################################### # SERVICE->HG<-HOSTMEMBERS MAGIC # we've skipped services without # host_name before, now deal with them # hostgroups have been prepared with # all their members too (!!) # we're working on 2.x objects now ###################################### # get the max key for hosts (required for adding more) my $obj_2x_hosts_cnt = (reverse sort {$a <=> $b} (keys %{@$cfg_obj_2x{'host'}}))[0]; #print "FOO: $obj_2x_hosts_cnt\n"; my $obj_2x_services_hg = {}; # filter all services with a hostgroup_name into smaller list foreach my $service_obj_2x_key (keys %{@$cfg_obj_2x{'service'}}) { my $obj_2x_service = @$cfg_obj_2x{'service'}->{$service_obj_2x_key}; #print "DEBUG: now checking $obj_2x_service->{'service_description'}...\n"; # skip all services which already got a host_name? which one wins here? XXX # skip all services without hostgroup_name next if(!defined($obj_2x_service->{'hostgroup_name'})); # XXX object tricks allow to use a comma seperated list of hostgroup_names! # http://docs.icinga.org/latest/en/objecttricks.html my @hostgroup_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_2x_service->{'hostgroup_name'}, ',', 1); foreach my $hostgroup_name (@hostgroup_names) { # we need to save all services first, but our new key is the hostgroupname # so that we can create multiple services for a single hosthg template later on push @{$obj_2x_services_hg->{$hostgroup_name}}, $service_obj_2x_key; } } # now loop over all hostgroups with service relations foreach my $service_hg_obj_2x_key (keys %{$obj_2x_services_hg}) { #say Dumper($obj_2x_services_hg); # get the stored unique key to our services my $hg_name = $service_hg_obj_2x_key; my @service_keys = @{$obj_2x_services_hg->{$hg_name}}; #print "DEBUG: Looking for $hg_name ...\n"; my $obj_2x_hostgroup = obj_get_hostgroup_obj_by_hostgroup_name($cfg_obj_2x, $hg_name); if(!defined($obj_2x_hostgroup)) { # no hostgroup defined? } # we now need all host names for this hostgroup name, as an array my @service_hostgroup_hostnames = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $hg_name); if(@service_hostgroup_hostnames == 0) { # no members, so service cannot be linked. log a warning XXX #print " DEBUG: no members found, skipping $hg_name\n"; next; } # we've got: # * n services linked to hostgroups, # * a hostgroup # * an array of hosts as hostgroup members # we'll create: # * n service templates, # * a hg-host template referencing the service templates, # * host objects inheriting from it # my $svc_count = 0; # create a host template with hgname-group my $obj_2x_host_template; $obj_2x_host_template->{'__I2CONVERT_IS_TEMPLATE'} = 1; $obj_2x_host_template->{'__I2CONVERT_TEMPLATE_NAME'} = $obj_2x_hostgroup->{'hostgroup_name'}."-group"; # XXX hardcode it for now # loop through all services and attach them to the host template foreach my $service_obj_2x_key_val (@service_keys) { #print "DEBUG: Working on $service_obj_2x_key_val ...\n"; # get the service object by key my $obj_2x_service = @$cfg_obj_2x{'service'}->{$service_obj_2x_key_val}; # set the service as template. $obj_2x_service->{'__I2CONVERT_IS_TEMPLATE'} = 1; $obj_2x_service->{'__I2CONVERT_TEMPLATE_NAME'} = $obj_2x_service->{'service_description'}."-group-".$svc_count; # XXX hardcode it for now # create a dummy service inheriting the service template my $obj_2x_service_inherit; $obj_2x_service_inherit->{__I2CONVERT_USES_TEMPLATE} = 1; push @{$obj_2x_service_inherit->{'__I2CONVERT_TEMPLATE_NAMES'}}, $obj_2x_service->{'__I2CONVERT_TEMPLATE_NAME'}; $obj_2x_service_inherit->{'service_description'} = $obj_2x_service->{'service_description'}; $obj_2x_service_inherit->{'__I2CONVERT_SERVICEDESCRIPTION'} = $obj_2x_service->{'service_description'}; # link the service inherit to the host template $obj_2x_host_template->{'SERVICE'}->{$svc_count} = $obj_2x_service_inherit; $svc_count++; } # all host objects on the hostgroup members will get the host hg template name pushed into their array foreach my $hostgroup_member_host_name (@service_hostgroup_hostnames) { # get the host obj my $obj_2x_host = obj_get_host_obj_by_host_name($cfg_obj_2x, $hostgroup_member_host_name); # this is a reference in memory, not a copy! # push the template used # (override __I2CONVERT_USES_TEMPLATE too) $obj_2x_host->{__I2CONVERT_USES_TEMPLATE} = 1; push @{$obj_2x_host->{'__I2CONVERT_TEMPLATE_NAMES'}}, $obj_2x_host_template->{'__I2CONVERT_TEMPLATE_NAME'}; } # push back the newly created host template (incl the service inherit below SERVICE) to the objects 2.x hive #say Dumper($obj_2x_host_template); $obj_2x_hosts_cnt++; #print "adding new host at key " . $obj_2x_hosts_cnt . "\n"; $cfg_obj_2x->{'host'}->{$obj_2x_hosts_cnt} = $obj_2x_host_template; } ###################################### # NEW: NOTIFICATION MAPPING # old: contact->notification_commands->commands # contact->email/etc # host/service -> contact # new: notification->notification_command # user->mail/etc # host/service->notifications[type]->notification_templates,users ###################################### my $notification_obj_cnt = 0; my $obj_notification_cnt = 0; # add a dummy value so that we can check against it $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_NAME'} = '__I2CONVERT_NOTIFICATION_DUMMY'; # go through all users and build notifications based on the notification_command foreach my $user_obj_2x_key (keys %{@$cfg_obj_2x{'user'}}) { my $obj_2x_user = @$cfg_obj_2x{'user'}->{$user_obj_2x_key}; my $user_notification; #################################################### # get all notification_commands, and create new notification templates #################################################### my $notification_commands = $obj_2x_user->{'__I2CONVERT_NOTIFICATION_COMMANDS'}; #say Dumper($notification_commands); foreach my $notification_command_type (keys %{$notification_commands}) { foreach my $notification_command_name (keys %{$notification_commands->{$notification_command_type}}) { my $notification_command_line = $notification_commands->{$notification_command_type}->{$notification_command_name}; #print "type: $notification_command_type name: $notification_command_name line: $notification_command_line\n"; my $notification_command_name_2x = $notification_command_type."-".$notification_command_name; my $notification_name_2x = $notification_command_name_2x.$obj_notification_cnt; $obj_notification_cnt++; # save a relation to this user and which notification templates are now linked ( ["name"] = { templates = "template" } ) # we'll use that later on when processing hosts/services and linking to users and notifications $user_notification->{$notification_name_2x}->{'name'} = $notification_name_2x; push @{$user_notification->{$notification_name_2x}->{'templates'}}, $notification_command_name_2x; push @{$user_notification->{$notification_name_2x}->{'users'}}, $obj_2x_user->{'user_name'}; # save the type for later objects (host or service) $user_notification->{$notification_name_2x}->{'type'} = $notification_command_type; # XXX do not add duplicate notifications, they must remain unique by their notification_command origin! #say Dumper("checking existing $notification_command_name_2x"); #say Dumper($user_notification); if (obj_2x_notification_exists($cfg_obj_2x, $notification_command_name_2x) == 1) { #say Dumper("already existing $notification_command_name_2x"); next; } next if (!defined($notification_command_name_2x)); # create a new NotificationCommand 2x object with the original name $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_TYPE'} = 'Notification'; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = $notification_command_name; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_LINE'} = $notification_command_line; # use the ITL plugin notification command template if(defined($icinga2_cfg->{'itl'}->{'notificationcommand-template'}) && $icinga2_cfg->{'itl'}->{'notificationcommand-template'} ne "") { push @{$cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notificationcommand-template'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } # the check command name of 1.x is still the unique command object name, so we just keep it # in __I2CONVERT_NOTIFICATION_COMMAND # our global PK $command_obj_cnt++; # create a new notification template object $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TEMPLATE_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_COMMAND'} = $notification_command_name; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_IS_TEMPLATE'} = 1; # this is a template, used in hosts/services then # more reference $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'users'} = $user_notification->{$notification_name_2x}->{'users'}; # add dependency to ITL template to objects if(defined($icinga2_cfg->{'itl'}->{'notification-template'}) && $icinga2_cfg->{'itl'}->{'notification-template'} ne "") { @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}} = (); push @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notification-template'}; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; # we now use a template, otherwise it won't be dumped } $notification_obj_cnt++; } } $cfg_obj_2x->{'user'}->{$user_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'} = $user_notification; #say Dumper($cfg_obj_2x->{'user'}->{$user_obj_2x_key}); } # go through all hosts/services, and add notifications based on the users # XXX hosts - do we notify on hosts? foreach my $host_obj_2x_key (keys %{@$cfg_obj_2x{'host'}}) { my $obj_2x_host = @$cfg_obj_2x{'host'}->{$host_obj_2x_key}; # make sure there are none delete($cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'}); @{$cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'}} = (); # convert users and usergroupmembers into a unique list of users my @users = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_2x_host->{'contacts'}, ',', 1); my @usergroups = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_2x_host->{'contact_groups'}, ',', 1); # get all members of the usergroups foreach my $usergroup (@usergroups) { my @users_ug = obj_get_usernames_arr_by_usergroup_name($cfg_obj_2x, $usergroup); push @users, @users_ug; } # create a unique array of users (XXX important! XXX) my @uniq_users = Icinga2::Utils::uniq(@users); # now loop and fetch objects, and their needed notification values as array # (prepared above - look for $user_notification->{$notification_command_name_2x}...) foreach my $uniq_user (@uniq_users) { my $obj_2x_user = obj_get_user_obj_by_user_name($cfg_obj_2x, $uniq_user); push @{$cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'}}, $obj_2x_user->{'__I2CONVERT_NOTIFICATIONS'}; # we'll add a reference to all notifications here. decide on dump which object type is given, and dump only those notifications! } #say Dumper($obj_2x_service); } # XXX services foreach my $service_obj_2x_key (keys %{@$cfg_obj_2x{'service'}}) { my $obj_2x_service = @$cfg_obj_2x{'service'}->{$service_obj_2x_key}; # make sure there are none delete($cfg_obj_2x->{'service'}->{$service_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'}); @{$cfg_obj_2x->{'service'}->{$service_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'}} = (); # convert users and usergroupmembers into a unique list of users my @users = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_2x_service->{'contacts'}, ',', 1); my @usergroups = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_2x_service->{'contact_groups'}, ',', 1); # get all members of the usergroups foreach my $usergroup (@usergroups) { my @users_ug = obj_get_usernames_arr_by_usergroup_name($cfg_obj_2x, $usergroup); push @users, @users_ug; } # create a unique array of users (XXX important! XXX) my @uniq_users = Icinga2::Utils::uniq(@users); # now loop and fetch objects, and their needed notification values as array # (prepared above - look for $user_notification->{$notification_command_name_2x}...) foreach my $uniq_user (@uniq_users) { my $obj_2x_user = obj_get_user_obj_by_user_name($cfg_obj_2x, $uniq_user); push @{$cfg_obj_2x->{'service'}->{$service_obj_2x_key}->{'__I2CONVERT_NOTIFICATIONS'}}, $obj_2x_user->{'__I2CONVERT_NOTIFICATIONS'}; # we'll add a reference to all notifications here. decide on dump which object type is given, and dump only those notifications! } #say Dumper($obj_2x_service); } #exit(0); ###################################### # NEW: ESCALATION TO NOTIFICATION ###################################### my $obj_notification_escal_cnt = 0; if (!@$cfg_obj_1x{'serviceescalation'}) { goto SKIP_SVCESCAL; } foreach my $serviceescalation_obj_1x_key (keys %{@$cfg_obj_1x{'serviceescalation'}}) { my $obj_1x_serviceescalation = @$cfg_obj_1x{'serviceescalation'}->{$serviceescalation_obj_1x_key}; ###################################### # create a unique users list ###################################### # we need to get all notification_commands and create a notification escalation item from that source my $notification_prefix = 'serviceescalation'; # convert users and usergroupmembers into a unique list of users my @users = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_serviceescalation->{'contacts'}, ',', 1); my @usergroups = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_serviceescalation->{'contact_groups'}, ',', 1); # get all members of the usergroups foreach my $usergroup (@usergroups) { my @users_ug = obj_get_usernames_arr_by_usergroup_name($cfg_obj_2x, $usergroup); push @users, @users_ug; } # create a unique array of users (XXX important! XXX) my @uniq_users = Icinga2::Utils::uniq(@users); ###################################### # link users to this notification ###################################### foreach my $uniq_user (@uniq_users) { my $obj_2x_user = obj_get_user_obj_by_user_name($cfg_obj_2x, $uniq_user); #say Dumper($obj_2x_user); my $user_notification; my $notification_commands = $obj_2x_user->{'__I2CONVERT_NOTIFICATION_COMMANDS'}; #say Dumper($notification_commands); foreach my $notification_command_type (keys %{$notification_commands}) { foreach my $notification_command_name (keys %{$notification_commands->{$notification_command_type}}) { my $notification_command_line = $notification_commands->{$notification_command_type}->{$notification_command_name}; #print "type: $notification_command_type name: $notification_command_name line: $notification_command_line\n"; my $notification_command_name_2x = $notification_prefix."-".$notification_command_type."-".$notification_command_name; my $notification_name_2x = $notification_command_name_2x.$obj_notification_escal_cnt; $obj_notification_escal_cnt++; # save a relation to this user and which notification templates are now linked ( ["name"] = { templates = "template" } ) # we'll use that later on when processing hosts/services and linking to users and notifications $user_notification->{$notification_name_2x}->{'name'} = $notification_name_2x; push @{$user_notification->{$notification_name_2x}->{'templates'}}, $notification_command_name_2x; push @{$user_notification->{$notification_name_2x}->{'users'}}, $obj_2x_user->{'user_name'}; # save the type for later objects (host or service) $user_notification->{$notification_name_2x}->{'type'} = $notification_command_type; ###################################### # create a unique services list, and # link that to the notification # - host_name/service_description # - hostgroup_name/service_description # - servicegroup_name ###################################### my $notification_interval = 60; # assume some default if everything goes wrong ###################################### # get the obj by host_name/service_description ###################################### if (defined($obj_1x_serviceescalation->{'host_name'}) && defined($obj_1x_serviceescalation->{'service_description'})) { my $serviceescalation_service_obj = obj_get_service_obj_by_host_name_service_description($cfg_obj_2x, "__I2CONVERT_SERVICE_HOSTNAME", "__I2CONVERT_SERVICEDESCRIPTION", $obj_1x_serviceescalation->{'host_name'}, $obj_1x_serviceescalation->{'service_description'}); push @{$serviceescalation_service_obj->{'__I2CONVERT_NOTIFICATIONS'}}, $user_notification; #say Dumper($serviceescalation_service_obj); # we need to calculate begin/end based on service->notification_interval # if notification_interval is not defined, we need to look it up in the template tree! if (defined($serviceescalation_service_obj->{'notification_interval'})) { $notification_interval = $serviceescalation_service_obj->{'notification_interval'}; } else { $notification_interval = obj_1x_get_service_attr($cfg_obj_1x, $serviceescalation_service_obj, $serviceescalation_service_obj->{'__I2CONVERT_SERVICE_HOSTNAME'}, 'notification_interval'); } #say Dumper($notification_interval); $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}->{'begin'} = $obj_1x_serviceescalation->{'first_notification'} * $notification_interval; $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}->{'end'} = $obj_1x_serviceescalation->{'last_notification'} * $notification_interval; # save a reference to more infos $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TIMES'} = $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}; #say Dumper($obj_1x_serviceescalation); #say Dumper($user_notification); ###################################### # now ADD the new escalation notification ###################################### # XXX do not add duplicate notifications, they must remain unique by their notification_command origin! next if (obj_2x_notification_exists($cfg_obj_2x, $notification_command_name_2x) == 1); next if (!defined($notification_command_name_2x)); # create a new NotificationCommand 2x object with the original name $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_TYPE'} = 'Notification'; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = $notification_command_name; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_LINE'} = $notification_command_line; # use the ITL plugin notification command template if(defined($icinga2_cfg->{'itl'}->{'notificationcommand-template'}) && $icinga2_cfg->{'itl'}->{'notificationcommand-template'} ne "") { push @{$cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notificationcommand-template'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } # the check command name of 1.x is still the unique command object name, so we just keep it # in __I2CONVERT_NOTIFICATION_COMMAND # our global PK $command_obj_cnt++; # create a new notification template object $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TEMPLATE_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_COMMAND'} = $notification_command_name; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_IS_TEMPLATE'} = 1; # this is a template, used in hosts/services then # more reference $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'users'} = $user_notification->{$notification_name_2x}->{'users'}; # add dependency to ITL template to objects if(defined($icinga2_cfg->{'itl'}->{'notification-template'}) && $icinga2_cfg->{'itl'}->{'notification-template'} ne "") { @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}} = (); push @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notification-template'}; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; # we now use a template, otherwise it won't be dumped } #say Dumper($cfg_obj_2x->{'notification'}->{$notification_obj_cnt}); # our PK $notification_obj_cnt++; } ###################################### # get all hosts in hostgroup, with service_description ###################################### if (defined($obj_1x_serviceescalation->{'hostgroup_name'}) && defined($obj_1x_serviceescalation->{'service_description'})) { my @serviceescalation_hostgroup_names = Icinga2::Utils::str2arr_by_delim_without_excludes($obj_1x_serviceescalation->{'hostgroup_name'}, ',', 1); foreach my $serviceescalation_hostgroup_name (@serviceescalation_hostgroup_names) { # get hg members my @serviceescalation_hostgroup_hostnames = obj_get_hostnames_arr_by_hostgroup_name($cfg_obj_2x, $serviceescalation_hostgroup_name); foreach my $serviceescalation_hostgroup_hostname (@serviceescalation_hostgroup_hostnames) { if (defined($obj_1x_serviceescalation->{'service_description'})) { my $serviceescalation_service_obj = obj_get_service_obj_by_host_name_service_description($cfg_obj_2x, "__I2CONVERT_SERVICE_HOSTNAME", "__I2CONVERT_SERVICEDESCRIPTION", $obj_1x_serviceescalation->{'host_name'}, $obj_1x_serviceescalation->{'service_description'}); push @{$serviceescalation_service_obj->{'__I2CONVERT_NOTIFICATIONS'}}, $user_notification; # we need to calculate begin/end based on service->notification_interval # if notification_interval is not defined, we need to look it up in the template tree! if (defined($serviceescalation_service_obj->{'notification_interval'})) { $notification_interval = $serviceescalation_service_obj->{'notification_interval'}; } else { $notification_interval = obj_1x_get_service_attr($cfg_obj_1x, $serviceescalation_service_obj, $serviceescalation_service_obj->{'__I2CONVERT_SERVICE_HOSTNAME'}, 'notification_interval'); } $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}->{'begin'} = $obj_1x_serviceescalation->{'first_notification'} * $notification_interval; $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}->{'end'} = $obj_1x_serviceescalation->{'last_notification'} * $notification_interval; # save a reference to more infos $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TIMES'} = $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}; ###################################### # now ADD the new escalation notification ###################################### # XXX do not add duplicate notifications, they must remain unique by their notification_command origin! next if (obj_2x_notification_exists($cfg_obj_2x, $notification_command_name_2x) == 1); next if (!defined($notification_command_name_2x)); # create a new NotificationCommand 2x object with the original name $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_TYPE'} = 'Notification'; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = $notification_command_name; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_LINE'} = $notification_command_line; # use the ITL plugin notification command template if(defined($icinga2_cfg->{'itl'}->{'notificationcommand-template'}) && $icinga2_cfg->{'itl'}->{'notificationcommand-template'} ne "") { push @{$cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notificationcommand-template'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } # the check command name of 1.x is still the unique command object name, so we just keep it # in __I2CONVERT_NOTIFICATION_COMMAND # our global PK $command_obj_cnt++; # create a new notification template object $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TEMPLATE_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_COMMAND'} = $notification_command_name; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_IS_TEMPLATE'} = 1; # this is a template, used in hosts/services then # more references (this is why code duplication happens $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'users'} = $user_notification->{$notification_name_2x}->{'users'}; # add dependency to ITL template to objects if(defined($icinga2_cfg->{'itl'}->{'notification-template'}) && $icinga2_cfg->{'itl'}->{'notification-template'} ne "") { @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}} = (); push @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notification-template'}; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; # we now use a template, otherwise it won't be dumped } # our PK $notification_obj_cnt++; } } } } #say Dumper($cfg_obj_2x->{'notification'}->{$notification_obj_cnt}); ###################################### # get all hosts and services from servicegroup definition and link them ###################################### # XXX FIXME if (defined($obj_1x_serviceescalation->{'servicegroup_name'})) { say Dumper($obj_1x_serviceescalation); my @service_names = obj_2x_get_service_arr_by_servicegroup_name($cfg_obj_2x, $obj_1x_serviceescalation->{'servicegroup_name'}); foreach my $serviceescalation_service_name (@service_names) { my $serviceescalation_service_obj = obj_get_service_obj_by_host_name_service_description($cfg_obj_2x, "__I2CONVERT_SERVICE_HOSTNAME", "__I2CONVERT_SERVICEDESCRIPTION", $serviceescalation_service_name->{'__I2CONVERT_SERVICE_HOSTNAME'}, $serviceescalation_service_name->{'__I2CONVERT_SERVICEDESCRIPTION'}); #say Dumper($serviceescalation_service_obj); # skip any templates which would create duplicates next if ($serviceescalation_service_obj->{'__I2CONVERT_IS_TEMPLATE'} == 1); say Dumper($serviceescalation_service_name); push @{$serviceescalation_service_obj->{'__I2CONVERT_NOTIFICATIONS'}}, $user_notification; # we need to calculate begin/end based on service->notification_interval # if notification_interval is not defined, we need to look it up in the template tree! if (defined($serviceescalation_service_obj->{'notification_interval'})) { $notification_interval = $serviceescalation_service_obj->{'notification_interval'}; } else { $notification_interval = obj_1x_get_service_attr($cfg_obj_1x, $serviceescalation_service_obj, $serviceescalation_service_obj->{'__I2CONVERT_SERVICE_HOSTNAME'}, 'notification_interval'); } $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}->{'begin'} = $obj_1x_serviceescalation->{'first_notification'} * $notification_interval; $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}->{'end'} = $obj_1x_serviceescalation->{'last_notification'} * $notification_interval; # save a reference to more infos $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TIMES'} = $user_notification->{$notification_name_2x}->{'__I2CONVERT_NOTIFICATION_TIMES'}; ###################################### # now ADD the new escalation notification ###################################### # XXX do not add duplicate notifications, they must remain unique by their notification_command origin! next if (obj_2x_notification_exists($cfg_obj_2x, $notification_command_name_2x) == 1); next if (!defined($notification_command_name_2x)); # create a new NotificationCommand 2x object with the original name $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_TYPE'} = 'Notification'; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_NAME'} = $notification_command_name; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_COMMAND_LINE'} = $notification_command_line; # use the ITL plugin notification command template if(defined($icinga2_cfg->{'itl'}->{'notificationcommand-template'}) && $icinga2_cfg->{'itl'}->{'notificationcommand-template'} ne "") { push @{$cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notificationcommand-template'}; $cfg_obj_2x->{'command'}->{$command_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; } # the check command name of 1.x is still the unique command object name, so we just keep it # in __I2CONVERT_NOTIFICATION_COMMAND # our global PK $command_obj_cnt++; # create a new notification template object $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_TEMPLATE_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_NAME'} = $notification_command_name_2x; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_NOTIFICATION_COMMAND'} = $notification_command_name; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_IS_TEMPLATE'} = 1; # this is a template, used in hosts/services then # more references (this is why code duplication happens $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'users'} = $user_notification->{$notification_name_2x}->{'users'}; # add dependency to ITL template to objects if(defined($icinga2_cfg->{'itl'}->{'notification-template'}) && $icinga2_cfg->{'itl'}->{'notification-template'} ne "") { @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}} = (); push @{$cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $icinga2_cfg->{'itl'}->{'notification-template'}; $cfg_obj_2x->{'notification'}->{$notification_obj_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; # we now use a template, otherwise it won't be dumped } # our PK $notification_obj_cnt++; } } } } $obj_2x_user->{'__I2CONVERT_NOTIFICATIONS'} = $user_notification; } } SKIP_SVCESCAL: ###################################### # HOST->SERVICE MAGIC # we need to do it _after_ we've # manipulated all service objects! ###################################### # "get all 'host' hashref as array in hashmap, and their keys to access it" foreach my $host_obj_2x_key (keys %{@$cfg_obj_2x{'host'}}) { #say Dumper(@$cfg_obj_2x{'host'}->{$host_obj_2x_key}); my $obj_2x_host = @$cfg_obj_2x{'host'}->{$host_obj_2x_key}; #################################################### # Create Host->Service Relation for later dumping # we use the prep'ed 2x service hashref already # all attributes _must_have been resolved already! #################################################### my $obj_2x_host_service_cnt = 0; # find all services for this host foreach my $service_obj_2x_key (keys %{@$cfg_obj_2x{'service'}}) { my $obj_2x_service = @$cfg_obj_2x{'service'}->{$service_obj_2x_key}; ###################################### # get host_name/service_desc for obj # (prepared in service loop already) ###################################### my $obj_2x_service_host_name = $obj_2x_service->{'__I2CONVERT_SERVICE_HOSTNAME'}; my $obj_2x_service_service_description = $obj_2x_service->{'__I2CONVERT_SERVICEDESCRIPTION'}; ###################################### # skip service templates ###################################### if ($obj_2x_service->{'__I2CONVERT_IS_TEMPLATE'} == 1) { #Icinga2::Utils::debug("WARNING: Skipping service template '$obj_2x_service->{'__I2CONVERT_TEMPLATE_NAMES'}' for linking to host '$obj_2x_host->{'__I2CONVERT_HOSTNAME'}'."); next; } # save it for later # XXX if host_name can't be located in the service template tree, check if hostgroup is set somewhere # we then need to check if the service -> hostgroup <- hostmember applies (ugly) FIXME # XXX if host_name can't be determined, log an error XXX templates MUST be skipped before (they cannot look down, only up in use tree) if (!defined($obj_2x_service_host_name)) { #print "ERROR: No host_name for service given " . Dumper($obj_2x_service); next; } ###################################### # found a host->service relation? ###################################### if ($obj_2x_service_host_name eq $obj_2x_host->{'__I2CONVERT_HOSTNAME'}) { #debug("service_description: $obj_2x_service_service_description host_name: $obj_2x_service_host_name"); # 1. generate template name "host-service" my $service_template_name = $obj_2x_service_host_name."-".$obj_2x_service_service_description; # 2. make the service object a template with a special unique name $cfg_obj_2x->{'service'}->{$service_obj_2x_key}->{'__I2CONVERT_IS_TEMPLATE'} = 1; $cfg_obj_2x->{'service'}->{$service_obj_2x_key}->{'__I2CONVERT_TEMPLATE_NAME'} = $service_template_name; # 3. use the template name as reference for the host->service $cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'SERVICE'}->{$obj_2x_host_service_cnt}->{'__I2CONVERT_USES_TEMPLATE'} = 1; push @{$cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'SERVICE'}->{$obj_2x_host_service_cnt}->{'__I2CONVERT_TEMPLATE_NAMES'}}, $service_template_name; # 4. define the service description for the service $cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'SERVICE'}->{$obj_2x_host_service_cnt}->{'__I2CONVERT_SERVICEDESCRIPTION'} = $obj_2x_service_service_description; ###################################### # LINK HOST COMMAND WITH SERVICE CHECK ###################################### my $service_check_command_2x = Icinga2::Convert::convert_checkcommand(@$cfg_obj_1x{'command'}, $obj_2x_service, $user_macros_1x); # check if this service check is a possible match for __I2CONVERT_HOST_CHECK? if (defined($service_check_command_2x->{'check_command_name_1x'})) { if ($cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'__I2CONVERT_HOSTCHECK_NAME'} eq $service_check_command_2x->{'check_command_name_1x'}) { # set service as hostcheck $cfg_obj_2x->{'host'}->{$host_obj_2x_key}->{'__I2CONVERT_HOSTCHECK'} = $obj_2x_service_service_description; } } # primary key $obj_2x_host_service_cnt++; } else { # no match #say "ERROR: No Match with ". Dumper($obj_1x_host); } } } ############################################################################ ############################################################################ # export takes place outside again return $cfg_obj_2x; } 1; __END__ # vi: sw=4 ts=4 expandtab :