# MonArch - Groundwork Monitor Architect # MonarchAutoConfig.pm # ############################################################################ # Release 4.1 # February 2012 ############################################################################ # # Original author: Scott Parris # # Copyright 2007-2012 GroundWork Open Source, Inc. (GroundWork) # All rights reserved. This program is free software; you can redistribute # it and/or modify it under the terms of the GNU General Public License # version 2 as published by the Free Software Foundation. # # 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. # use strict; use MonarchStorProc; use MonarchFile; use MonarchInstrument; package AutoConfig; my $SERVER_SOFTWARE = $ENV{SERVER_SOFTWARE}; my $monarch_cgi; my $monarch_css; my $monarch_js; if (defined($SERVER_SOFTWARE) && $SERVER_SOFTWARE eq 'TOMCAT') { $monarch_cgi = '/monarch'; $monarch_css = '/monarch/css'; $monarch_js = '/monarch/js'; } elsif ( -e '/usr/local/groundwork/config/db.properties' ) { $monarch_cgi = '/monarch/cgi-bin'; $monarch_css = '/monarch'; $monarch_js = '/monarch'; } else { # Standalone Monarch (outside of GW Monitor) is no longer supported. } my $form_class = 'row1'; my $global_cell_pad = 3; my %config_settings = (); sub dbconnect() { return StorProc->dbconnect(); } sub dbdisconnect() { return StorProc->dbdisconnect(); } sub get_import_data() { my $data_source = $_[1]; my @file_data = (); my @errors = (); if ( !open( FILE, '<', $data_source ) ) { push @errors, "error: cannot open $data_source to read ($!)"; } else { while ( my $line = ) { $line =~ s/[\n\r]+/\n/; next if ( $line =~ /^\s*(?:#.*)?$/ ); push @file_data, $line; } close FILE; } if (@errors) { @file_data = @errors } return @file_data; } sub advanced_import(@) { my $schema_name = $_[1]; my $import_file = $_[2]; my $processed_file = $_[3]; my $monarch_home = $_[4]; my %schema = StorProc->fetch_schema($schema_name); if ($import_file) { $import_file = "$monarch_home/automation/data/$import_file"; } else { $import_file = $schema{'data_source'}; } my %import_data = (); my %processed = (); # Determine name and primary record position my %name_position = (); my %parent_host = (); my %new_parent = (); my @errors = (); foreach my $column ( keys %{ $schema{'column'} } ) { foreach my $match ( keys %{ $schema{'column'}{$column}{'match'} } ) { my $object = $schema{'column'}{$column}{'match'}{$match}{'object'}; if (defined $object) { if ( $object eq 'Primary record' ) { $name_position{'Primary record'} = $schema{'column'}{$column}{'position'}; } if ( $object eq 'Name' ) { $name_position{'Name'} = $schema{'column'}{$column}{'position'}; } } } } unless ( $name_position{'Primary record'} ) { $name_position{'Primary record'} = $name_position{'Name'}; } if ( -e "$monarch_home/automation/data/$processed_file" ) { if ( !open( FILE, '<', "$monarch_home/automation/data/$processed_file" ) ) { push @errors, "error: cannot open $monarch_home/automation/data/$processed_file to read ($!)"; } else { while ( my $line = ) { $line =~ s/[\r\n]+//; next if ( $line =~ /^\s*(?:#.*)?$/ ); $processed{$line} = 1; } close(FILE); } } if ( !open( FILE, '<', $import_file ) ) { push @errors, "error: cannot open $import_file to read ($!)"; } else { unless (@errors) { my %where = (); my %hosts_vitals = (); my %hosts_exist = StorProc->fetch_list_hash_array( 'hosts', \%where ); foreach my $host_id ( keys %hosts_exist ) { $hosts_vitals{ $hosts_exist{$host_id}[1] }{'id'} = $host_id; $hosts_vitals{ $hosts_exist{$host_id}[1] }{'address'}{ $hosts_exist{$host_id}[3] } = 1; $hosts_vitals{ $hosts_exist{$host_id}[1] }{'alias'} = $hosts_exist{$host_id}[2]; } my %service_name = (); my %service_name_hash = StorProc->fetch_list_hash_array( 'service_names', \%where ); foreach my $service_id ( keys %service_name_hash ) { $service_name{ $service_name_hash{$service_id}[1] }{'id'} = $service_id; $service_name{ $service_name_hash{$service_id}[1] }{'command_line'} = $service_name_hash{$service_id}[5]; } my %group_name = StorProc->get_table_objects('monarch_groups'); my %hostgroup_name = StorProc->get_table_objects('hostgroups'); my %contactgroup_name = StorProc->get_table_objects('contactgroups'); my %serviceprofile_name = StorProc->get_table_objects('profiles_service'); my %hostprofile_name = StorProc->get_table_objects('profiles_host'); my %add_objects = (); my %multihomed = (); my %discard_objects = (); my %resolve_parent = (); my $primary_rec = undef; my $delimiter = undef; my @chars = defined( $schema{'delimiter'} ) ? split( //, $schema{'delimiter'} ) : (); if (defined( $schema{'delimiter'} ) && $schema{'delimiter'} eq 'tab') { $delimiter = "\t"; } elsif (!defined( $schema{'delimiter'} ) || $schema{'delimiter'} eq '') { $delimiter = ' '; } else { foreach my $char (@chars) { unless ($char) { next } if ( $char =~ /\[|\]|\(|\)|\.|\*|\^|\$|\?|\||\\|\// ) { $delimiter .= "\\$char"; } else { $delimiter .= $char; } } } while ( my $line = ) { $line =~ s/[\r\n]+//; next if ( $line =~ /^\s*(?:#.*)?$/ ); my @line = split( /$delimiter/, $line ); my %cell_value = (); my $i = 1; foreach my $cell (@line) { $cell =~ s/^\s+|\s+$//g; $cell =~ s/^\"//; $cell =~ s/\"$//; $cell_value{$i} = $cell; $i++; } next if ( $cell_value{ $name_position{'Primary record'} } eq '0' ); $cell_value{ $name_position{'Primary record'} } = StorProc->get_normalized_hostname( $cell_value{ $name_position{'Primary record'} } ); if ( $cell_value{ $name_position{'Primary record'} } ) { $primary_rec = $cell_value{ $name_position{'Primary record'} }; } foreach my $column ( keys %{ $schema{'column'} } ) { my %match_order = (); foreach my $match ( keys %{ $schema{'column'}{$column}{'match'} } ) { $match_order{ $schema{'column'}{$column}{'match'}{$match}{'order'} } = $match; } my $cell_value = $cell_value{ $schema{'column'}{$column}{'position'} }; foreach my $order ( sort keys %match_order ) { my $value = undef; if ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'use-value-as-is' ) { $value = $cell_value; } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'exact' ) { if ( defined($cell_value) && $cell_value =~ /^($schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}(?:::.*)?)$/i ) { $value = $1; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'contains' ) { if ( defined($cell_value) && $cell_value =~ /$schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}/i ) { $value = $cell_value; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'begins-with' ) { if ( defined($cell_value) && $cell_value =~ /^$schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}/i ) { $value = $cell_value; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'ends-with' ) { if ( defined($cell_value) && $cell_value =~ /(.*?$schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}(?:::.*)?)$/i ) { $value = $1; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'service-definition' ) { if ( defined($cell_value) && $cell_value =~ /$schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}/) { my @vals = split( /$schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}/, $cell_value ); my $service = $vals[0]; $service =~ s/^service-//; unless ( $service_name{$service}{'id'} ) { if ( -e "/usr/local/groundwork/core/profiles/service-$service.xml" ) { my @import_results = import_profile( '', '/usr/local/groundwork/core/profiles', "service-$service.xml", '1' ); my %sn = StorProc->fetch_one( 'service_names', 'name', $service ); $service_name{$service}{'id'} = $sn{'servicename_id'}; $service_name{$service}{'command_line'} = $sn{'command_line'}; } } if ( $service_name{$service}{'id'} ) { if ( $vals[1] ) { $vals[1] =~ s/^!//; my @command = split( /!/, $service_name{$service}{'command_line'} ); if ( $command[1] ) { $import_data{$primary_rec}{'Service'}{$service}{'command_line'} = "$command[0]!$vals[1]"; } else { $import_data{$primary_rec}{'Service'}{$service}{'command_line'} = "$service_name{$service}{'command_line'}!$vals[1]"; } } else { $import_data{$primary_rec}{'Service'}{$service}{'command_line'} = $service_name{$service}{'command_line'}; } $import_data{$primary_rec}{'Service'}{$service}{'instances'}{ $vals[2] }{'arguments'} = $vals[3]; } } else { my $service = $cell_value; if (defined $service) { $service =~ s/^service-//; unless ( $service_name{$service}{'id'} ) { if ( -e "/usr/local/groundwork/core/profiles/service-$service.xml" ) { my @import_results = import_profile( '', '/usr/local/groundwork/core/profiles', "service-$service.xml", '1' ); my %sn = StorProc->fetch_one( 'service_names', 'name', $service ); $service_name{$service}{'id'} = $sn{'servicename_id'}; $service_name{$service}{'command_line'} = $sn{'command_line'}; } } if ( $service_name{$service}{'id'} ) { $import_data{$primary_rec}{'Service'}{$service}{'command_line'} = $service_name{$service}{'command_line'}; } } } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'is-null' ) { unless ( $cell_value ) { $value = 'is-null'; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'match_type'} eq 'use-perl-reg-exp' ) { if ( $cell_value =~ /$schema{'column'}{$column}{'match'}{$match_order{$order}}{'match_string'}/i ) { ## the parens, if any, for populating $1 come from a user-supplied expression, ## which is why they don't appear explicitly in the line above. $value = $1; unless ($value) { $value = $cell_value; } } } if ($value) { my %obj_name = ( 'Host group' => 'hostgroups', 'Contact group' => 'contactgroups', 'Group' => 'groups', 'Parent' => 'parents', 'Service profile' => 'serviceprofiles' ); my $object = $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'object'}; if ( defined($object) && $object eq 'Address' ) { $multihomed{$primary_rec}{$value} = 1; } if ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Discard record' ) { $import_data{$primary_rec}{'discard'} = 1; last; } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Skip column record' ) { last; } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Resolve to parent' ) { $resolve_parent{$primary_rec}{$value} = 1; } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Discard if match existing host' ) { if ( $hosts_vitals{$value} ) { $import_data{$primary_rec}{'discard'} = 1; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign host profile' ) { $import_data{$primary_rec}{'Host profile'} = $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'hostprofile'}; } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign host profile if undefined' ) { unless ( $import_data{$primary_rec}{'Host profile'} ) { $import_data{$primary_rec}{'Host profile'} = $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'hostprofile'}; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign object(s)' ) { foreach my $obj_name ( @{ $schema{'column'}{$column}{'match'}{ $match_order{$order} }{ $obj_name{ $object } } } ) { if ( $object eq 'Group' ) { $import_data{$primary_rec}{'Group'}{$obj_name} = $group_name{$obj_name}; } elsif ( $object eq 'Contact group' ) { $import_data{$primary_rec}{'Contact group'}{$obj_name} = $contactgroup_name{$obj_name}; } elsif ( $object eq 'Host group' ) { $import_data{$primary_rec}{'Host group'}{$obj_name} = $hostgroup_name{$obj_name}; } elsif ( $object eq 'Parent' ) { $import_data{$primary_rec}{'Parent'}{$obj_name} = 1; $parent_host{$obj_name} = 1; } elsif ( $object eq 'Service profile' ) { $import_data{$primary_rec}{'Service profile'}{$obj_name} = $serviceprofile_name{$obj_name}; } } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign value to' ) { $import_data{$primary_rec}{$object} = $value; } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign value if undefined' ) { unless ( $import_data{$primary_rec}{$object} ) { $import_data{$primary_rec}{$object} = $value; } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign object if exists' ) { if ( $object eq 'Group' ) { if ( $group_name{$value} ) { $import_data{$primary_rec}{'Group'}{$value} = $group_name{$value}; } } elsif ( $object eq 'Contact group' ) { if ( $contactgroup_name{$value} ) { $import_data{$primary_rec}{'Contact group'}{$value} = $contactgroup_name{$value}; } } elsif ( $object eq 'Host group' ) { if ( $hostgroup_name{$value} ) { $import_data{$primary_rec}{'Host group'}{$value} = $hostgroup_name{$value}; } } elsif ( $object eq 'Parent' ) { $import_data{$primary_rec}{'Parent'}{$value} = 1; $parent_host{$value} = 1; } elsif ( $object eq 'Host profile' ) { unless ( $hostprofile_name{$value} ) { $value =~ s/host-profile-//; unless ( $hostprofile_name{$value} ) { if ( -e "/usr/local/groundwork/core/profiles/host-profile-$value.xml" ) { my @import_results = import_profile( '', '/usr/local/groundwork/core/profiles', "host-profile-$value.xml", '' ); my %hp = StorProc->fetch_one( 'profiles_host', 'name', $value ); $import_data{$primary_rec}{'Host profile'}{$value} = $hp{'hostprofile_id'}; $serviceprofile_name{$value} = $hp{'hostprofile_id'}; } } } if ( $hostprofile_name{$value} ) { $import_data{$primary_rec}{'Host profile'} = $value; } } elsif ( $object eq 'Service profile' ) { unless ( $serviceprofile_name{$value} ) { $value =~ s/service-profile-//; unless ( $serviceprofile_name{$value} ) { if ( -e "/usr/local/groundwork/core/profiles/service-profile-$value.xml" ) { my @import_results = import_profile( '', '/usr/local/groundwork/core/profiles', "service-profile-$value.xml", '' ); my %sp = StorProc->fetch_one( 'profiles_service', 'name', $value ); $import_data{$primary_rec}{'Service profile'}{$value} = $sp{'serviceprofile_id'}; $serviceprofile_name{$value} = $sp{'serviceprofile_id'}; } } } if ( $serviceprofile_name{$value} ) { $import_data{$primary_rec}{'Service profile'}{$value} = $serviceprofile_name{$value}; } } elsif ( $object eq 'Service' ) { unless ( $service_name{$value} ) { $value =~ s/service-//; unless ( $service_name{$value}{'id'} ) { if ( -e "/usr/local/groundwork/core/profiles/service-$value.xml" ) { my @import_results = import_profile( '', '/usr/local/groundwork/core/profiles', "service-$value.xml", '1' ); my %sn = StorProc->fetch_one( 'service_names', 'name', $value ); $service_name{$value}{'id'} = $sn{'servicename_id'}; } } } if ( $service_name{$value}{'id'} ) { $import_data{$primary_rec}{'Service'}{$value}{'name'} = $value; } } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Add if not exists and assign object' ) { if ( $object eq 'Group' ) { ## check for MySQL case insensitivity foreach my $group ( keys %group_name ) { if ( $value =~ /^$group$/i ) { $value = $group; } } if ( $group_name{$value} ) { $import_data{$primary_rec}{'Group'}{$value} = $group_name{$value}; } else { $add_objects{'groups'}{$value}{$primary_rec} = 1; $discard_objects{$primary_rec}{'groups'}{$value} = 1; } } elsif ( $object eq 'Contact group' ) { ## check for MySQL case insensitivity foreach my $contactgroup ( keys %contactgroup_name ) { if ( $value =~ /^$contactgroup$/i ) { $value = $contactgroup; } } if ( $contactgroup_name{$value} ) { $import_data{$primary_rec}{'Contact group'}{$value} = $contactgroup_name{$value}; } else { $add_objects{'contactgroups'}{$value}{$primary_rec} = 1; $discard_objects{$primary_rec}{'contactgroups'}{$value} = 1; } } elsif ( $object eq 'Host group' ) { ## check for MySQL case insensitivity foreach my $hostgroup ( keys %hostgroup_name ) { if ( $value =~ /^$hostgroup$/i ) { $value = $hostgroup; } } if ( $hostgroup_name{$value} ) { $import_data{$primary_rec}{'Host group'}{$value} = $hostgroup_name{$value}; } else { $add_objects{'hostgroups'}{$value}{$primary_rec} = 1; $discard_objects{$primary_rec}{'hostgroups'}{$value} = 1; } } } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Convert dword and assign to' ) { use Socket; $import_data{$primary_rec}{$object} = inet_ntoa( pack 'N', $value ); } elsif ( $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'rule'} eq 'Assign service' ) { my $service = $schema{'column'}{$column}{'match'}{ $match_order{$order} }{'service_name'}; $import_data{$primary_rec}{'Service'}{$service}{'service_id'} = $service_name{$service}{'id'}; } } } } } close(FILE); ############################################################################ # determine new hosts and exceptions and apply smart names and apply default profile # foreach my $primary_rec ( sort keys %import_data ) { if ( $import_data{$primary_rec}{'Name'} && $import_data{$primary_rec}{'Address'} && $import_data{$primary_rec}{'Alias'} ) { if ( $hosts_vitals{ $import_data{$primary_rec}{'Name'} }{'id'} ) { $import_data{$primary_rec}{'exists'} = 1; $import_data{$primary_rec}{'host_id'} = $hosts_vitals{ $import_data{$primary_rec}{'Name'} }{'id'}; } else { if ( $parent_host{ $import_data{$primary_rec}{'Name'} } ) { $import_data{$primary_rec}{'new_parent'} = 1; } $import_data{$primary_rec}{'new'} = 1; } } elsif ( $schema{'smart_name'} ) { unless ( $import_data{$primary_rec}{'Name'} && $import_data{$primary_rec}{'Address'} && $import_data{$primary_rec}{'Alias'} ) { unless ( $import_data{$primary_rec}{'Name'} ) { if ( $import_data{$primary_rec}{'Alias'} ) { $import_data{$primary_rec}{'Name'} = $import_data{$primary_rec}{'Alias'}; } elsif ( $import_data{$primary_rec}{'Address'} ) { $import_data{$primary_rec}{'Name'} = $import_data{$primary_rec}{'Address'}; } else { $import_data{$primary_rec}{'exception'} = 1; } } unless ( $import_data{$primary_rec}{'Address'} ) { if ( $import_data{$primary_rec}{'Alias'} ) { $import_data{$primary_rec}{'Address'} = $import_data{$primary_rec}{'Alias'}; } elsif ( $import_data{$primary_rec}{'Name'} ) { $import_data{$primary_rec}{'Address'} = $import_data{$primary_rec}{'Name'}; } else { $import_data{$primary_rec}{'exception'} = 1; } } unless ( $import_data{$primary_rec}{'Alias'} ) { if ( $import_data{$primary_rec}{'Address'} ) { $import_data{$primary_rec}{'Alias'} = $import_data{$primary_rec}{'Address'}; } elsif ( $import_data{$primary_rec}{'Name'} ) { $import_data{$primary_rec}{'Alias'} = $import_data{$primary_rec}{'Name'}; } else { $import_data{$primary_rec}{'exception'} = 1; } } if ( $hosts_vitals{ $import_data{$primary_rec}{'Name'} }{'id'} ) { $import_data{$primary_rec}{'exists'} = 1; $import_data{$primary_rec}{'host_id'} = $hosts_vitals{ $import_data{$primary_rec}{'Name'} }{'id'}; } else { if ( $parent_host{ $import_data{$primary_rec}{'Name'} } ) { $import_data{$primary_rec}{'new_parent'} = 1; } $import_data{$primary_rec}{'new'} = 1; } } } else { $import_data{$primary_rec}{'exception'} = 1; } if ( $schema{'default_profile'} ) { unless ( $import_data{$primary_rec}{'Host profile'} ) { $import_data{$primary_rec}{'Host profile'} = $schema{'default_profile'}; } } unless ( $import_data{$primary_rec}{'Host profile'} ) { $import_data{$primary_rec}{'exception'} = 1; } } ############################################################################ # Apply the Resolve to parent rule # if ( keys %resolve_parent ) { my %new_parent = (); foreach my $primary_rec ( sort keys %import_data ) { $hosts_vitals{ $import_data{$primary_rec}{'Name'} }{'alias'} = $import_data{$primary_rec}{'Alias'}; foreach my $address ( keys %{ $multihomed{$primary_rec} } ) { $hosts_vitals{ $import_data{$primary_rec}{'Name'} }{'address'}{$address} = 1; } } foreach my $primary_rec ( keys %resolve_parent ) { foreach my $parent ( keys %{ $resolve_parent{$primary_rec} } ) { foreach my $host ( keys %hosts_vitals ) { if ( $host eq $parent ) { $import_data{$primary_rec}{'Parent'}{$host} = 1; unless ( $hosts_vitals{$host}{'id'} ) { $new_parent{$host} = 1; } } elsif ( $hosts_vitals{$host}{'alias'} eq $parent ) { $import_data{$primary_rec}{'Parent'}{$host} = 1; unless ( $hosts_vitals{$host}{'id'} ) { $new_parent{$host} = 1; } } else { foreach my $address ( keys %{ $hosts_vitals{$host}{'address'} } ) { if ( $address eq $parent ) { $import_data{$primary_rec}{'Parent'}{$host} = 1; unless ( $hosts_vitals{$host}{'id'} ) { $new_parent{$host} = 1; } } } } } } } foreach my $primary_rec ( keys %import_data ) { if ( $new_parent{ $import_data{$primary_rec}{'Name'} } ) { $import_data{$primary_rec}{'new_parent'} = 1; } } } ############################################################################ # determine what to keep and what to throw away # foreach my $primary_rec ( keys %import_data ) { if ( $processed{$primary_rec} ) { delete $import_data{$primary_rec}; next; } if ( $import_data{$primary_rec}{'discard'} ) { delete $import_data{$primary_rec}; delete $resolve_parent{$primary_rec}; foreach my $group ( keys %{ $discard_objects{$primary_rec}{'groups'} } ) { delete $add_objects{'groups'}{$group}; } foreach my $group ( keys %{ $discard_objects{$primary_rec}{'contactgroups'} } ) { delete $add_objects{'contactgroups'}{$group}; } foreach my $group ( keys %{ $discard_objects{$primary_rec}{'hostgroups'} } ) { delete $add_objects{'hostgroups'}{$group}; } next; } } ############################################################################ # add new supporting objects # foreach my $group ( keys %{ $add_objects{'groups'} } ) { my @vals = ( \undef, $group, $group, '', '', "\n\n" ); my $id = StorProc->insert_obj_id( 'monarch_groups', \@vals, 'group_id' ); $group_name{$group} = $id; foreach my $record ( keys %{ $add_objects{'groups'}{$group} } ) { $import_data{$record}{'Group'}{$group} = $id; } } foreach my $group ( keys %{ $add_objects{'contactgroups'} } ) { my @vals = ( \undef, $group, $group, '' ); my $id = StorProc->insert_obj_id( 'contactgroups', \@vals, 'contactgroup_id' ); $contactgroup_name{$group} = $id; foreach my $record ( keys %{ $add_objects{'contactgroups'}{$group} } ) { $import_data{$record}{'Contact group'}{$group} = $id; } } foreach my $group ( keys %{ $add_objects{'hostgroups'} } ) { my @vals = ( \undef, $group, $group, '', '', '', '1', '', '' ); my $id = StorProc->insert_obj_id( 'hostgroups', \@vals, 'hostgroup_id' ); foreach my $record ( keys %{ $add_objects{'hostgroups'}{$group} } ) { $import_data{$record}{'Host group'}{$group} = $id; } } ############################################################################ # only for host-profile-sync # if ( $schema{'type'} eq 'host-profile-sync' ) { my %import_host = (); foreach my $primary_rec ( keys %import_data ) { $import_host{ $import_data{$primary_rec}{'Name'} } = 1; } %import_data = StorProc->profile_sync( \%import_data, \%import_host, \%schema ); } } } return \%import_data, \%schema, \@errors; } sub process_import_data(@) { my %import_data = %{ $_[1] }; my %results = (); my %externals = StorProc->get_externals(); my %host_name = StorProc->get_table_objects('hosts'); my %hostprofile_name = StorProc->get_table_objects('profiles_host'); my %serviceprofile_name = StorProc->get_table_objects('profiles_service'); my %service_name = (); my %where = (); my %service_name_id = (); my %service_name_hash = StorProc->fetch_list_hash_array( 'service_names', \%where ); foreach my $service_id ( keys %service_name_hash ) { $service_name_id{$service_id} = $service_name_hash{$service_id}[1]; $service_name{ $service_name_hash{$service_id}[1] }{'id'} = $service_id; $service_name{ $service_name_hash{$service_id}[1] }{'template'} = $service_name_hash{$service_id}[3]; $service_name{ $service_name_hash{$service_id}[1] }{'check_command'} = $service_name_hash{$service_id}[4]; $service_name{ $service_name_hash{$service_id}[1] }{'command_line'} = $service_name_hash{$service_id}[5]; $service_name{ $service_name_hash{$service_id}[1] }{'escalation'} = $service_name_hash{$service_id}[6]; $service_name{ $service_name_hash{$service_id}[1] }{'extinfo'} = $service_name_hash{$service_id}[7]; } my %service_name_overrides = (); my %service_name_override_hash = StorProc->fetch_list_hash_array( 'servicename_overrides', \%where ); foreach my $service_id ( keys %service_name_hash ) { $service_name_overrides{ $service_name_id{$service_id} }{'id'} = $service_id; $service_name_overrides{ $service_name_id{$service_id} }{'check_period'} = $service_name_override_hash{$service_id}[1]; $service_name_overrides{ $service_name_id{$service_id} }{'notification_period'} = $service_name_override_hash{$service_id}[2]; $service_name_overrides{ $service_name_id{$service_id} }{'event_handler'} = $service_name_override_hash{$service_id}[3]; if ( $service_name_override_hash{$service_id}[4] ) { $service_name_overrides{ $service_name_id{$service_id} }{'data'} = $service_name_override_hash{$service_id}[4]; } else { $service_name_overrides{ $service_name_id{$service_id} }{'data'} = qq( ); } } my %service_name_dependency = (); my %service_name_dependency_hash = StorProc->fetch_list_hash_array( 'servicename_dependency', \%where ); foreach my $id ( keys %service_name_dependency_hash ) { $service_name_dependency{ $service_name_id{ $service_name_dependency_hash{$id}[1] } }{$id}{'host'} = $service_name_dependency_hash{$id}[2]; $service_name_dependency{ $service_name_id{ $service_name_dependency_hash{$id}[1] } }{$id}{'template'} = $service_name_dependency_hash{$id}[3]; } %externals = (); my %externals_hash = StorProc->fetch_list_hash_array( 'externals', \%where ); foreach my $id ( keys %externals_hash ) { $externals{ $externals_hash{$id}[3] }{$id} = $externals_hash{$id}[4]; } my %service_name_externals = (); my %service_name_externals_hash_array = StorProc->fetch_hash_array_generic_key( 'external_service_names', \%where ); foreach my $key ( keys %service_name_externals_hash_array ) { my $external_id = $service_name_externals_hash_array{$key}[0]; my $servicename_id = $service_name_externals_hash_array{$key}[1]; $service_name_externals{$servicename_id}{$external_id} = $external_id; } my %host_profile_externals = (); my %host_profile_externals_hash_array = StorProc->fetch_hash_array_generic_key( 'external_host_profile', \%where ); foreach my $key ( keys %host_profile_externals_hash_array ) { my $external_id = $host_profile_externals_hash_array{$key}[0]; my $hostprofile_id = $host_profile_externals_hash_array{$key}[1]; $host_profile_externals{$hostprofile_id}{$external_id} = $external_id; } # Arrange process order so that new parent hosts are added first. # FIX MINOR: If a parent is ignored due to a circular parentage loop, then all its children should be ignored as well, # so we don't import any of the children without their intended parentage properly defined. # FIX MINOR: Deal appropriately with importing a child with a declared parent which is neither in the set to be imported, # nor already existing within Monarch, so we don't import the child without its intended parentage properly defined. # FIX MINOR: If circular parentage loops are discovered, offer the user a choice of whether to cancel the entire import, # to import only the hosts not related to those with such parentage problems, or to also import the affected hosts while # ignoring their respective claimed parentage. my @non_parents = (); my %sorted = (); my %new_parents = (); my %children = (); my @records = (); foreach my $rec ( keys %import_data ) { if ( $import_data{$rec}{'new_parent'} ) { $new_parents{ $import_data{$rec}{'Name'} } = $rec; delete $import_data{$rec}{'Parent'}{ $import_data{$rec}{'Name'} }; if ( keys %{ $import_data{$rec}{'Parent'} } ) { $children{ $import_data{$rec}{'Name'} } = $rec; } else { push @records, $rec; $sorted{ $import_data{$rec}{'Name'} } = $rec; } } else { push @non_parents, $rec; } } my %recursive_parents = (); for (my $loop = scalar keys %children; $loop >= 0; --$loop) { last unless %children; foreach my $child ( keys %children ) { my $have_unsorted_parent = 0; foreach my $parent ( keys %{ $import_data{ $children{$child} }{'Parent'} } ) { $recursive_parents{$parent}{$child} = 1; if ( $recursive_parents{$child}{$parent} ) { $results{'errors'}{ $child } .= "Host \"$child\" is part of a circular parentage loop (parent is \"$parent\") and is being ignored."; delete $children{$child}; } # FIX MINOR: What value does the $new_parents{$parent} test add here? elsif ( $new_parents{$parent} and not $sorted{$parent} ) { $have_unsorted_parent = 1; } } unless ($have_unsorted_parent or not exists $children{$child}) { push @records, $children{$child}; $sorted{$child} = 1; delete $children{$child}; } } } foreach my $child ( keys %children ) { # This covers all loops with more than two members. $results{'errors'}{ $child } .= "Host \"$child\" is part of a circular parentage loop and is being ignored."; } ## FIX THIS: The handling of $results{'errors'}{$rec} throughout this loop is poor. ## We should probably make it an array, push new messages rather than concatenate them, ## and adjust all callers accordingly. Or always bypass further processing upon first error. push( @records, @non_parents ); foreach my $rec (@records) { if ( $import_data{$rec}{'delete'} ) { my %where = ( 'host_id' => $import_data{$rec}{'host_id'} ); my $result = StorProc->delete_one_where( 'contactgroup_host', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= $result } $result = StorProc->delete_all( 'hosts', 'host_id', $import_data{$rec}{'host_id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= $result } $results{'deleted'}{ $import_data{$rec}{'Name'} } = 'Host removed.'; } elsif ( $import_data{$rec}{'exception'} ) { $results{'exception'}{$rec} = "Record $rec skipped for lack of information."; } else { my %profile = (); my $id = undef; my %exists = (); my %values = ( 'alias' => $import_data{$rec}{'Alias'}, 'address' => $import_data{$rec}{'Address'} ); if ( $import_data{$rec}{'Host profile'} ) { @{ $profile{'service_profiles'} } = (); @{ $profile{'externals'} } = (); @{ $profile{'parents'} } = (); unless ( $hostprofile_name{ $import_data{$rec}{'Host profile'} } ) { $import_data{$rec}{'Host profile'} =~ s/host-profile-//; unless ( $hostprofile_name{ $import_data{$rec}{'Host profile'} } ) { my @import_results = import_profile( '', '/usr/local/groundwork/core/profiles', "host-profile-$import_data{$rec}{'Host profile'}.xml", '' ); ## FIX THIS: do something with those results? } } if ( $hostprofile_name{ $import_data{$rec}{'Host profile'} } ) { %profile = StorProc->fetch_one( 'profiles_host', 'name', $import_data{$rec}{'Host profile'} ); my %where = ( 'hostprofile_id' => $profile{'hostprofile_id'} ); @{ $profile{'service_profiles'} } = StorProc->fetch_list_where( 'profile_host_profile_service', 'serviceprofile_id', \%where ); @{ $profile{'parents'} } = StorProc->fetch_list_where( 'profile_parent', 'host_id', \%where ); @{ $profile{'hostgroups'} } = StorProc->fetch_list_where( 'profile_hostgroup', 'hostgroup_id', \%where ); %where = ( 'hostprofile_id' => $profile{'hostprofile_id'} ); @{ $profile{'contactgroups'} } = StorProc->fetch_list_where( 'contactgroup_host_profile', 'contactgroup_id', \%where ); $values{'hostprofile_id'} = $profile{'hostprofile_id'}; $values{'host_escalation_id'} = $profile{'host_escalation_id'}; $values{'service_escalation_id'} = $profile{'service_escalation_id'}; $values{'hosttemplate_id'} = $profile{'host_template_id'}; $values{'hostextinfo_id'} = $profile{'host_extinfo_id'}; } } if ( $import_data{$rec}{'exists'} ) { $id = $import_data{$rec}{'host_id'}; my $result = StorProc->update_obj( 'hosts', 'host_id', $id, \%values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= $result; } $result = StorProc->delete_all( 'serviceprofile_host', 'host_id', $id ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } $result = StorProc->delete_all( 'host_parent', 'host_id', $id ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } $result = StorProc->delete_all( 'external_host', 'host_id', $id ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } $result = StorProc->delete_all( 'hostgroup_host', 'host_id', $id ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } $result = StorProc->delete_all( 'monarch_group_host', 'host_id', $id ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } my %w = ( 'host_id' => $id ); $result = StorProc->delete_one_where( 'contactgroup_host', \%w ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } unless ( $results{'errors'}{$rec} ) { $results{'updated'}{$rec} = 'host updated'; } } else { my @values = ( \undef, $import_data{$rec}{'Name'}, $values{'alias'}, $values{'address'}, '', $values{'hosttemplate_id'}, $values{'hostextinfo_id'}, $values{'hostprofile_id'}, $values{'host_escalation_id'}, $values{'service_escalation_id'}, '1', '', '' ); if ( $host_name{ $import_data{$rec}{'Name'} } ) { $results{'errors'}{$rec} .= "Duplicate, a host with name \"$import_data{$rec}{'Name'}\" already added."; } elsif ($values{'alias'} eq '') { $results{'errors'}{$rec} .= "No alias is specified for host \"$import_data{$rec}{'Name'}\"."; } elsif ($values{'address'} eq '') { $results{'errors'}{$rec} .= "No address is specified for host \"$import_data{$rec}{'Name'}\"."; } else { $id = StorProc->insert_obj_id( 'hosts', \@values, 'host_id' ); if ( $id =~ /error/i ) { $results{'errors'}{$rec} .= "Failed to add host \"$import_data{$rec}{'Name'}\": $id"; } else { $host_name{ $import_data{$rec}{'Name'} } = $id; $results{'added'}{$rec} = "Host \"$import_data{$rec}{'Name'}\" added."; } } } unless ( $results{'errors'}{$rec} ) { if ( $profile{'hostprofile_id'} ) { my @hosts = ($id); my @errors = StorProc->host_profile_apply( $profile{'hostprofile_id'}, \@hosts ); $results{'errors'}{$rec} .= join('
', @errors) if @errors; my ( $cnt, $err ) = StorProc->service_profile_apply( \@{ $profile{'service_profiles'} }, 'replace', \@hosts ); $results{'errors'}{$rec} .= join('
', @$err) if @$err; foreach my $spid ( @{ $profile{'service_profiles'} } ) { $exists{'service_profiles'}{$spid} = 1; my @vals = ( $spid, $id ); my $result = StorProc->insert_obj( 'serviceprofile_host', \@vals ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } foreach my $pid ( @{ $profile{'parents'} } ) { $exists{'parents'}{$pid} = 1; unless ( $id eq $pid ) { my @vals = ( $id, $pid ); my $result = StorProc->insert_obj( 'host_parent', \@vals ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } } foreach my $ext ( keys %{ $host_profile_externals{ $profile{'hostprofile_id'} } } ) { my @vals = ( $ext, $id, $externals{'host'}{$ext}, \'0+0' ); my $result = StorProc->insert_obj( 'external_host', \@vals ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } foreach my $hgid ( @{ $profile{'hostgroups'} } ) { $exists{'hostgroups'}{$hgid} = 1; my @vals = ( $hgid, $id ); my $result = StorProc->insert_obj( 'hostgroup_host', \@vals ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } foreach my $cgid ( @{ $profile{'contactgroups'} } ) { $exists{'contactgroups'}{$cgid} = 1; my @vals = ( $cgid, $id ); my $result = StorProc->insert_obj( 'contactgroup_host', \@vals ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } } foreach my $value ( keys %{ $import_data{$rec}{'Parent'} } ) { if ( $host_name{$value} ) { unless ( $exists{'parents'}{ $host_name{$value} } ) { unless ( $id eq $host_name{$value} ) { my @values = ( $id, $host_name{$value} ); my $result = StorProc->insert_obj( 'host_parent', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= $result; } } } } } foreach my $value ( keys %{ $import_data{$rec}{'Contact group'} } ) { if ( $import_data{$rec}{'Contact group'}{$value} ) { unless ( $exists{'contactgroups'}{ $import_data{$rec}{'Contact group'}{$value} } ) { my @values = ( $import_data{$rec}{'Contact group'}{$value}, $id ); my $result = StorProc->insert_obj( 'contactgroup_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= "contact group \"$value\"=\"$import_data{$rec}{'Contact group'}{$value}\": $result"; } } } } foreach my $value ( keys %{ $import_data{$rec}{'Host group'} } ) { if ( $import_data{$rec}{'Host group'}{$value} ) { unless ( $exists{'hostgroups'}{ $import_data{$rec}{'Host group'}{$value} } ) { my @values = ( $import_data{$rec}{'Host group'}{$value}, $id ); my $result = StorProc->insert_obj( 'hostgroup_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= "host group \"$value\"=\"$import_data{$rec}{'Host group'}{$value}\": $result"; } } } } foreach my $value ( keys %{ $import_data{$rec}{'Group'} } ) { if ( $import_data{$rec}{'Group'}{$value} ) { unless ( $exists{'groups'}{ $import_data{$rec}{'Group'}{$value} } ) { my @values = ( $import_data{$rec}{'Group'}{$value}, $id ); my $result = StorProc->insert_obj( 'monarch_group_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= "group \"$value\"=\"$import_data{$rec}{'Group'}{$value}\": $result"; } } } } my @profiles = (); foreach my $value ( keys %{ $import_data{$rec}{'Service profile'} } ) { if ( $import_data{$rec}{'Service profile'}{$value} ) { unless ( $exists{'service_profiles'}{ $import_data{$rec}{'Service profile'}{$value} } ) { my @values = ( $import_data{$rec}{'Service profile'}{$value}, $id ); my $result = StorProc->insert_obj( 'serviceprofile_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} .= "service profile \"$value\"=\"$import_data{$rec}{'Service profile'}{$value}\": $result"; } push @profiles, $import_data{$rec}{'Service profile'}{$value}; } } } if (@profiles) { my @hosts = ($id); my ( $cnt, $err ) = StorProc->service_profile_apply( \@profiles, 'merge', \@hosts ); $results{'errors'}{$rec} .= join('
', @$err) if @$err; } foreach my $service ( keys %{ $import_data{$rec}{'Service'} } ) { # We cannot use an early call to StorProc->get_hostid_servicenameid_serviceid() above because # the set of services may have been changed just above by StorProc->service_profile_apply(). # Thus we must probe now for this individual service. my $sid = undef; my %where = ( 'host_id' => $id, 'servicename_id' => $service_name{$service}{'id'} ); my @sids = StorProc->fetch_list_where('services', 'service_id', \%where); if (@sids) { if (@sids == 1) { $sid = $sids[0]; } else { # This will be impossible once we have a unique {host_id, servicename_id} index on 'services'. $results{'errors'}{$rec} .= "Error: found duplicates of host $import_data{$rec}{'Name'} service $service."; } } else { my @values = ( \undef, $id, $service_name{$service}{'id'}, $service_name{$service}{'template'}, $service_name{$service}{'extinfo'}, $service_name{$service}{'escalation'}, '1', $service_name{$service}{'check_command'}, $import_data{$rec}{'Service'}{$service}{'command_line'}, '', '' ); $sid = StorProc->insert_obj_id( 'services', \@values, 'service_id' ); if ( $sid =~ /^Error/ ) { $results{'errors'}{$rec} .= $sid; $sid = undef; } } if (defined $sid) { my @values = ( $sid, $service_name_overrides{$service}{'check_period'}, $service_name_overrides{$service}{'notification_period'}, $service_name_overrides{$service}{'event_handler'}, $service_name_overrides{$service}{'data'} ); my $result = StorProc->insert_obj( 'service_overrides', \@values ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } foreach my $dependency_id ( keys %{ $service_name_dependency{$service} } ) { my $depend_on_host = $id; if ( $service_name_dependency{$service}{$dependency_id}{'host'} ) { $depend_on_host = $service_name_dependency{$service}{$dependency_id}{'host'}; } @values = ( \undef, $sid, $id, $depend_on_host, $service_name_dependency{$service}{$dependency_id}{'template'}, '' ); my $result = StorProc->insert_obj( 'service_dependency', \@values ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } foreach my $instance ( keys %{ $import_data{$rec}{'Service'}{$service}{'instance'} } ) { @values = ( \undef, $sid, $instance, '1', $import_data{$rec}{'Service'}{$service}{'instance'}{$instance}{'arguments'} ); my $result = StorProc->insert_obj( 'service_instance', \@values ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } foreach my $ext ( keys %{ $service_name_externals{ $service_name{$service}{'id'} } } ) { @values = ( $ext, $id, $sid, $externals{'service'}{$ext}, \'0+0' ); my $result = StorProc->insert_obj( 'external_service', \@values ); if ( $result =~ /^Error/ ) { $results{'errors'}{$rec} .= $result; } } } } } } } # Return a hash of hashes describing what happened, key = host name return %results; } sub process_import_sync(@) { my %schema = %{ $_[1] }; my %import_data = %{ $_[2] }; my $data_check = $_[3]; my %results = (); # Hash of arrays to capture results # Translate host attributes name, address, alias to primary key values my %hosts_vitals = StorProc->get_hosts_vitals(); # Get sync objects - the values we want are stored as hash keys so we need to extract them # Each record has only one sync object value, but it is stored as a hash key my %sync_objs = (); foreach my $rec ( keys %import_data ) { if ( $schema{'sync_object'} eq 'Host' ) { if ( $import_data{$rec}{'Host name'} ) { if ( $hosts_vitals{'name'}{ $import_data{$rec}{'Host name'} } ) { $sync_objs{$rec}{'name'} = $import_data{$rec}{'Host name'}; $sync_objs{$rec}{'id'} = $hosts_vitals{'name'}{ $import_data{$rec}{'Host name'} }; } else { $results{'errors'}{$rec} = "Host \"$import_data{$rec}{'Host name'}\" not defined. Cannot sync non-existant host."; } } elsif ( $import_data{$rec}{'Address'} ) { if ( $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} } ) { $sync_objs{$rec}{'name'} = $import_data{$rec}{'Address'}; $sync_objs{$rec}{'id'} = $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} }; } else { $results{'errors'}{$rec} = "Host with address \"$import_data{$rec}{'Address'}\" not defined. Cannot sync non-existant host."; } } elsif ( $import_data{$rec}{'Alias'} ) { if ( $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} } ) { $sync_objs{$rec}{'name'} = $import_data{$rec}{'Alias'}; $sync_objs{$rec}{'id'} = $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} }; } else { $results{'errors'}{$rec} = "Host with alias \"$import_data{$rec}{'Alias'}\" not defined. Cannot sync non-existant host."; } } } elsif ( $schema{'sync_object'} eq 'Parent' ) { foreach my $obj ( keys %{ $import_data{$rec}{'Parent'} } ) { if ( $hosts_vitals{'name'}{$obj} || $hosts_vitals{'address'}{$obj} || $hosts_vitals{'alias'}{$obj} ) { $sync_objs{$rec}{'name'} = $obj; $sync_objs{$rec}{'id'} = $import_data{$rec}{'Parent'}{$obj}; } else { $results{'errors'}{$rec} = "Parent \"$obj\" not defined. Cannot use non-existant parent as sync object."; } } } elsif ( $schema{'sync_object'} eq 'Group' ) { foreach my $obj ( keys %{ $import_data{$rec}{'Group'} } ) { $sync_objs{$rec}{'name'} = $obj; $sync_objs{$rec}{'id'} = $import_data{$rec}{'Group'}{$obj}; } } elsif ( $schema{'sync_object'} eq 'Host group' ) { foreach my $obj ( keys %{ $import_data{$rec}{'Host group'} } ) { $sync_objs{$rec}{'name'} = $obj; $sync_objs{$rec}{'id'} = $import_data{$rec}{'Host group'}{$obj}; } } elsif ( $schema{'sync_object'} eq 'Contact group' ) { foreach my $obj ( keys %{ $import_data{$rec}{'Contact group'} } ) { $sync_objs{$rec}{'name'} = $obj; $sync_objs{$rec}{'id'} = $import_data{$rec}{'Contact group'}{$obj}; } } } # Clear existing associations before importing new ones foreach my $rec ( keys %import_data ) { unless ( $results{'errors'}{$rec} ) { if ( $schema{'sync_object'} eq 'Host' ) { if ( $sync_objs{$rec}{'id'} ) { if ( $import_data{$rec}{'Contact group'} ) { my %where = ( 'host_id' => $sync_objs{$rec}{'id'} ); my $result = StorProc->delete_one_where( 'contactgroup_host', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } if ( $import_data{$rec}{'Parent'} ) { my $result = StorProc->delete_all( 'host_parent', 'host_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } if ( $import_data{$rec}{'Group'} ) { my $result = StorProc->delete_all( 'monarch_group_host', 'host_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } if ( $import_data{$rec}{'Host group'} ) { my $result = StorProc->delete_all( 'hostgroup_host', 'host_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } elsif ( $schema{'sync_object'} eq 'Parent' ) { my %processed = (); unless ( $processed{ $sync_objs{$rec}{'name'} } ) { if ( $hosts_vitals{'name'}{ $sync_objs{$rec}{'name'} } || $hosts_vitals{'address'}{ $sync_objs{$rec}{'name'} } || $hosts_vitals{'alias'}{ $sync_objs{$rec}{'name'} } ) { my $result = StorProc->delete_all( 'host_parent', 'parent_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{ $sync_objs{$rec}{'name'} } = 1; } } } elsif ( $schema{'sync_object'} eq 'Group' ) { my %processed = (); if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { unless ( $processed{'host'}{ $sync_objs{$rec}{'name'} } ) { my $result = StorProc->delete_all( 'monarch_group_host', 'group_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'host'}{ $sync_objs{$rec}{'name'} } = 1; } } if ( $import_data{$rec}{'Host group'} ) { unless ( $processed{'hostgroup'}{ $sync_objs{$rec}{'name'} } ) { my $result = StorProc->delete_all( 'monarch_group_hostgroup', 'group_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'hostgroup'}{ $sync_objs{$rec}{'name'} } = 1; } } if ( $import_data{$rec}{'Contact group'} ) { unless ( $processed{'contactgroup'}{ $sync_objs{$rec}{'name'} } ) { my %where = ( 'group_id' => $sync_objs{$rec}{'id'} ); my $result = StorProc->delete_one_where( 'contactgroup_group', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'contactgroup'}{ $sync_objs{$rec}{'name'} } = 1; } } } elsif ( $schema{'sync_object'} eq 'Host group' ) { my %processed = (); if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { unless ( $processed{'host'}{ $sync_objs{$rec}{'name'} } ) { my $result = StorProc->delete_all( 'hostgroup_host', 'hostgroup_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'host'}{ $sync_objs{$rec}{'name'} } = 1; } } if ( $import_data{$rec}{'Contact group'} ) { unless ( $processed{'contactgroup'}{ $sync_objs{$rec}{'name'} } ) { my %where = ( 'hostgroup_id' => $sync_objs{$rec}{'id'} ); my $result = StorProc->delete_one_where( 'contactgroup_hostgroup', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'contactgroup'}{ $sync_objs{$rec}{'name'} } = 1; } } if ( $import_data{$rec}{'Group'} ) { unless ( $processed{'group'}{ $sync_objs{$rec}{'name'} } ) { my $result = StorProc->delete_all( 'monarch_group_hostgroup', 'hostgroup_id', $sync_objs{$rec}{'id'} ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'group'}{ $sync_objs{$rec}{'name'} } = 1; } } } elsif ( $schema{'sync_object'} eq 'Contact group' ) { my %processed = (); if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { unless ( $processed{'hosts'}{ $sync_objs{$rec}{'name'} } ) { my %where = ( 'contactgroup_id' => $sync_objs{$rec}{'id'} ); my $result = StorProc->delete_one_where( 'contactgroup_host', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'hosts'}{ $sync_objs{$rec}{'name'} } = 1; } } if ( $import_data{$rec}{'Group'} ) { unless ( $processed{'group'}{ $sync_objs{$rec}{'name'} } ) { my %where = ( 'contactgroup_id' => $sync_objs{$rec}{'id'} ); my $result = StorProc->delete_one_where( 'contactgroup_group', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'group'}{ $sync_objs{$rec}{'name'} } = 1; } } if ( $import_data{$rec}{'Host group'} ) { unless ( $processed{'hostgroup'}{ $sync_objs{$rec}{'name'} } ) { my %where = ( 'contactgroup_id' => $sync_objs{$rec}{'id'} ); my $result = StorProc->delete_one_where( 'contactgroup_hostgroup', \%where ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } $processed{'hostgroup'}{ $sync_objs{$rec}{'name'} } = 1; } } } } } # Importing new associations my %processed = (); foreach my $rec ( keys %import_data ) { unless ( $results{'errors'}{$rec} ) { # Host # A host name, address, or alias can be synced with parents, and/or monarch groups, and/or host groups, and/or contact groups. if ( $schema{'sync_object'} eq 'Host' ) { if ( $sync_objs{$rec}{'id'} ) { if ( $import_data{$rec}{'Parent'} ) { foreach my $parent ( keys %{ $import_data{$rec}{'Parent'} } ) { if ( $hosts_vitals{'name'}{$parent} ) { unless ( $sync_objs{$rec}{'id'} eq $hosts_vitals{'name'}{$parent} ) { my @values = ( $sync_objs{$rec}{'id'}, $hosts_vitals{'name'}{$parent} ); my $result = StorProc->insert_obj( 'host_parent', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } else { $results{'errors'}{$rec} = "Parent host \"$parent\" does not exist"; } } } if ( $import_data{$rec}{'Group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Group'} } ) { if ( $import_data{$rec}{'Group'}{$group} ) { my @values = ( $sync_objs{$rec}{'id'}, $import_data{$rec}{'Group'}{$group} ); my $result = StorProc->insert_obj( 'monarch_group_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } if ( $import_data{$rec}{'Host group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Host group'} } ) { if ( $import_data{$rec}{'Host group'}{$group} ) { my @values = ( $sync_objs{$rec}{'id'}, $import_data{$rec}{'Host group'}{$group} ); my $result = StorProc->insert_obj( 'hostgroup_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } if ( $import_data{$rec}{'Contact group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Contact group'} } ) { if ( $import_data{$rec}{'Contact group'}{$group} ) { my @values = ( $import_data{$rec}{'Contact group'}{$group}, $sync_objs{$rec}{'id'} ); my $result = StorProc->insert_obj( 'contactgroup_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } } } # Host group # A host group can be synced with one of the three host attributes, and/or monarch groups, and/or contact groups elsif ( $schema{'sync_object'} eq 'Host group' ) { if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { my $host_id = undef; if ( $import_data{$rec}{'Host name'} ) { $host_id = $hosts_vitals{'name'}{ $import_data{$rec}{'Host name'} }; } elsif ( $import_data{$rec}{'Address'} ) { $host_id = $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} }; } elsif ( $import_data{$rec}{'Alias'} ) { $host_id = $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} }; } my @values = ( $sync_objs{$rec}{'id'}, $host_id ); my $result = StorProc->insert_obj( 'hostgroup_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } if ( $import_data{$rec}{'Contact group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Contact group'} } ) { if ( $import_data{$rec}{'Contact group'}{$group} ) { my @values = ( $import_data{$rec}{'Contact group'}{$group}, $sync_objs{$rec}{'id'} ); my $result = StorProc->insert_obj( 'contactgroup_hostgroup', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } if ( $import_data{$rec}{'Group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Group'} } ) { if ( $import_data{$rec}{'Group'}{$group} ) { my @values = ( $import_data{$rec}{'Group'}{$group}, $sync_objs{$rec}{'id'} ); my $result = StorProc->insert_obj( 'monarch_group_hostgroup', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } } # Parent # A parent host can be synced with one of the three host attributes elsif ( $schema{'sync_object'} eq 'Parent' ) { if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { my $host_id = undef; if ( $import_data{$rec}{'Host name'} ) { $host_id = $hosts_vitals{'name'}{ $import_data{$rec}{'Host name'} }; } elsif ( $import_data{$rec}{'Address'} ) { $host_id = $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} }; } elsif ( $import_data{$rec}{'Alias'} ) { $host_id = $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} }; } if ($host_id) { unless ( $sync_objs{$rec}{'id'} eq $host_id ) { my @values = ( $sync_objs{$rec}{'id'}, $host_id ); my $result = StorProc->insert_obj( 'host_parent', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } } # Group # A monarch group can be synced with one of the three host attributes, and/or host groups, and/or contact groups elsif ( $schema{'sync_object'} eq 'Group' ) { if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { my $host_id = undef; my $host_value = undef; if ( $import_data{$rec}{'Host name'} ) { $host_id = $hosts_vitals{'name'}{ $import_data{$rec}{'Host name'} }; $host_value = "host name $import_data{$rec}{'Host name'}"; } elsif ( $import_data{$rec}{'Address'} ) { $host_id = $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} }; $host_value = "host address $import_data{$rec}{'Address'}"; } elsif ( $import_data{$rec}{'Alias'} ) { $host_id = $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} }; $host_value = "host alias $import_data{$rec}{'Alias'}"; } if ($host_id) { my @values = ( $sync_objs{$rec}{'id'}, $host_id ); my $result = StorProc->insert_obj( 'monarch_group_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } else { $results{'errors'}{$rec} = "Match not found: $host_value."; } } if ( $import_data{$rec}{'Host group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Host group'} } ) { if ( $import_data{$rec}{'Host group'}{$group} ) { my @values = ( $sync_objs{$rec}{'id'}, $import_data{$rec}{'Host group'}{$group} ); my $result = StorProc->insert_obj( 'monarch_group_hostgroup', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } if ( $import_data{$rec}{'Contact group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Contact group'} } ) { if ( $import_data{$rec}{'Contact group'}{$group} ) { my @values = ( $import_data{$rec}{'Contact group'}{$group}, $sync_objs{$rec}{'id'} ); my $result = StorProc->insert_obj( 'contactgroup_group', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } } # Contact group # A contact group can be synced with one of the three host attributes, and/or host groups, and/or monarch groups elsif ( $schema{'sync_object'} eq 'Contact group' ) { if ( $import_data{$rec}{'Host name'} || $import_data{$rec}{'Address'} || $import_data{$rec}{'Alias'} ) { my $host_id = undef; my $host_value = undef; if ( $import_data{$rec}{'Host name'} ) { $host_id = $hosts_vitals{'name'}{ $import_data{$rec}{'Host name'} }; $host_value = "host name $import_data{$rec}{'Host name'}"; } elsif ( $import_data{$rec}{'Address'} ) { $host_id = $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} }; $host_value = "host address $import_data{$rec}{'Address'}"; } elsif ( $import_data{$rec}{'Alias'} ) { $host_id = $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} }; $host_value = "host alias $import_data{$rec}{'Alias'}"; } if ($host_id) { my @values = ( $sync_objs{$rec}{'id'}, $host_id ); my $result = StorProc->insert_obj( 'contactgroup_host', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } else { $results{'errors'}{$rec} = "Match not found: $host_value."; } } if ( $import_data{$rec}{'Host group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Host group'} } ) { if ( $import_data{$rec}{'Host group'}{$group} ) { my @values = ( $sync_objs{$rec}{'id'}, $import_data{$rec}{'Host group'}{$group} ); my $result = StorProc->insert_obj( 'contactgroup_hostgroup', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } if ( $import_data{$rec}{'Group'} ) { foreach my $group ( keys %{ $import_data{$rec}{'Group'} } ) { if ( $import_data{$rec}{'Group'}{$group} ) { my @values = ( $sync_objs{$rec}{'id'}, $import_data{$rec}{'Group'}{$group} ); my $result = StorProc->insert_obj( 'contactgroup_group', \@values ); if ( $result =~ /error/i ) { $results{'errors'}{$rec} = $result; } } } } } unless ( $results{'errors'}{$rec} ) { $results{'updated'}{ $import_data{$rec}{'Name'} } = 'host updated'; } } } # Return a hash of hashs describing what happened, with key = host name return %results; } sub discover_prep(@) { my %group = %{ $_[1] }; my %process = (); my @errors = (); $process{'id'} = $group{'id'}; $process{'name'} = $group{'name'}; $process{'description'} = $group{'description'}; $process{'auto'} = $group{'auto'}; $process{'schema'} = $group{'schema'}; $process{'enable_traceroute'} = $group{'enable_traceroute'}; $process{'traceroute_command'} = $group{'traceroute_command'}; $process{'traceroute_max_hops'} = $group{'traceroute_max_hops'}; $process{'traceroute_timeout'} = $group{'traceroute_timeout'}; @{ $process{'nmaps'} } = (); @{ $process{'udp_nmaps'} } = (); @{ $process{'snmps'} } = (); @{ $process{'scripts'} } = (); @{ $process{'wmis'} } = (); # group filters my %global = (); foreach my $filter ( keys %{ $group{'filter'} } ) { my $type = $group{'filter'}{$filter}{'type'}; my @hosts = split( /,/, $group{'filter'}{$filter}{'filter'} ); foreach my $host_str (@hosts) { if ( $host_str =~ m{^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)\s*$} ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } if ($5 < 16 || $5 > 32) { push @errors, "Error: subnet prefix length /$5 in range/filter \"$filter\" is invalid (must be between /16 and /32 inclusive)."; last; } my $ip_base = unpack ('N', pack ('C4', $1, $2, $3, $4)); my $prefix_len = $5; my $_32_bits = 0xffffffff; my $netmask = ($_32_bits << (32 - $prefix_len)) & $_32_bits; my $hostmask = $_32_bits ^ $netmask; my $network_addr = $ip_base & $netmask; my $broadcast_addr = $ip_base | $hostmask; if ($ip_base & $hostmask) { my ($b0, $b1, $b2, $b3) = unpack ('C4', pack ('N', $network_addr)); push @errors, "Error: illegal CIDR block in range/filter \"$filter\"; " . "base address $1.$2.$3.$4 extends beyond subnet mask /$5 (network address $b0.$b1.$b2.$b3)."; last; } my $min_ip_addr = $network_addr; my $max_ip_addr = $broadcast_addr; if ($prefix_len <= 30) { ++$min_ip_addr; --$max_ip_addr; } for (my $host_dword = $min_ip_addr; $host_dword <= $max_ip_addr; ++$host_dword) { my ($b0, $b1, $b2, $b3) = unpack ('C4', pack ('N', $host_dword)); $global{$type}{$host_dword} = "$b0.$b1.$b2.$b3"; } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*-\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } if ($5 > 255 || $6 > 255 || $7 > 255 || $8 > 255) { push @errors, "Error: IP address $5.$6.$7.$8 in range/filter \"$filter\" is invalid."; last; } if ( $1 != $5 || $2 != $6 ) { ## the user has specified too wide an address range for our taste push @errors, "Error: IP address range '$host_str' in range/filter \"$filter\" is too large; the first two octets much match in this kind of range."; last; } my $i = $3; my $j = $4; while ( $i < $7 || ($i == $7 && $j <= $8) ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $i ) * 256 + $j; $global{$type}{$host_dword} = "$1.$2.$i.$j"; ## Note: We intentionally include supposed network (.0) and broadcast (.255) addresses ## in this counting, presuming that the user specified the range exactly as desired. ## Those won't actually be network and broadcast addresses if this is a section of some ## subnet (CIDR block) larger than a Class C subnet. See RFC 1812 for address conventions. ++$j; if ($j > 255) { ++$i; $j = 0; } } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*-\s*(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } if ($5 > 255) { push @errors, "Error: IP address $1.$2.$3.$5 in range/filter \"$filter\" is invalid."; last; } for ( my $j = $4 ; $j <= $5 ; $j++ ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $3 ) * 256 + $j; $global{$type}{$host_dword} = "$1.$2.$3.$j"; } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*-\s*(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255) { push @errors, "Error: IP address range $1.$2.$3.* in range/filter \"$filter\" is invalid."; last; } if ($4 > 255) { push @errors, "Error: IP address range $1.$2.$4.* in range/filter \"$filter\" is invalid."; last; } for ( my $i = $3 ; $i <= $4 ; $i++ ) { for ( my $j = 1 ; $j < 255 ; $j++ ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $i ) * 256 + $j; $global{$type}{$host_dword} = "$1.$2.$i.$j"; } } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.\*\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255) { push @errors, "Error: IP address range $1.$2.$3.* in range/filter \"$filter\" is invalid."; last; } for ( my $j = 1 ; $j < 255 ; $j++ ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $3 ) * 256 + $j; $global{$type}{$host_dword} = "$1.$2.$3.$j"; } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $3 ) * 256 + $4; $global{$type}{$host_dword} = "$1.$2.$3.$4"; } else { $global{$type}{$host_str} = $host_str; } } } foreach my $method ( sort keys %{ $group{'method'} } ) { $process{'method'}{$method}{'description'} = $group{'method'}{$method}{'description'}; if ( $group{'method'}{$method}{'type'} eq 'Nmap' ) { $process{'method'}{$method}{'timeout'} = $group{'method'}{$method}{'timeout'}; $process{'method'}{$method}{'scan_type'} = $group{'method'}{$method}{'scan_type'}; $process{'method'}{$method}{'tcp_snmp_check'} = $group{'method'}{$method}{'tcp_snmp_check'}; $process{'method'}{$method}{'snmp_strings'} = $group{'method'}{$method}{'snmp_strings'}; if ( $group{'method'}{$method}{'scan_type'} eq 'udp_scan' ) { push @{ $process{'udp_nmaps'} }, $method; } else { push @{ $process{'nmaps'} }, $method; } # ports my %ports = (); foreach my $port_definition ( keys %{ $group{'method'}{$method} } ) { if ( $port_definition =~ /^port_/ ) { my $value = $group{'method'}{$method}{$port_definition}; $port_definition =~ s/^port_//; $process{'method'}{$method}{'port_definition'}{$port_definition} = $value; if ( $port_definition =~ /,/ ) { my @ports = split( /,/, $port_definition ); foreach my $p (@ports) { if ( $p =~ /(\d+)-(\d+)/ ) { for ( my $i = $1 ; $i <= $2 ; $i++ ) { $ports{$i} = 1; } } else { $ports{$p} = 1; } } } elsif ( $port_definition =~ /(\d+)-(\d+)/ ) { for ( my $i = $1 ; $i <= $2 ; $i++ ) { $ports{$i} = 1; } } else { $ports{$port_definition} = 1; } } $process{'method'}{$method}{'port_list'} = join(',', sort { $a cmp $b } keys %ports); } } elsif ( $group{'method'}{$method}{'type'} eq 'SNMP' ) { push @{ $process{'snmps'} }, $method; $process{'method'}{$method}{'community_strings'} = $group{'method'}{$method}{'community_strings'}; $process{'method'}{$method}{'snmp_ver'} = $group{'method'}{$method}{'snmp_ver'}; } elsif ( $group{'method'}{$method}{'type'} eq 'WMI' ) { push @{ $process{'wmis'} }, $method; } elsif ( $group{'method'}{$method}{'type'} eq 'Script' ) { $process{'method'}{$method}{'run_mode'} = $group{'method'}{$method}{'run_mode'}; if ( $group{'method'}{$method}{'run_mode'} eq 'Batch Mode' ) { $group{'method'}{$method}{'command_line'} = 'batch:' . $group{'method'}{$method}{'command_line'} unless ( $group{'method'}{$method}{'command_line'} =~ /^batch:/ ); } $process{'method'}{$method}{'command_line'} = $group{'method'}{$method}{'command_line'}; push @{ $process{'scripts'} }, $method; } # method filters foreach my $filter ( keys %{ $group{'method'}{$method}{'filter'} } ) { my $type = $group{'method'}{$method}{'filter'}{$filter}{'type'}; my @hosts = split( /,/, $group{'method'}{$method}{'filter'}{$filter}{'filter'} ); foreach my $host_str (@hosts) { if ( $host_str =~ m{^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)\s*$} ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } if ($5 < 16 || $5 > 32) { push @errors, "Error: subnet prefix length /$5 in range/filter \"$filter\" is invalid (must be between /16 and /32 inclusive)."; last; } my $ip_base = unpack ('N', pack ('C4', $1, $2, $3, $4)); my $prefix_len = $5; my $_32_bits = 0xffffffff; my $netmask = ($_32_bits << (32 - $prefix_len)) & $_32_bits; my $hostmask = $_32_bits ^ $netmask; my $network_addr = $ip_base & $netmask; my $broadcast_addr = $ip_base | $hostmask; if ($ip_base & $hostmask) { my ($b0, $b1, $b2, $b3) = unpack ('C4', pack ('N', $network_addr)); push @errors, "Error: illegal CIDR block in range/filter \"$filter\"; " . "base address $1.$2.$3.$4 extends beyond subnet mask /$5 (network address $b0.$b1.$b2.$b3)."; last; } my $min_ip_addr = $network_addr; my $max_ip_addr = $broadcast_addr; if ($prefix_len <= 30) { ++$min_ip_addr; --$max_ip_addr; } for (my $host_dword = $min_ip_addr; $host_dword <= $max_ip_addr; ++$host_dword) { my ($b0, $b1, $b2, $b3) = unpack ('C4', pack ('N', $host_dword)); $process{'method'}{$method}{'host'}{$type}{$host_dword} = "$b0.$b1.$b2.$b3"; } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*-\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } if ($5 > 255 || $6 > 255 || $7 > 255 || $8 > 255) { push @errors, "Error: IP address $5.$6.$7.$8 in range/filter \"$filter\" is invalid."; last; } if ( $1 != $5 || $2 != $6 ) { ## the user has specified too wide an address range for our taste push @errors, "Error: IP address range '$host_str' in range/filter \"$filter\" is too large; the first two octets much match in this kind of range."; last; } my $i = $3; my $j = $4; while ( $i < $7 || ($i == $7 && $j <= $8) ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $i ) * 256 + $j; $process{'method'}{$method}{'host'}{$type}{$host_dword} = "$1.$2.$i.$j"; ## Note: We intentionally include supposed network (.0) and broadcast (.255) addresses ## in this counting, presuming that the user specified the range exactly as desired. ## Those won't actually be network and broadcast addresses if this is a section of some ## subnet (CIDR block) larger than a Class C subnet. See RFC 1812 for address conventions. ++$j; if ($j > 255) { ++$i; $j = 0; } } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*-\s*(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } if ($5 > 255) { push @errors, "Error: IP address $1.$2.$3.$5 in range/filter \"$filter\" is invalid."; last; } for ( my $j = $4 ; $j <= $5 ; $j++ ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $3 ) * 256 + $j; $process{'method'}{$method}{'host'}{$type}{$host_dword} = "$1.$2.$3.$j"; } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*-\s*(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255) { push @errors, "Error: IP address range $1.$2.$3.* in range/filter \"$filter\" is invalid."; last; } if ($4 > 255) { push @errors, "Error: IP address range $1.$2.$4.* in range/filter \"$filter\" is invalid."; last; } for ( my $i = $3 ; $i <= $4 ; $i++ ) { for ( my $j = 1 ; $j < 255 ; $j++ ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $i ) * 256 + $j; $process{'method'}{$method}{'host'}{$type}{$host_dword} = "$1.$2.$i.$j"; } } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.\*\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255) { push @errors, "Error: IP address range $1.$2.$3.* in range/filter \"$filter\" is invalid."; last; } for ( my $j = 1 ; $j < 255 ; $j++ ) { my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $3 ) * 256 + $j; $process{'method'}{$method}{'host'}{$type}{$host_dword} = "$1.$2.$3.$j"; } } elsif ( $host_str =~ /^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*$/ ) { if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255) { push @errors, "Error: IP address $1.$2.$3.$4 in range/filter \"$filter\" is invalid."; last; } my $host_dword = ( ( $1 * 256 + $2 ) * 256 + $3 ) * 256 + $4; $process{'method'}{$method}{'host'}{$type}{$host_dword} = "$1.$2.$3.$4"; } else { ## FIX THIS: why this difference? is this just a bug? if ( $group{'method'}{$method}{'filter'}{$filter}{'filter'} =~ /,/ ) { $process{'method'}{$method}{'host'}{$type}{$host_str} = $host_str; } else { $process{'method'}{$method}{'host'}{$type}{$filter} = $filter; } } } } # Apply group global filters to method foreach my $host ( keys %{ $global{'include'} } ) { $process{'method'}{$method}{'host'}{'include'}{$host} = $global{'include'}{$host}; } foreach my $host ( keys %{ $global{'exclude'} } ) { delete $process{'method'}{$method}{'host'}{'include'}{$host}; } # Consolidate local filters foreach my $host ( keys %{ $process{'method'}{$method}{'host'}{'exclude'} } ) { delete $process{'method'}{$method}{'host'}{'include'}{$host}; } $process{'method'}{$method}{'hosts'} = $process{'method'}{$method}{'host'}{'include'}; delete $process{'method'}{$method}{'host'}{'include'}; delete $process{'method'}{$method}{'host'}{'exclude'}; } return \%process, \@errors; } # FIX THIS: The use of a timestamp here as a cache buster is bogus, # since we will only generate this URL once and the browser will use it # multiple times to fetch Ajax data from the server. And even if that # were not true, a 1-second resolution of the timestamp is hardly enough # to make this number guaranteed unique across successive hits. This # has to be replaced with proper cache control at the header level. We # need to likewise look for other attempts to perform cache busting and # evaluate them for sensibility. sub get_scan_url() { my $nocache = time; return "$monarch_cgi/monarch_discover.cgi?nocache=$nocache"; } sub ajax_header(@) { return qq( Discovery ); } sub nmap_header(@) { my %suggestions = %{ $_[1] }; my $scan_type = $_[2]; my $suggestion_list = undef; foreach my $suggestion ( sort keys %suggestions ) { $suggestion_list .= qq("$suggestion",); } chop $suggestion_list; $scan_type = '' if not defined $scan_type; return qq( Discovery ); } sub discover_form(@) { my $name = $_[1]; my %process = %{ $_[2] }; my $import_file = $_[3]; my $monarch_home = $_[4]; my $user_name = $_[5]; my %methods_js = (); my $port_defs_str = ''; my $ports_str = ''; my $snmp_match_str = ''; my $tcp_snmp_opt = ''; my $snmp_commands = ''; my $traceroute = ''; if ( $process{'enable_traceroute'} ) { $traceroute = "$process{'traceroute_command'}"; $traceroute .= " -m $process{'traceroute_max_hops'}" if $process{'traceroute_max_hops'}; $traceroute .= " -w $process{'traceroute_timeout'}" if $process{'traceroute_timeout'}; } my $traceroute_opt = ", 'traceroute'"; my $input_tags = qq( ); my $method_cnt = keys %{ $process{'method'} }; my $nmap_enabled = 0; my $i = $method_cnt; if ( $process{'auto'} =~ /Auto/ ) { $i++ } if ( $process{'auto'} eq 'Auto-Commit' ) { $i++ } $i--; my $method_id = 1; ## Nmap foreach my $method ( @{ $process{'nmaps'} } ) { $nmap_enabled = 1; $input_tags .= qq( ); my $host_cnt = keys %{ $process{'method'}{$method}{'hosts'} }; $methods_js{$method}{'vars'} .= qq( methods[$i] = $method_id; var hosts_$method_id = new Array($host_cnt);); my $j = $host_cnt; $j--; foreach my $host ( sort keys %{ $process{'method'}{$method}{'hosts'} } ) { my $message_ele = 'hosts_' . $method_id . '[' . $j . ']'; $methods_js{$method}{'hosts'} .= qq( $message_ele="$process{'method'}{$method}{'hosts'}{$host}";); $input_tags .= qq( ); $j--; } my $port_defs = undef; foreach my $port_def ( keys %{ $process{'method'}{$method}{'port_definition'} } ) { $port_defs .= qq(port_def_$method_id\_$port_def=$process{'method'}{$method}{'port_definition'}{$port_def}:-:); } $port_defs =~ s/:-:$//; ############################################################################ # An evil IE limitation forced this wasteful kludge ############################################################################ # $port_defs = uri_escape($port_defs); $port_defs_str .= "#port_def_$method_id:-:$port_defs\n"; # $input_tags .= qq( # ); $ports_str .= "#ports_$method_id:-:$process{'method'}{$method}{'port_list'}\n"; # $input_tags .= qq( # );name="snmp_strings"> ); $methods_js{$method}{'vars'} .= qq( methods[$i] = $method_id;); $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { // alert('debug ' + method_complete); document.getElementById("method_status").innerHTML = "$method"; var type = "type_$method_id"; var scan_type = "scan_type_$method_id"; var timeout = "timeout_$method_id"; var method_id = "method_id_$method_id"; var input_0 = "input_0_$method_id"; if (method_complete) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; method_complete = ''; process_methods(); } else { document.getElementById("status").innerHTML = 'Running UDP scans ...'; get_host( [ input_0, type, 'file', 'monarch_home', scan_type, timeout, method_id$traceroute_opt ], [ addRow ] ); // alert('debug after' + method_complete); }); } else { $nmap_enabled = 1; $input_tags .= qq( ); my $host_cnt = keys %{ $process{'method'}{$method}{'hosts'} }; $methods_js{$method}{'vars'} .= qq( methods[$i] = $method_id; var hosts_$method_id = new Array($host_cnt);); my $j = $host_cnt; $j--; foreach my $host ( sort keys %{ $process{'method'}{$method}{'hosts'} } ) { my $message_ele = 'hosts_' . $method_id . '[' . $j . ']'; $methods_js{$method}{'hosts'} .= qq( $message_ele="$process{'method'}{$method}{'hosts'}{$host}";); $input_tags .= qq( ); $j--; } my $port_defs = undef; foreach my $port_def ( keys %{ $process{'method'}{$method}{'port_definition'} } ) { $port_defs .= qq(port_def_$method_id\_$port_def=$process{'method'}{$method}{'port_definition'}{$port_def}:-:); } $port_defs =~ s/:-:$//; $port_defs_str .= "#port_def_$method_id:-:$port_defs\n"; $ports_str .= "#ports_$method_id:-:$process{'method'}{$method}{'port_list'}\n"; $snmp_match_str .= "#snmp_match_strings:-:$process{'method'}{$method}{'snmp_strings'}\n"; $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { document.getElementById("method_status").innerHTML = "$method"; var type = "type_$method_id"; var scan_type = "scan_type_$method_id"; var timeout = "timeout_$method_id"; var method_id = "method_id_$method_id"; var host = hosts_$method_id.pop(); if (host) { document.getElementById("status").innerHTML = 'Scanning: ' + host + ' ...'; get_host( [ host, type, 'file', 'monarch_home', scan_type, timeout, method_id$traceroute_opt ], [ addRow ] ); } else { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; process_methods(); }); } $i--; $method_id++; $traceroute_opt = ''; } ## SNMP foreach my $method ( @{ $process{'snmps'} } ) { my $snmp_command = ''; if ( $process{'method'}{$method}{'snmp_ver'} eq '3' ) { $snmp_command .= "#snmp_command_$method_id:-:-u $process{'method'}{$method}{'snmp_v3_user'}"; unless ( $process{'method'}{$method}{'snmp_v3_authProtocol'} eq 'none' ) { $snmp_command .= " -a $process{'method'}{$method}{'snmp_v3_authProtocol'}"; $snmp_command .= " -A $process{'method'}{$method}{'snmp_v3_authKey'}" if defined $process{'method'}{$method}{'snmp_v3_authKey'}; } unless ( $process{'method'}{$method}{'snmp_v3_privProtocol'} eq 'none' ) { $snmp_command .= " -x $process{'method'}{$method}{'snmp_v3_privProtocol'}"; $snmp_command .= " -X $process{'method'}{$method}{'snmp_v3_privKey'}" if defined $process{'method'}{$method}{'snmp_v3_privKey'}; } if ( $process{'method'}{$method}{'snmp_v3_misc'} ) { $snmp_command .= " $process{'method'}{$method}{'snmp_v3_misc'}"; } $snmp_command .= " -l $process{'method'}{$method}{'snmp_v3_securityLevel'}\n"; } $snmp_commands = $snmp_command; $methods_js{$method}{'vars'} .= qq( methods[$i] = $method_id;); $input_tags .= qq( ); ## NMAP SNMP if ($nmap_enabled) { $input_tags .= qq( ); $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { document.getElementById("method_status").innerHTML = "$method"; var type = "type_$method_id"; var command = "snmp_command_$method_id"; var community_strings = "community_strings_$method_id"; var input_0 = "input_0_$method_id"; var snmp_ver = "snmp_ver_$method_id"; if (method_complete) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; method_complete = ''; process_methods(); } else { if (host) { document.getElementById("status").innerHTML = 'Scanning' + host + ' ...'; } else { document.getElementById("status").innerHTML = 'Running SNMP scans ...'; } get_host( [ input_0, type, 'file', 'monarch_home', community_strings, command, snmp_ver ], [ addRow ] ); }); } ## SOLO SNMP else { $input_tags .= qq( ); my $host_cnt = keys %{ $process{'method'}{$method}{'hosts'} }; $methods_js{$method}{'vars'} .= qq( var hosts_$method_id = new Array($host_cnt);); my $j = $host_cnt; $j--; foreach my $host ( sort keys %{ $process{'method'}{$method}{'hosts'} } ) { my $message_ele = 'hosts_' . $method_id . '[' . $j . ']'; $methods_js{$method}{'hosts'} .= qq( $message_ele="$process{'method'}{$method}{'hosts'}{$host}";); $input_tags .= qq( ); $j--; } $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") {); $methods_js{$method}{'main'} .= qq( document.getElementById("method_status").innerHTML = "$method";); $input_tags .= qq( ); $methods_js{$method}{'main'} .= qq( var type = "type_$method_id"; var command = "snmp_command_$method_id"; var community_strings = "community_strings_$method_id"; var host = hosts_$method_id.pop(); var snmp_ver = "snmp_ver_$method_id"; if (host == undefined) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; process_methods(); } else { document.getElementById("status").innerHTML = 'Scanning: ' + host + ' ...'; get_host( [ host, type, 'file', 'monarch_home', community_strings, command, snmp_ver$traceroute_opt ], [ addRow ] ); }); } $i--; $method_id++; $traceroute_opt = ''; } ## Scripts foreach my $method ( @{ $process{'scripts'} } ) { $methods_js{$method}{'vars'} .= qq( methods[$i] = $method_id;); $input_tags .= qq( ); ##### NMAP Scripts if ( $nmap_enabled && $process{'method'}{$method}{'run_mode'} ne 'Batch Mode' ) { $input_tags .= qq( ); $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { document.getElementById("method_status").innerHTML = "$method"; var type = "type_$method_id"; var command = "script_$method_id"; var input_0 = "input_0_$method_id"; if (method_complete) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; method_complete = ''; process_methods(); } else { if (host) { document.getElementById("status").innerHTML = 'Scanning' + host + ' ...'; } else { document.getElementById("status").innerHTML = 'Running script scans ...'; } get_host( [ input_0, type, 'file', 'monarch_home', command ], [ addRow ] ); }); } ## Scripts that run over each host in range, regardless of Nmap output elsif ( !$nmap_enabled && $process{'method'}{$method}{'run_mode'} ne 'Batch Mode' ) { $input_tags .= qq( ); my $host_cnt = keys %{ $process{'method'}{$method}{'hosts'} }; $methods_js{$method}{'vars'} .= qq( var hosts_$method_id = new Array($host_cnt);); my $j = $host_cnt; $j--; foreach my $host ( sort keys %{ $process{'method'}{$method}{'hosts'} } ) { my $message_ele = 'hosts_' . $method_id . '[' . $j . ']'; $methods_js{$method}{'hosts'} .= qq( $message_ele="$process{'method'}{$method}{'hosts'}{$host}";); $input_tags .= qq( ); $j--; } $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { document.getElementById("method_status").innerHTML = "$method"; var type = "type_$method_id"; var command = "script_$method_id"; var host = hosts_$method_id.pop(); if (host == undefined) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; process_methods(); } else { document.getElementById("status").innerHTML = 'Scanning: ' + host + ' ...'; get_host( [ host, type, 'file', 'monarch_home', command ], [ addRow ] ); }); } ## Scripts that run only once (aka SOLO (??) scripts) else { $input_tags .= qq( ); $methods_js{$method}{'vars'} .= qq( var hosts_$method_id = new Array(1);); $methods_js{$method}{'hosts'} = "hosts_$method_id" . "[0]=\"batch\";"; $input_tags .= ""; $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { document.getElementById("method_status").innerHTML = "$method"; var type = "type_$method_id"; var command = "script_$method_id"; var host = hosts_$method_id.pop(); if (host == undefined) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; process_methods(); } else { document.getElementById("status").innerHTML = 'Scanning: ' + host + ' ...'; get_host( [ host, type, 'file', 'monarch_home', command ], [ addRow ] ); }); } $i--; $method_id++; } ## WMI foreach my $method ( @{ $process{'wmis'} } ) { $methods_js{$method}{'vars'} .= qq( methods[$i] = $method_id;); unless ($nmap_enabled) { my $host_cnt = keys %{ $process{'method'}{$method}{'hosts'} }; $methods_js{$method}{'vars'} .= qq( var hosts_$method_id = new Array($host_cnt);); my $j = $host_cnt; $j--; foreach my $host ( sort keys %{ $process{'method'}{$method}{'hosts'} } ) { my $message_ele = 'hosts_' . $method_id . '[' . $j . ']'; $methods_js{$method}{'hosts'} .= qq( $message_ele="$process{'method'}{$method}{'hosts'}{$host}";); $input_tags .= qq( ); $j--; } } $methods_js{$method}{'main'} .= qq( } else if (method == "$method_id") { document.getElementById("method_status").innerHTML = "$method";); if ($nmap_enabled) { $input_tags .= qq( ); $methods_js{$method}{'main'} .= qq( var type = "type_$method_id"; get_host( [ host, type, 'file', 'monarch_home', community_strings ], [ addRow ] ); method = undefined; process_methods();); } else { $input_tags .= qq( ); $methods_js{$method}{'main'} .= qq( var type = "wmi"; var host = hosts_$method_id.pop(); if (host == undefined) { document.getElementById("status").innerHTML = "Method $method complete"; method = undefined; process_methods(); } else { document.getElementById("status").innerHTML = host + ' ...'; get_host( [ host, type, 'file', 'monarch_home' ], [ addRow ] ); }); } $i--; $method_id++; } ## Auto import if ( $process{'auto'} eq 'Auto' ) { $input_tags .= qq( ); } ## Auto commit if ( $process{'auto'} eq 'Auto-Commit' ) { $input_tags .= qq( ); } my $javascript_hosts = ''; my $javascript_main = ''; my $javascript_vars = ''; foreach my $method ( keys %methods_js ) { $javascript_hosts .= $methods_js{$method}{'hosts'} if defined $methods_js{$method}{'hosts'}; } foreach my $method ( keys %methods_js ) { $javascript_main .= $methods_js{$method}{'main'}; } foreach my $method ( keys %methods_js ) { $javascript_vars .= $methods_js{$method}{'vars'}; } # FIX MINOR: SafeAddOnload(process_methods); probably isn't doing anything, because it's called # before the function is defined; should the call be moved after the function, or just deleted? my $javascript = qq( ); my $detail = qq(
Discovery ...
Wait for scan to finish before closing this window. Select "Cancel" to start over.
  Method:
  Status:
$javascript $input_tags
Time Event Detail (typically: Address, Host, Description)
 
); if ( $process{'auto'} eq 'Interactive' ) { $detail .= qq(    ); } if ( $process{'auto'} eq 'Auto' ) { $detail .= qq(    ); } $detail .= qq(
); my $errstr = undef; my $dt = datetime(); if ( !open( FILE, '>', "$config_settings{'monarch_home'}/automation/data/$import_file" ) ) { $errstr = "Error: Cannot create $config_settings{'monarch_home'}/automation/data/$import_file ($!)"; } else { print FILE "\n#$dt Discovery process begins.\n# name;;alias;;address;;description;;parent;;profile;;service profile;;service\n"; print FILE $port_defs_str . $ports_str . $tcp_snmp_opt . $snmp_match_str . $snmp_commands; close FILE; } return $detail, $errstr; } ############################################################################ # Subs to support automation scripts ############################################################################ sub config_settings() { my %where = (); my %objects = StorProc->fetch_list_hash_array( 'setup', \%where ); $config_settings{'nagios_ver'} = $objects{'nagios_version'}[2]; $config_settings{'nagios_bin'} = $objects{'nagios_bin'}[2]; $config_settings{'nagios_etc'} = $objects{'nagios_etc'}[2]; $config_settings{'monarch_home'} = $objects{'monarch_home'}[2]; $config_settings{'monarch_ver'} = $objects{'monarch_version'}[2]; $config_settings{'backup_dir'} = $objects{'backup_dir'}[2]; $config_settings{'commit_type'} = $objects{'commit_type'}[2]; unless ( $config_settings{'commit_type'} ) { $config_settings{'commit_type'} = 'default'; } $config_settings{'illegal_object_name_chars'} = $objects{'illegal_object_name_chars'}[2]; $config_settings{'illegal_object_name_chars'} .= "/"; $config_settings{'is_portal'} = 0; if ( -e '/usr/local/groundwork/config/db.properties' ) { $config_settings{'is_portal'} = 1; } return %config_settings; } sub backup() { %config_settings = %{ $_[1] }; my ( $backup_dir, $errors ) = StorProc->backup( $config_settings{'nagios_etc'}, $config_settings{'backup_dir'} ); return $backup_dir, \@{$errors}; } sub build_files(@) { my %file_ref = %{ $_[1] }; %config_settings = %{ $_[2] }; config_settings(); my ( $files, $errors ) = Files->build_files( $file_ref{'user_acct'}, $file_ref{'group'}, $file_ref{'commit_step'}, $file_ref{'export'}, $config_settings{'nagios_ver'}, $config_settings{'nagios_etc'}, $file_ref{'location'}, $file_ref{'tarball'} ); if ( !defined( $file_ref{'commit_step'} ) || $file_ref{'commit_step'} =~ /\d/ ) { ## If an error leads you here, you have been bitten by the well-intentioned renaming of a ## hash key. Previously the hashkey in question was called variously 'type' or 'preflight', ## and it had possible values of '' (empty string), 1, or 2, with 1 meaning ## preflight and 2 meaning commit. Now it is called 'commit_step' and it has possible ## corresponding values of '' (empty string), 'preflight', and 'commit'. print STDERR &$Instrument::show_trace_as_text(); push( @$errors, "Error: in MonarchAutoConfig.pm build_files(), file_ref hashref should include a commit_step element with value of '', 'preflight', or 'commit'." ); } return $files, \@{$errors}; } sub pre_flight_check(@) { %config_settings = %{ $_[1] }; my @preflight_results = StorProc->pre_flight_check( $config_settings{'nagios_bin'}, $config_settings{'monarch_home'} ); my $rc = 0; foreach my $msg (@preflight_results) { if ( $msg =~ /Things look okay/ ) { $rc = 1 } } if ( $config_settings{'verbose'} ) { return $rc, \@preflight_results; } else { return $rc; } } sub import_profile(@) { my $folder = $_[1]; my $file = $_[2]; my $overwrite = $_[3]; my @messages = (); use MonarchProfileImport; push @messages, "Importing $file"; my @msgs = ProfileImporter->import_profile( $folder, $file, $overwrite ); push( @messages, @msgs ); push @messages, '-----------------------------------------------------'; return @messages; } sub datetime() { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); $year += 1900; $mon++; if ( $mon =~ /^\d{1}$/ ) { $mon = '0' . $mon } if ( $mday =~ /^\d{1}$/ ) { $mday = '0' . $mday } if ( $hour =~ /^\d{1}$/ ) { $hour = '0' . $hour } if ( $min =~ /^\d{1}$/ ) { $min = '0' . $min } if ( $sec =~ /^\d{1}$/ ) { $sec = '0' . $sec } return "$year-$mon-$mday $hour:$min:$sec"; } # Begin 5.3 edit - sparris 1 April 2008 sub copy_files(@) { my $source = $_[1]; my $destination = $_[2]; my $result = Files->copy_files( $source, $destination ); return $result; } sub rewrite_nagios(@) { my $source = $_[1]; my $destination = $_[2]; my $result = Files->rewrite_nagios_cfg( $source, $destination ); return $result; } # End 5.3 edit sub commit(@) { %config_settings = %{ $_[1] }; my @commit_results = StorProc->commit( $config_settings{'monarch_home'} ); return @commit_results; } ############################################################################ # Forms for Auto Configuration interface ############################################################################ sub show_import_data(@) { my @file_data = @{ $_[1] }; my $detail .= qq(
); my $class = 'row_dk'; foreach my $line (@file_data) { if ( $class eq 'row_lt' ) { $class = 'row_dk'; } elsif ( $class eq 'row_dk' ) { $class = 'row_lt'; } $detail .= qq( ); } $detail .= qq(
$line
); return $detail; } sub js_utils() { return qq( ); } sub import_schema(@) { my %schema = %{ $_[1] }; my $column_id = $_[2]; my $match_id = $_[3]; my $match_name = $_[4]; my @objects = @{ $_[5] }; my @host_profiles = @{ $_[6] }; my $updated = $_[7]; my $discover_name = $_[8]; my $match_type = $_[9]; my $rule = $_[10]; my $object = $_[11]; my $uri_name = uri_escape( $schema{'name'} ); my %doc = doc(); my $tab = 1; my $docs = undef; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '500', fontsize => '12px' ); my $detail = js_utils(); $detail .= qq(
$schema{'name'}   ); unless ($discover_name) { $detail .= qq(   ); $detail .= qq(   ); } $tab++; $detail .= qq(    ); $tab++; $detail .= qq(    ); $tab++; $detail .= qq(    
An automation schema is an import/update data mapping tool that can be applied to any data source from which a text delimited file can be extracted.
 
Description: ); $tab++; my $description = $schema{'description'}; $description = '' if not defined $description; $description = HTML::Entities::encode($description); my $lines = ( ( (my @lines) = split( /\n/, $description, 20 ) ) || 1 ) + 1; foreach my $line (@lines) { $lines += int (length($line) / 100); } $detail .= qq(
Schema type: $schema{'type'}
); $tab++; if ( $schema{'type'} eq 'other-sync' ) { $detail .= qq(
Primary Sync Object

Select the primary object type on which to assign child objects. For example, if your data lists parents by host, select Host as the sync object. You can choose to sync with host name, host address, or host alias.
Primary sync object:
); } else { my $smart_name = $schema{'smart_name'} ? 'checked' : ''; $detail .= qq(
Smart Names Option

Select this option to satisfy required Name, Address, and Alias values from limited data. If after processing one or more values are left undefined, this option will substitute a defined value. The order of substitution for Name is Alias then Address, for Address is Alias then Name, and for Alias is Address then Name.
Use smart names:
); } my $class = ''; if ($discover_name) { $detail .= qq( ); } else { $options{'title'} = 'Data Source'; $options{'width'} = 800; $options{'left'} = '1'; $docs = "\n$doc{'data-source'}"; my $tooltip = $tt->tooltip( $docs, \%options ); delete $options{'left'}; $tab++; my $data_source = $schema{'data_source'}; $data_source = '' if not defined $data_source; $data_source = HTML::Entities::encode($data_source); $detail .= qq(
Data source:   ); $tab++; $detail .= qq(   ?
Delimiter: ); $tab++; $detail .= qq(   Other: ); $tab++; $other = '' if not defined $other; $other = HTML::Entities::encode($other); $detail .= qq(  ?
); } $tab++; unless ( $schema{'type'} eq 'other-sync' ) { $detail .= qq(
Default Host Profile

Required for schema type host-profile-sync. Select a default host profile to ensure any host record can be imported with a working configuration.
Default host profile:
); } $detail .= qq( ); } else { $detail .= qq(
Data columns:
); my %columns = (); foreach my $key ( keys %{ $schema{'column'} } ) { if ($key) { $columns{ $schema{'column'}{$key}{'position'} }{'name'} = $schema{'column'}{$key}{'name'}; $columns{ $schema{'column'}{$key}{'position'} }{'id'} = $key; } } my $arrow = ' '; foreach my $pos ( sort { $a <=> $b } keys %columns ) { unless ($pos) { next } unless ($column_id) { $column_id = $columns{$pos}{'id'} } if ( $columns{$pos}{'id'} eq $column_id ) { $class = 'match_selected'; ## $arrow = '>'; } else { $class = 'column'; ## $arrow = ' '; } $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); } $tab++; $detail .= qq(
Position Column name  
); $options{'title'} = 'Define Column'; $options{'width'} = 500; $docs = "\nEnter the position and name of the new column definition.

Position: $doc{'column'}{'position'}

\nColumn name: $doc{'column'}{'name'}

"; my $tooltip = $tt->tooltip( $docs, \%options ); $tab++; $detail .= qq( ); $tab++; $detail .= qq(
Define Column
  ?
); my %order = (); foreach my $key ( keys %{ $schema{'column'}{$column_id}{'match'} } ) { $order{ $schema{'column'}{$column_id}{'match'}{$key}{'order'} }{'name'} = $schema{'column'}{$column_id}{'match'}{$key}{'name'}; $order{ $schema{'column'}{$column_id}{'match'}{$key}{'order'} }{'id'} = $key; } foreach my $pos ( sort { $a <=> $b } keys %order ) { unless ($pos) { next } unless ($match_id) { $match_id = $order{$pos}{'id'}; ## FIX THIS: Do we now also need to wipe out these variables, so they don't mistakenly take ## bad values from the function parameters but instead perhaps get new values assigned below? ## $object, $match_type, $rule, $match_name } if ( $order{$pos}{'id'} eq $match_id ) { $class = 'match_selected'; } else { $class = 'match'; } $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); } if ($column_id) { $detail .= qq(
Order Match task name  
$pos
); $tab++; $detail .= qq( ); $options{'title'} = 'Define Match Task'; $options{'left'} = '1'; $docs = "\nEnter the order and name of the new match definition.

Order: $doc{'match'}{'order'}

\n

Match task name: $doc{'match'}{'name'}

"; my $tooltip = $tt->tooltip( $docs, \%options ); delete $options{'left'}; $tab++; $detail .= qq( ); $tab++; $detail .= qq(
Define Match Task
  ?
 
); } $detail .= qq( ); my $form_match = 'match_selected'; if (defined $match_id) { $object = $schema{'column'}{$column_id}{'match'}{$match_id}{'object'} unless defined($object); $match_type = $schema{'column'}{$column_id}{'match'}{$match_id}{'match_type'} unless defined($match_type); $rule = $schema{'column'}{$column_id}{'match'}{$match_id}{'rule'} unless defined($rule); } my @match_types = ( 'use-value-as-is', 'is-null', 'exact', 'begins-with', 'ends-with', 'contains', 'use-perl-reg-exp', 'service-definition' ); $detail .= qq(
Match Task Detail
); if ($updated) { $detail .= qq(
$updated
); } elsif ($match_id) { unless ($match_name) { $match_name = $schema{'column'}{$column_id}{'match'}{$match_id}{'name'}; } $options{'title'} = 'Match Order'; $docs = "\nOrder: $doc{'match'}{'order'}
"; my $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); $options{'title'} = 'Match Task Name'; $options{'width'} = 650; $options{'left'} = '1'; $docs = "\nName: $doc{'match'}{'name'}
"; $tooltip = $tt->tooltip( $docs, \%options ); delete $options{'left'}; $detail .= qq( ); $detail .= qq( ); $options{'title'} = 'Match Types'; $docs = 'Select the match type to determine what part of the data record to match. All matches are case insensitive.
'; my $select_options = ''; foreach my $match_item (@match_types) { if ( $match_item eq $match_type ) { $select_options .= "\n"; } else { $select_options .= "\n"; } $docs .= "\n

$match_item: $doc{'type'}{$match_item}

"; } $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); my $match_string = $schema{'column'}{$column_id}{'match'}{$match_id}{'match_string'}; $match_string = '' if not defined $match_string; if ( $match_type =~ /exact|begins-with|ends-with|contains|use-perl-reg-exp/ ) { $options{'title'} = 'Match String'; $options{'width'} = 600; $options{'left'} = '1'; if ( $match_type eq 'use-perl-reg-exp' ) { $docs = "\nuse-perl-reg-exp: $doc{'string'}{'use-perl-reg-exp'}"; } else { $docs = "\n$match_type: $doc{'string'}{'other'}
"; } $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); delete $options{'left'}; } elsif ( $match_type eq 'service-definition' ) { $options{'title'} = 'Service Delimiter'; $options{'width'} = 800; $options{'left'} = '1'; $docs = "\nservice-delimiter: $doc{'string'}{'service-delimiter'}"; $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); delete $options{'left'}; } else { $detail .= qq( ); } unless ( $match_type eq 'service-definition' ) { $detail .= qq( ); my @rules = (); if ( $schema{'type'} eq 'host-profile-sync' ) { if ( $match_type =~ /exact|begins-with|ends-with|contains|use-perl-reg-exp/ ) { @rules = ( 'Assign object(s)', 'Add if not exists and assign object', 'Assign value to', 'Assign value if undefined', 'Assign host profile if undefined', 'Convert dword and assign to', 'Skip column record', 'Discard record', 'Discard if match existing host' ); } elsif ( $match_type eq 'is-null' ) { @rules = ( 'Assign object(s)', 'Skip column record', 'Discard record' ); } else { @rules = ( 'Add if not exists and assign object', 'Assign object if exists', 'Assign value to', 'Assign value if undefined', 'Assign host profile if undefined', 'Convert dword and assign to', 'Discard if match existing host', 'Resolve to parent' ); } } else { if ( $match_type =~ /exact|begins-with|ends-with|contains|use-perl-reg-exp/ ) { @rules = ( 'Assign object(s)', 'Add if not exists and assign object', 'Assign value to', 'Assign value if undefined', 'Assign host profile if undefined', 'Assign host profile', 'Assign service', 'Convert dword and assign to', 'Skip column record', 'Discard record', 'Discard if match existing host' ); } elsif ( $match_type eq 'is-null' ) { @rules = ( 'Assign object(s)', 'Assign host profile', 'Discard record' ); } else { @rules = ( 'Add if not exists and assign object', 'Assign object if exists', 'Assign value to', 'Assign value if undefined', 'Assign host profile if undefined', 'Convert dword and assign to', 'Discard if match existing host', 'Resolve to parent' ); } } $options{'title'} = 'Rules and Objects'; $options{'width'} = 750; $docs = 'Select the appropriate rule to determine how the match should be applied.
'; my $select_options = ''; foreach my $rule_item (@rules) { if ( $rule_item eq $rule ) { $select_options .= "\n"; } else { $select_options .= "\n"; } $docs .= "\n

$rule_item: $doc{'rule'}{$rule_item}

"; } $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); my @obj_types = (); my $schema_rule = $schema{'column'}{$column_id}{'match'}{$match_id}{'rule'}; if ( $schema{'type'} eq 'host-profile-sync' ) { if ( $schema_rule =~ /Assign object/ ) { @obj_types = ( 'Parent', 'Group', 'Host group', 'Service profile', 'Contact group' ); } elsif ( $schema_rule eq 'Add if not exists and assign object' ) { @obj_types = ( 'Contact group', 'Group', 'Host group' ); } elsif ( $schema_rule =~ /discard/i ) { @obj_types = (); } elsif ( $schema_rule =~ /Assign value to|Assign value if undefined|Assign host profile if undefined|Apply|Convert dword and assign to/ ) { @obj_types = ( 'Primary record', 'Name', 'Address', 'Alias', 'Description' ); } } elsif ( $schema{'type'} eq 'other-sync' ) { if ( $schema_rule =~ /Assign object/ ) { @obj_types = ( 'Parent', 'Group', 'Host group', 'Service profile', 'Contact group' ); } elsif ( $schema_rule eq 'Add if not exists and assign object' ) { @obj_types = ( 'Contact group', 'Group', 'Host group' ); } elsif ( $schema_rule =~ /discard|resolve parent/i ) { @obj_types = (); } else { @obj_types = ( 'Primary record', 'Name', 'Address', 'Alias', 'Description' ); } } elsif (defined $schema_rule) { if ( $schema_rule eq 'Assign object if exists' ) { @obj_types = ( 'Contact group', 'Group', 'Host group', 'Host profile', 'Parent', 'Service profile', 'Service' ); } elsif ( $schema_rule =~ /Assign object/ ) { @obj_types = ( 'Contact group', 'Group', 'Host group', 'Parent', 'Service profile' ); } elsif ( $schema_rule eq 'Add if not exists and assign object' ) { @obj_types = ( 'Contact group', 'Group', 'Host group' ); } elsif ( $schema_rule =~ /discard/i ) { @obj_types = (); } elsif ( $schema_rule =~ /Assign value to|Assign value if undefined|Assign host profile if undefined|Apply|Convert dword and assign to/ ) { @obj_types = ( 'Primary record', 'Name', 'Address', 'Alias', 'Description', 'Host profile', ); } } my %obj_name = ( 'Host group' => 'hostgroups', 'Contact group' => 'contactgroups', 'Group' => 'groups', 'Parent' => 'parents', 'Service profile' => 'serviceprofiles' ); if ( $rule eq 'Assign host profile' || $rule eq 'Assign host profile if undefined' ) { $detail .= qq( ); } elsif ( $rule eq 'Assign service' ) { $detail .= qq( ); } elsif (@obj_types) { ## FIX THIS: This is showing up sometimes for some Rule choices when it does not belong on-screen, ## simply because some stuff is being carried forward from previous screens; it needs more ## qualification before being displayed. It may be because one needs to press Update first ## after changing the Rule, before the old garbage state is cleared out. Then again, more ## testing is needed before we can draw any solid conclusions. $detail .= qq( ); # FIX THIS: should this include the 'Add if not exists and assign object' rule as well? if ( $rule eq 'Assign object(s)' && defined($object) && $object ne '' ) { my $objects_size = scalar @objects; my $list_size = $objects_size <= 20 ? $objects_size : 20; $list_size = $list_size >= 3 ? $list_size : 3; $detail .= qq( ); } $detail .= qq(
Order:  ? Match name:  ?
Match:  ? Match string:  ? Service delimiter:  ?    
Rule:  ? Host profile: Service name: ); $detail .= "\n"; @objects = sort { lc($a) cmp lc($b) } @objects; foreach my $object_item (@objects) { my $service_name = $schema{'column'}{$column_id}{'match'}{$match_id}{'service_name'}; if ( defined($service_name) && $object_item eq $service_name ) { $detail .= "\n"; } else { $detail .= "\n"; } } $detail .= qq(
Object:
    $object:
); } } $detail .= qq(
); } $detail .= $tt->at_end; $tab++; $detail .= qq( ); return $detail; } sub import_form(@) { my $name = $_[1]; my %import_data = %{ $_[2] }; my %objects = %{ $_[3] }; my %overrides = %{ $_[4] }; my $sort_by = $_[5]; my $sort_on = $_[6]; my $select_on = $_[7]; my %record = %{ $_[8] }; my $show_overrides = $_[9]; my %service_objs = %{ $_[10] }; my %overview = %{ $_[11] }; my $scroll_px = '600px'; $select_on = '' if not defined $select_on; if ($show_overrides) { $scroll_px = '300px' } my %sort_order = (); use URI::Escape; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'No value is specified.', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '450', fontsize => '12px' ); my $debug = 0; unless ($sort_by) { $sort_by = 'Primary record' } unless ($sort_on) { $sort_on = 'exception' } my $i = 1; $name = uri_escape($name); foreach my $rec ( keys %import_data ) { unless ($rec) { next } if ( $import_data{$rec}{'exception'} ) { if ( $sort_by eq 'Primary record' ) { $sort_order{'exception'}{$rec} = $rec; } elsif ( $import_data{$rec}{$sort_by} ) { $sort_order{'exception'}{ $import_data{$rec}{$sort_by} } = $rec; } else { $sort_order{'exception'}{"n/a $i"} = $rec; $i++; } } elsif ( $import_data{$rec}{'exists'} ) { if ( $sort_by eq 'Primary record' ) { $sort_order{'exists'}{$rec} = $rec; } elsif ( $import_data{$rec}{$sort_by} ) { $sort_order{'exists'}{ $import_data{$rec}{$sort_by} } = $rec; } else { $sort_order{'exists'}{"n/a $i"} = $rec; $i++; } } elsif ( $import_data{$rec}{'new_parent'} ) { if ( $sort_by eq 'New parent' ) { $sort_order{'new_parent'}{$rec} = $rec; } elsif ( $import_data{$rec}{$sort_by} ) { $sort_order{'new_parent'}{ $import_data{$rec}{$sort_by} } = $rec; } else { $sort_order{'new_parent'}{"n/a $i"} = $rec; $i++; } } elsif ( $import_data{$rec}{'delete'} ) { if ( $sort_by eq 'Primary record' ) { $sort_order{'delete'}{$rec} = $rec; } elsif ( $import_data{$rec}{$sort_by} ) { $sort_order{'delete'}{ $import_data{$rec}{$sort_by} } = $rec; } else { $sort_order{'delete'}{"n/a $i"} = $rec; $i++; } } else { if ( $sort_by eq 'Primary record' ) { $sort_order{'new'}{$rec} = $rec; } elsif ( $import_data{$rec}{$sort_by} ) { $sort_order{'new'}{ $import_data{$rec}{$sort_by} } = $rec; } else { $sort_order{'new'}{"n/a $i"} = $rec; $i++; } } if ($debug) { if ( $import_data{$rec}{'delete'} ) { $debug .= "
$import_data{$rec}{'Name'} e $import_data{$rec}{'exists'} n $import_data{$rec}{'new'}"; } # $debug .= qq( #
$rec $sort_by --- $import_data{$rec}{$sort_by} --- $import_data{$rec}{'Name'}); } } my $class = 'row_dk'; my $class_top = 'row_dk_top'; my $class_status = 'row_good'; my %doc = doc(); my $detail = js_utils(); if ($debug) { $detail .= qq(
$debug
); } $detail .= qq(
$overview{'records'}            
$overview{'overview'}
); $options{'title'} = 'New Parent'; $docs = "\n$doc{'process'}{'New Parent'}"; $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); $options{'title'} = 'New Host'; $docs = "\n$doc{'process'}{'New Host'}"; $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); $options{'title'} = 'Host Exists'; $docs = "\n$doc{'process'}{'Host Exists'}"; $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); $options{'title'} = 'Exception'; $docs = "\n$doc{'process'}{'Exception'}"; $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq( ); $options{'title'} = 'Delete Host'; $docs = "\n$doc{'process'}{'Delete Host'}"; $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq(
 Sort by:    ? Select:
?
?
?
?
?
); my @sorter = ( 'exception', 'delete', 'exists', 'new_parent', 'new' ); if ( $sort_on eq 'exists' ) { @sorter = ( 'exists', 'new_parent', 'new', 'exception', 'delete' ); } elsif ( $sort_on eq 'new' ) { @sorter = ( 'new', 'exception', 'delete', 'exists', 'new_parent' ); } elsif ( $sort_on eq 'new_parent' ) { @sorter = ( 'new_parent', 'new', 'exception', 'delete', 'exists' ); } elsif ( $sort_on eq 'delete' ) { @sorter = ( 'delete', 'exists', 'new_parent', 'new', 'exception' ); } my $display_recs = 0; my $possible_recs = 0; foreach my $sorter (@sorter) { foreach my $value ( sort { $a cmp $b } keys %{ $sort_order{$sorter} } ) { $possible_recs++; if ( $display_recs >= 100 ) { last } $display_recs++; unless ($value) { next } my $checked = ''; my $label = ''; my $rec = $sort_order{$sorter}{$value}; my $uri_rec = uri_escape($rec); if ( $sorter eq 'exception' ) { $class = 'row_lt'; $class_top = 'top_exception'; $class_status = 'row_exception'; $label = 'Exception'; if ( ( $select_on ne '' && $sort_on eq 'exception' ) || ( $select_on eq '' && defined( $record{$rec} ) ) ) { $checked = 'checked' } } elsif ( $sorter eq 'new_parent' ) { $class = 'row_lt'; $class_top = 'top_new_parent'; $class_status = 'row_new_parent'; $label = 'New Parent'; if ( ( $select_on ne '' && $sort_on eq 'new_parent' ) || ( $select_on eq '' && defined( $record{$rec} ) ) ) { $checked = 'checked' } } elsif ( $sorter eq 'new' ) { $class = 'row_lt'; $class_top = 'top_new'; $class_status = 'row_new'; $label = 'New Host'; if ( ( $select_on ne '' && $sort_on eq 'new' ) || ( $select_on eq '' && defined( $record{$rec} ) ) ) { $checked = 'checked' } } elsif ( $sorter eq 'delete' ) { $class = 'row_lt'; $class_top = 'top_delete'; $class_status = 'row_delete'; $label = 'Delete'; if ( ( $select_on ne '' && $sort_on eq 'delete' ) || ( $select_on eq '' && defined( $record{$rec} ) ) ) { $checked = 'checked' } } else { $class = 'row_lt'; $class_top = 'top_exists'; $class_status = 'row_exists'; $label = 'Exists'; if ( ( $select_on ne '' && $sort_on eq 'exists' ) || ( $select_on eq '' && defined( $record{$rec} ) ) ) { $checked = 'checked' } } my $uriesc_hostname = uri_escape( $import_data{$rec}{'Name'} ); my $uriesc_address = uri_escape( $import_data{$rec}{'Address'} ); my $uriesc_alias = uri_escape( $import_data{$rec}{'Alias'} ); my $uriesc_hostprofile = uri_escape( $import_data{$rec}{'Host profile'} ); my $uriesc_description = uri_escape( $import_data{$rec}{'Description'} ); my $hostname = $import_data{$rec}{'Name'} || '(none)'; my $address = $import_data{$rec}{'Address'} || '(none)'; my $host_profile = $import_data{$rec}{'Host profile'} || '(none)'; $uriesc_address = '' if not defined $uriesc_address; $uriesc_description = '' if not defined $uriesc_description; if ( $import_data{$rec}{'delete'} ) { $detail .= qq( ); } else { $detail .= qq( ); if ( !defined( $import_data{$rec}{'Alias'} ) || $import_data{$rec}{'Alias'} eq '' ) { $detail .= qq( ); } else { $options{'title'} = 'Alias'; delete $options{'left'}; my $tt_alias = $tt->tooltip( $import_data{$rec}{'Alias'}, \%options ); $detail .= qq( ); } $options{'left'} = "1"; $detail .= qq( ); if ( $import_data{$rec}{'Service profile'} ) { my $groups = undef; foreach my $profile ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Service profile'} } ) { my $uriesc_p = uri_escape($profile); $groups .= "$profile
"; $detail .= qq( ); } $options{'title'} = 'Service profiles'; my $g_list = $tt->tooltip( $groups, \%options ); $detail .= qq( ); } else { $detail .= qq( ); } my $services = undef; if ( $import_data{$rec}{'Service'} ) { foreach my $service ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Service'} } ) { unless ( $service && $import_data{$rec}{'Service'}{$service} ) { next; } my $uriesc_svc = uri_escape($service); my $uriesc_arg = uri_escape( $import_data{$rec}{'Service'}{$service}{'command_line'} ); $uriesc_arg = '' if not defined $uriesc_arg; $detail .= qq( ); my $cnt = 0; my %sorted = (); foreach my $instance ( keys %{ $import_data{$rec}{'Service'}{$service}{'instances'} } ) { my $key = $instance; $key =~ s/^_//; $sorted{$key} = $instance; } foreach my $key ( sort { $a <=> $b } keys %sorted ) { my $uriesc_inst = uri_escape( $sorted{$key} ); if ( $cnt ) { $services .= ", "; if ( $cnt % 3 == 0 ) { $services .= "
"; } } ++$cnt; $services .= "$service$sorted{$key}"; my $uriesc_arg = uri_escape( $import_data{$rec}{'Service'}{$service}{'instances'}{ $sorted{$key} }{'arguments'} ); my $status = $import_data{$rec}{'Service'}{$service}{'instance'}{ $sorted{$key} }{'status'}; $detail .= qq( ); } if ( keys %{ $import_data{$rec}{'Service'}{$service}{'instances'} } ) { $services .= "
"; } else { $services .= "$service
"; } } } if ($services) { $options{'title'} = 'Discovered Services'; my $g_list = $tt->tooltip( $services, \%options ); $detail .= qq( ); } else { $detail .= qq( ); } if ( $import_data{$rec}{'Parent'} ) { my $groups = undef; foreach my $parent ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Parent'} } ) { my $uriesc_p = uri_escape($parent); $groups .= "$parent
"; $detail .= qq( ); } $options{'title'} = 'Parents'; my $g_list = $tt->tooltip( $groups, \%options ); $detail .= qq( ); } else { $detail .= qq( ); } if ( $import_data{$rec}{'Host group'} ) { my $groups = undef; foreach my $hostgroup ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Host group'} } ) { my $uriesc_hg = uri_escape($hostgroup); $groups .= "$hostgroup
"; $detail .= qq( ); } $options{'title'} = 'Host Groups'; my $g_list = $tt->tooltip( $groups, \%options ); $detail .= qq( ); } else { $detail .= qq( ); } my $new_parent = $import_data{$rec}{'new_parent'}; $new_parent = '' if not defined $new_parent; my $description = $import_data{$rec}{'Description'}; $description = '' if not defined $description; $detail .= qq( ); if ( $import_data{$rec}{'Group'} ) { my $groups = undef; foreach my $group ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Group'} } ) { my $uriesc_g = uri_escape($group); $groups .= "$group
"; $detail .= qq( ); } $options{'title'} = 'Groups'; my $g_list = $tt->tooltip( $groups, \%options ); $detail .= qq( ); } else { $detail .= qq( ); } if ( $import_data{$rec}{'Contact group'} ) { my $groups = undef; foreach my $group ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Contact group'} } ) { my $uriesc_g = uri_escape($group); $groups .= "$group
"; $detail .= qq( ); } $options{'title'} = 'Contact Groups'; my $g_list = $tt->tooltip( $groups, \%options ); $detail .= qq( ); } else { $detail .= qq( ); } $detail .= qq( ); } } } $detail .= qq(
 Record: $rec  $label
 Name:  $hostname Host flagged for deletion
 
 Record: $rec  $label
 Name: $hostname Alias:  (none) Alias Host profile: $host_profile Service profilesService profiles Discovered ServicesDiscovered ServicesParentsParentsHost groupsHost groups
 Address: $address Description: $description GroupsGroupsContact groupsContact groups
 
); if ( $possible_recs > $display_recs ) { $detail .= qq(
The set above is limited to the first $display_recs records. Additional records are also available, and will be displayed once these are processed. The Sort and Select criteria above can also be used to change the order of displayed records and bring others into view.
); } my $javascript = qq( ); } else { $options{'title'} = 'Enable Overrides'; $docs = "\n$doc{'overrides'}{'enable'}"; my $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq(
  ?      
); } $detail .= $tt->at_end; $detail .= qq( ); $javascript .= "\n"; return $javascript . $detail; } sub import_edit(@) { my $record_esc = $_[1]; my %host_data = %{ $_[2] }; my %objects = %{ $_[3] }; my %service_objs = %{ $_[4] }; my $got_selected = undef; my $record = uri_unescape($record_esc); my $tab = 0; my $javascript = qq(
 Record: $record
); if ( $host_data{'exists'} ) { $detail .= qq( ); } else { $detail .= qq( ); } $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); $tab++; $detail .= qq(
Host name: $host_data{'Name'}
Address:
Alias:
Description:
Host profile:
Service profiles:
Parents:
Groups:
Host groups:
Contact groups:
Discovered and Additional Services and Instances
); my $got_services = 0; $got_selected = 0; foreach my $service ( sort { $a cmp $b } keys %{ $host_data{'Service'} } ) { unless ($service) { next } $got_services = 1; my $class = $form_class; my $button_class = 'column'; $tab++; if ( $service_objs{$service}{'service_selected'} ) { $got_selected = $service; $button_class = 'match_selected'; $class = 'match_selected'; $detail .= qq( ); } else { $detail .= qq( ); } $detail .= qq( ); my $arguments = $service_objs{$service}{'arguments'}; $arguments = '' if not defined $arguments; if ( $service_objs{$service}{'service_selected'} && !( keys %{ $service_objs{$service}{'instances'} } ) ) { $tab++; $detail .= qq( ); } else { $detail .= qq( ); } $tab++; $detail .= qq( ); my @alph_sorted = (); my @num_sorted = (); my %instance_sort = (); foreach my $instance ( keys %{ $service_objs{$service}{'instances'} } ) { my $inst = $instance; $inst =~ s/^_//; $inst .= rand(); if ( $inst =~ /^\d+/ ) { push @num_sorted, $inst; } else { push @alph_sorted, $inst; } $instance_sort{$inst}{'name'} = $instance; } foreach my $inst ( sort { $a <=> $b } @num_sorted ) { my $instance = $instance_sort{$inst}{'name'}; $detail .= qq( ); if ( $service_objs{$service}{'service_selected'} ) { $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); } else { $tab++; $detail .= qq( ); } } foreach my $inst ( sort { lc($a) cmp lc($b) } @alph_sorted ) { my $instance = $instance_sort{$inst}{'name'}; $detail .= qq( ); if ( $service_objs{$service}{'service_selected'} ) { $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); } else { $tab++; $detail .= qq( ); } } } if ( $got_services && $got_selected ) { $javascript .= qq( window.onload = function() { document.form.instance_add.focus(); } ); } else { $javascript .= qq( window.onload = function() { document.form.add_instance.className = 'submitbutton_disabled'; document.form.add_instance.disabled = true; document.form.instance_add.disabled = true; document.form.address.focus(); } ); } unless ($got_services) { $detail .= qq( ); } $tab++; $detail .= qq(
  Service name Check command Arguments  
$service $service_objs{$service}{'check_command'}  $arguments
  $service$instance $service_objs{$service}{'check_command'}
$service_objs{$service}{'instances'}{$instance}
  $service$instance $service_objs{$service}{'check_command'}
$service_objs{$service}{'instances'}{$instance}
No services have been assigned.
); $tab++; $detail .= qq(
Service:  
Service Name Instance Suffix:  
); return $javascript . $detail; } sub import_sync_other(@) { my %schema = %{ $_[1] }; my %import_data = %{ $_[2] }; my %hosts_vitals = StorProc->get_hosts_vitals(); my $detail = qq(
); foreach my $rec ( sort { $a cmp $b } keys %import_data ) { if ( $schema{'sync_object'} eq 'Host' ) { if ( $import_data{$rec}{'Name'} ) { if ( $hosts_vitals{'name'}{ $import_data{$rec}{'Name'} } ) { my $uriesc_val = uri_unescape( $import_data{$rec}{'Name'} ); $detail .= qq( ); } else { $detail .= qq( ); } } elsif ( $import_data{$rec}{'Address'} ) { if ( $hosts_vitals{'address'}{ $import_data{$rec}{'Address'} } ) { my $uriesc_val = uri_unescape( $import_data{$rec}{'Address'} ); $detail .= qq( ); } else { $detail .= qq( ); } } elsif ( $import_data{$rec}{'Alias'} ) { if ( $hosts_vitals{'alias'}{ $import_data{$rec}{'Alias'} } ) { my $uriesc_val = uri_unescape( $import_data{$rec}{'Alias'} ); $detail .= qq( ); } else { $detail .= qq( ); } } } else { if ( $schema{'sync_object'} eq 'Group' && $import_data{$rec}{'Group'} ) { ## FIX THIS: what sense does it make to stringify a hash reference? my $uriesc_val = uri_unescape( $import_data{$rec}{'Group'} ); $detail .= qq( ); } elsif ( $schema{'sync_object'} eq 'Host group' && $import_data{$rec}{'Host group'} ) { ## FIX THIS: what sense does it make to stringify a hash reference? my $uriesc_val = uri_unescape( $import_data{$rec}{'Host group'} ); $detail .= qq( ); } elsif ( $schema{'sync_object'} eq 'Parent' ) { ## FIX THIS: what sense does it make to stringify a hash reference? my $uriesc_val = uri_unescape( $import_data{$rec}{'Parent'} ); if ( $hosts_vitals{'name'}{$uriesc_val} || $hosts_vitals{'address'}{$uriesc_val} || $hosts_vitals{'alias'}{$uriesc_val} ) { $detail .= qq( ); } else { $detail .= qq( ); } } elsif ( $schema{'sync_object'} eq 'Contact group' && $import_data{$rec}{'Contact group'} ) { ## FIX THIS: what sense does it make to stringify a hash reference? my $uriesc_val = uri_unescape( $import_data{$rec}{'Contact group'} ); $detail .= qq( ); } else { $detail .= qq( ); } } if ( $import_data{$rec}{'Group'} ) { $detail .= qq( ); } if ( $import_data{$rec}{'Host group'} ) { $detail .= qq( ); } if ( $import_data{$rec}{'Parent'} ) { $detail .= qq( ); } if ( $import_data{$rec}{'Service profile'} ) { $detail .= qq( ); } if ( $import_data{$rec}{'Contact group'} ) { $detail .= qq( ); } $detail .= qq( ); } $detail .= qq(
Name: $import_data{$rec}{'Name'}
Error: Record $rec Host with name $import_data{$rec}{'Name'} does not exist. Nothing to update.
Address: $import_data{$rec}{'Address'}
Error: $rec Host with address $import_data{$rec}{'Address'} does not exist. Nothing to update.
Alias: $import_data{$rec}{'Alias'}
Error: $rec Host with alias $import_data{$rec}{'Alias'} does not exist. Nothing to update.
Group: $import_data{$rec}{'Group'}
Host group: $import_data{$rec}{'Host group'}
Parent: $import_data{$rec}{'Parent'}
Error: Record $rec Parent $uriesc_val does not exist. Nothing to update.
Contact group: $import_data{$rec}{'Contact group'}
Error: Insuffecient data to import.
); foreach my $group ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Group'} } ) { my $uriesc_g = uri_escape($group); $detail .= qq( ); } $detail .= qq(
$group
); foreach my $hostgroup ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Host group'} } ) { my $uriesc_hg = uri_escape($hostgroup); $detail .= qq( ); } $detail .= qq(
$hostgroup
); if ( $import_data{$rec}{'Parent'} ) { foreach my $parent ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Parent'} } ) { my $uriesc_p = uri_escape($parent); if ( $hosts_vitals{'name'}{$uriesc_p} || $hosts_vitals{'address'}{$uriesc_p} || $hosts_vitals{'alias'}{$uriesc_p} ) { $detail .= qq( ); } else { $detail .= qq( ); } } } else { $detail .= qq( ); } $detail .= qq(
Parent: $parent
Error: Record $rec Parent $parent does not exist. Nothing to update.
 
); foreach my $profile ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Service profile'} } ) { my $uriesc_p = uri_escape($profile); $detail .= qq( ); } $detail .= qq(
$profile
); foreach my $group ( sort { $a cmp $b } keys %{ $import_data{$rec}{'Contact group'} } ) { my $uriesc_g = uri_escape($group); $detail .= qq( ); } $detail .= qq(
$group
); return $detail; } sub select_schema(@) { my %list = %{ $_[1] }; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '500', fontsize => '12px' ); my %doc = doc(); my $detail = qq(
Select or define an automation schema
An automation schema is an import/update data mapping tool that can be applied to any data source from which a text delimited file can be extracted. The automation schema types are:
  ); $options{'title'} = 'Host Import Schema'; my $s_detail = $tt->tooltip( "host-import

$doc{'host-import'}

", \%options ); $detail .= qq( host-import
  ); $options{'title'} = 'Host Profile Sync Schema'; $s_detail = $tt->tooltip( "host-profile-sync

$doc{'host-profile-sync'}

", \%options ); $detail .= qq( host-profile-sync
  ); $options{'title'} = 'Other Sync Schema'; $s_detail = $tt->tooltip( "other-sync

$doc{'other-sync'}

", \%options ); $detail .= qq( other-sync); $detail .= $tt->at_end; $detail .= qq(
); my $class = 'row_dk'; foreach my $name ( sort keys %list ) { if ( $class eq 'row_dk' ) { $class = 'row_lt'; } else { $class = 'row_dk'; } $detail .= qq( ); } $detail .= qq(
Select Automation Schema Name Type Description
$name $list{$name}{'type'} $list{$name}{'description'}
); return $detail; } sub select_nms() { my %nms_opts = %{ $_[1] }; my $tab = 0; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '600', fontsize => '12px' ); my $detail = qq(
NMS Configuration Integration
Select the appropriate option to update the GroundWork configuration with data from an NMS application or other data source.
); if ( $nms_opts{'cacti_sync'} ) { $detail .= qq(
Cacti host profile sync Use Cacti Sync to update the host list in the Cacti host profile in GroundWork Configuration. Hosts found in the configuration database but not found in the Cacti data source are flagged for deletion. The Cacti host profiles sync can be fully ...); my $tt_doc = qq(

The Cacti host profile sync can be fully automated. The process is broken down into two steps: extract the data from Cacti and import the data into GroundWork Configuration.

• The script to extract data from Cacti is:
   /usr/local/groundwork/core/monarch/automation/scripts/extract_cacti.pl
   Please note that the database authentication information for Cacti resides in the script.

• The script to import data into GroundWork Configuration is:
   /usr/local/groundwork/core/monarch/automation/scripts/auto_import_cacti.pl Cacti

   Please note the the schema name is set in the auto_import_cacti.pl script.

); $options{'title'} = 'Automation'; $options{'width'} = 750; my $s_detail = $tt->tooltip( "Cacti host profile sync
$tt_doc", \%options ); $detail .= qq( automated.
); } if ( $nms_opts{'nedi_sync'} ) { $detail .= qq(
NeDi parent-child sync Use NeDi Sync to update the host parent-child relationships in GroundWork Configuration. Both parent and child hosts must be present in the GroundWork configuration database. To import new hosts from NeDi data, use NeDi Import. The NeDi parent-child sync can be fully ...); my $tt_doc = qq(

The NeDi parent-child sync can be fully automated. The process is broken down into two steps: extract the data from NeDi and import the data into GroundWork Configuration.

• The script to extract data from NeDi is:
   /usr/local/groundwork/core/monarch/automation/scripts/extract_nedi.pl
   Please note that the database authentication information for NeDi resides in the script.

• The script to import data into GroundWork Configuration is:
   /usr/local/groundwork/core/monarch/automation/scripts/auto_import_nedi_sync.pl NeDi-parent-child-sync
   Please note the schema name is set in the auto_import_nedi_sync.pl script.

); $options{'title'} = 'Automation'; my $s_detail = $tt->tooltip( "NeDi parent-child sync
$tt_doc", \%options ); $detail .= qq( automated.
NeDi host import Use NeDi host import to update the GroundWork Configuration with data from NeDi. The NeDi host import can be fully ...); $tt_doc = qq(

The NeDi host import can be fully automated. The process is broken down into two steps: extract the data from NeDi and import the data into GroundWork Configuration.

• The script to extract data from NeDi is:
   /usr/local/groundwork/core/monarch/automation/scripts/extract_nedi.pl
   Please note that the database authentication information for NeDi resides in the script.

• The script to import data into GroundWork Configuration is:
   /usr/local/groundwork/core/monarch/automation/scripts/auto_import_nedi_host.pl NeDi-host-import
   Please note the schema name is set in the auto_import_nedi_host.pl script.

); $options{'title'} = 'Automation'; $s_detail = $tt->tooltip( "NeDi host import
$tt_doc", \%options ); $detail .= qq( automated.
); } $detail .= qq(
Import data from another data source Create and select your own schema to import data from any text delimited data source.
); $detail .= $tt->at_end; return $detail; } sub discover_home(@) { my %groups = %{ $_[1] }; my $tab = 0; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '500', fontsize => '12px' ); $options{'title'} = 'Discovery Definitions'; my $docs = "\nDiscovery definitions specify methods to find network devices and servers connected to your network. Discovery definitions can be created to match your particular needs and environment."; my $tooltip = $tt->tooltip( $docs, \%options ); my $detail = js_utils(); $detail .= qq(
Discovery Definitions  ?            
); my $form_match = 'match_selected'; my $row_class = 'row_dk'; foreach my $name ( sort keys %groups ) { unless ($name) { next } my $radio = 'radio'; my %selected = (); my %auto = (); if ( $row_class eq 'row_lt' ) { $row_class = 'row_dk'; } elsif ( $row_class eq 'row_dk' ) { $row_class = 'row_lt'; } my $class = $row_class; $radio = $class; if ( $groups{$name}{'selected'} ) { $class = $form_match; $radio = 'radio_orange'; $selected{$name} = 'checked'; $auto{ $groups{$name}{'auto'} } = 'checked'; } else { $selected{$name} = ''; } my $auto = $auto{'Auto'}; my $auto_commit = $auto{'Auto-Commit'}; my $interactive = $auto{'Interactive'}; $auto = '' if not defined $auto; $auto_commit = '' if not defined $auto_commit; $interactive = '' if not defined $interactive; my $description = $groups{$name}{'description'}; $description = '' if not defined $description; $detail .= qq( ); } $detail .= $tt->at_end; $detail .= qq(
Discovery Definition Name Description Control Type
$name $description Interactive Auto Auto-Commit  
); return $detail; } sub manage_group(@) { my $name = $_[1]; my %group = %{ $_[2] }; my @schemas = @{ $_[3] }; my %methods = %{ $_[4] }; my $tab = 0; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '500', left => '1', fontsize => '12px' ); my $detail = js_utils(); $detail .= qq( ); $tab++; my $description = $group{'description'}; $description = '' if not defined $description; $detail .= qq( ); $tab++; $detail .= qq( ); $tab++; my @auto = ( 'Interactive', 'Auto', 'Auto-Commit' ); $detail .= qq(
$name               
Description:
Import/update automation schema:
Default control type:
); $tab++; my $traceroute = 'traceroute not found'; if ( $group{'traceroute_command'} ) { if ( -e $group{'traceroute_command'} ) { $traceroute = $group{'traceroute_command'}; } elsif ( $group{'traceroute_command'} eq 'traceroute not found' ) { $traceroute = 'traceroute not found'; } else { $traceroute .= ": $group{'traceroute_command'}"; } } elsif ( -e '/bin/traceroute' ) { $traceroute = '/bin/traceroute'; } elsif ( -e '/usr/sbin/traceroute' ) { $traceroute = '/usr/sbin/traceroute'; } my $checked = $group{'enable_traceroute'} ? 'checked' : ''; my $colors = $group{'enable_traceroute'} ? 'color: #000000; background-color: #FFFFFF;' : 'color: #707070; background-color: #CCCCCC;'; $detail .= qq(
Traceroute Option
Use traceroute to help determine Nagios parent child relationships.

(These values must then be defined. Typical values are 6 for max hops and 2 for timeout.)

Command:   ); if ( $traceroute =~ /traceroute not found/ ) { $tab++; $detail .= qq(

); } else { $detail .= qq($traceroute     ); } my $traceroute_max_hops = $group{'traceroute_max_hops'}; $traceroute_max_hops = '' if not defined $traceroute_max_hops; $tab++; $detail .= qq( max hops (-m)    ); my $traceroute_timeout = $group{'traceroute_timeout'}; $traceroute_timeout = '' if not defined $traceroute_timeout; $tab++; $detail .= qq( timeout (-w)   seconds ); $detail .= qq(

); $tab++; $detail .= qq(
Methods
); my $color = 'dk'; foreach my $method ( sort keys %methods ) { unless ($method) { next } my $selected = $group{'method'}{$method} ? 'checked' : ''; if ( $color eq 'lt' ) { $color = 'dk'; } elsif ( $color eq 'dk' ) { $color = 'lt'; } $tab++; my $description = $methods{$method}{'description'}; $description = '' if not defined $description; $detail .= qq( ); } $options{'title'} = 'Add Method'; my $docs = "\nEnter the method name, the type, and a description."; my $tooltip = $tt->tooltip( $docs, \%options ); $detail .= qq(
Method Name Type Description  
$method $methods{$method}{'type'} $description
); $tab++; $detail .= qq( ); $tab++; $detail .= qq( ); $tab++; $detail .= qq(
Method Name Type Description
  ?
); $detail .= $tt->at_end; return $detail, $tab; } sub manage_method(@) { my $name = $_[1]; my $method = $_[2]; my %method = %{ $_[3] }; my $tab = 0; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '500', fontsize => '12px' ); my $bookmark = ( $method{'type'} eq 'Nmap' ) ? '#MiniTOCBookMark17' : ( $method{'type'} eq 'SNMP' ) ? '#MiniTOCBookMark18' : ( $method{'type'} eq 'WMI' ) ? '#MiniTOCBookMark19' : '#MiniTOCBookMark20'; my $detail = js_utils(); $detail .= qq( ); $tab++; my $description = $method{'description'}; $description = '' if not defined $description; $detail .= qq(
$method            
Type: $method{'type'}
Description:
); if ( $method{'type'} eq 'Nmap' ) { unless ( $method{'scan_type'} ) { $method{'scan_type'} = 'tcp_syn_scan'; } my %selected = ( $method{'scan_type'} => 'checked' ); $tab++; $options{'title'} = 'TCP SYN SCAN'; $options{'width'} = 700; my $docs = qq(TCP SYN SCAN is the most popular scan option for good reasons. It can be performed quickly, scanning thousands of ports per second on a fast network not hampered by intrusive firewalls. SYN scan is relatively unobtrusive and stealthy, since it never completes TCP connections. It also works against any compliant TCP stack rather than depending on idiosyncrasies of specific platforms as Nmap's FIN/null/Xmas, Maimon and idle scans do. It also allows clear, reliable differentiation between the open, closed, and filtered states.

This technique is often referred to as half-open scanning, because you don't open a full TCP connection. You send a SYN packet, as if you are going to open a real connection and then wait for a response. A SYN/ACK indicates the port is listening (open), while a RST (reset) is indicative of a non-listener. If no response is received after several retransmissions, the port is marked as filtered. The port is also marked filtered if an ICMP unreachable error (type 3, code 1,2, 3, 9, 10, or 13) is received.

); my $tooltip = $tt->tooltip( $docs, \%options ); my $tcp_syn_scan = $selected{'tcp_syn_scan'}; $tcp_syn_scan = '' if not defined $tcp_syn_scan; $detail .= qq( ); $tab++; $options{'title'} = 'TCP CONNECT SCAN'; $docs = qq(TCP CONNECT SCAN is the scan type to use when the TCP SYN SCAN is not an option. This is the case when a user does not have raw packet privileges or is scanning IPv6 networks. Instead of writing raw packets as most other scan types do, Nmap asks the underlying operating system to establish a connection with the target machine and port by issuing the connect() system call. This is the same high-level system call that web browsers, P2P clients, and most other network-enabled applications use to establish a connection. It is part of a programming interface known as the Berkeley Sockets API. Rather than read raw packet responses off the wire, Nmap uses this API to obtain status information on each connection attempt.

When SYN scan is available, it is usually a better choice. Nmap has less control over the high level connect() call than with raw packets, making it less efficient. The system call completes connections to open target ports rather than performing the half-open reset that SYN scan does. Not only does this take longer and require more packets to obtain the same information, but target machines are more likely to log the connection. A decent IDS will catch either, but most machines have no such alarm system. Many services on your average Unix system will add a note to syslog, and sometimes a cryptic error message, when Nmap connects and then closes the connection without sending data. Truly pathetic services crash when this happens, though that is uncommon. An administrator who sees a bunch of connection attempts in her logs from a single system should know that she has been connect scanned.

); $tooltip = $tt->tooltip( $docs, \%options ); my $tcp_connect_scan = $selected{'tcp_connect_scan'}; $tcp_connect_scan = '' if not defined $tcp_connect_scan; $detail .= qq( ); $tab++; $options{'title'} = 'UDP SCAN'; $docs = qq(While most popular services on the Internet run over the TCP protocol, UDP services are widely deployed. DNS, SNMP, and DHCP (registered ports 53, 161/162, and 67/68) are three of the most common. Because UDP scanning is generally slower and more difficult than TCP, some security auditors ignore these ports. This is a mistake, as exploitable UDP services are quite common and attackers certainly don\'t ignore the whole protocol. Fortunately, Nmap can help inventory UDP ports.); $tooltip = $tt->tooltip( $docs, \%options ); my $udp_scan = $selected{'udp_scan'}; $udp_scan = '' if not defined $udp_scan; $detail .= qq(
Scan Type
TCP SYN SCAN TCP SYN SCAN is the most popular scan option. It can be performed quickly, scanning thousands of ports per second on a fast network.
TCP CONNECT SCAN TCP CONNECT SCAN is the scan type to use when the TCP SYN SCAN is not an option. This is the case when a user does not have raw packet privileges or is scanning IPv6 networks.
UDP SCAN UDP SCAN: While most popular services on the Internet run over the TCP protocol, UDP services are widely deployed. DNS, SNMP, and DHCP (registered ports 53, 161/162, and 67/68) are three of the most common.
); my $checked = $method{'tcp_snmp_check'} ? 'checked' : ''; $options{'title'} = 'UDP SNMP Check'; $docs = qq(For scan types TCP SYN SCAN and TCP CONNECT SCAN optionally have a follow-up UDP scan to help determine whether or not the device is SNMP-enabled. If the UDP scan finds one or more SNMP ports open the device is flagged for further processing. This feature works with discovery definitions that include a UDP SCAN method and/or an SNMP method. Selecting this option will add to the time it takes it takes to discover each device.); $tooltip = $tt->tooltip( $docs, \%options ); $tab++; $detail .= qq(
UDP SNMP Check
UDP SNMP Check: Optionally have a follow-up UDP scan to check if the device is SNMP-enabled. Note that this option may not work across firewalls, and is recommended for scanning the local area network only. Use for remote networks only when you are sure all the intervening routers and firewalls will forward UDP traffic in both directions.
); $options{'title'} = 'SNMP Match Strings'; $docs = qq(For scan types TCP SYN SCAN and TCP CONNECT SCAN optionally provide a comma separated list of match strings to flag a discovered operating system as an SNMP-enabled device for further processing. This feature works with discovery definitions that include a UDP SCAN method and/or an SNMP method.); $tooltip = $tt->tooltip( $docs, \%options ); $tab++; my $snmp_strings = $method{'snmp_strings'}; $snmp_strings = '' if not defined $snmp_strings; $detail .= qq(
SNMP Match Strings
SNMP Match Strings: Optionally provide a comma-separated list of match strings to flag a discovered operating system as an SNMP-enabled device. This matching is case-insensitive. Such flagging is in addition to whatever hosts may be discovered to be SNMP-enabled via other checks, and is not a constricting filter on those other results.
); unless ( $method{'timeout'} ) { $method{'timeout'} = 'Normal' } # GWMON-5006 $method{'timeout'} = 'Sneaky' if $method{'timeout'} eq 'Paranoid'; %selected = ( $method{'timeout'} => 'selected' ); $options{'title'} = 'SCAN TIMEOUT'; $docs = qq(The timeout methods are sneaky, polite, normal, aggressive, and insane. These timeout algorithms allow the user to specify how aggressive they wish to be, while leaving Nmap to pick the exact timing values.

Sneaky mode is for IDS evasion; it scans only one port at a time and waits 15 seconds between probes. Polite mode slows down the scan to use less bandwidth and target machine resources. Normal mode is the default, and includes parallelization. Aggressive mode speeds scans up by making the assumption that you are on a reasonably fast and reliable network. Finally, insane mode assumes that you are on an extraordinarily fast network or are willing to sacrifice some accuracy for speed.

); $tooltip = $tt->tooltip( $docs, \%options ); $tab++; my $Sneaky = $selected{'Sneaky'}; my $Polite = $selected{'Polite'}; my $Normal = $selected{'Normal'}; my $Aggressive = $selected{'Aggressive'}; my $Insane = $selected{'Insane'}; $Sneaky = '' if not defined $Sneaky; $Polite = '' if not defined $Polite; $Normal = '' if not defined $Normal; $Aggressive = '' if not defined $Aggressive; $Insane = '' if not defined $Insane; $detail .= qq(
Scan Timeout
SCAN TIMEOUT: The timeout methods are sneaky (slow), polite, normal, aggressive, and insane (fast). These timeout algorithms allow the user to specify how aggressive they wish to be, while leaving Nmap to pick the exact timing values.
Ports
); my $color = 'dk'; foreach my $port ( sort keys %method ) { if ( $port =~ /port_(\S+)/ ) { if ( $color eq 'lt' ) { $color = 'dk'; } elsif ( $color eq 'dk' ) { $color = 'lt'; } $tab++; $detail .= qq( ); } } $options{'title'} = 'Add Port'; $docs = "\nEnter the port number and optionally the value to store as a match when the port is found to be active. If left blank, the stored match value will be what Nmap returns"; $tooltip = $tt->tooltip( $docs, \%options ); $tab++; $detail .= qq(
Ports Match Value
$1 $method{$port}
); $tab++; $detail .= qq( ); $tab++; $detail .= qq(
  ?
); } elsif ( $method{'type'} eq 'SNMP' ) { $tab++; unless ( $method{'snmp_ver'} ) { $method{'snmp_ver'} = '2c' } my %checked = ( $method{'snmp_ver'} => 'checked' ); my %class_v3 = ( 'enabled' => 'enabled', 'disabled' => 'disabled' ); my %class = ( 'enabled' => 'disabled', 'disabled' => 'enabled' ); unless ( $method{'snmp_ver'} ) { $method{'snmp_ver'} = '2c' } my $checked_1 = $checked{'1'}; my $checked_2c = $checked{'2c'}; my $checked_3 = $checked{'3'}; $checked_1 = '' if not defined $checked_1; $checked_2c = '' if not defined $checked_2c; $checked_3 = '' if not defined $checked_3; $detail .= qq( ); my $doc = qq(
Community strings and SNMP version 3 authorization
SNMP version:  1  ); $tab++; $detail .= qq(  2c  ); $tab++; $detail .= qq(  3
ParameterCommand Line Flagsnmp.conf token
securityName-u NAMEdefSecurityName NAME
authProtocol-a (MD5|SHA)defAuthType (MD5|SHA)
privProtocol-x (AES|DES)defPrivType DES
authKey-A PASSPHRASEdefAuthPassphrase PASSPHRASE
privKey-X PASSPHRASEdefPrivPassphrase PASSPHRASE
securityLevel-l (noAuthNoPriv|authNoPriv|authPriv)defSecurityLevel (noAuthNoPriv|authNoPriv|authPriv)
context-n CONTEXTNAMEdefContext CONTEXTNAME
); $tab++; my $class_v3_method_snmp_ver_3 = $class_v3{ $method{'snmp_ver_3'} } if defined $method{'snmp_ver_3'}; $class_v3_method_snmp_ver_3 = '' if not defined $class_v3_method_snmp_ver_3; my $snmp_v3_user = $method{'snmp_v3_user'}; $snmp_v3_user = '' if not defined $snmp_v3_user; $detail .= qq( SNMP v3 user: ); $tab++; unless ( $method{'snmp_v3_authProtocol'} ) { $method{'snmp_v3_authProtocol'} = 'none'; } unless ( $method{'snmp_v3_privProtocol'} ) { $method{'snmp_v3_privProtocol'} = 'none'; } unless ( $method{'snmp_v3_securityLevel'} ) { $method{'snmp_v3_securityLevel'} = 'noAuthNoPriv'; } %checked = ( $method{'snmp_v3_authProtocol'} => 'checked', $method{'snmp_v3_privProtocol'} => 'checked', $method{'snmp_v3_securityLevel'} => 'checked' ); $checked{'authProtocol_none'} = $method{'snmp_v3_authProtocol'} eq 'none' ? 'checked' : ''; $checked{'privProtocol_none'} = $method{'snmp_v3_privProtocol'} eq 'none' ? 'checked' : ''; my $class_v3_method_snmp_ver = $class_v3{$method{'snmp_ver'}}; $class_v3_method_snmp_ver = '' if not defined $class_v3_method_snmp_ver; $detail .= qq( SNMP v3 authentication protocol:  None  ); $tab++; my $MD5 = $checked{'MD5'}; $MD5 = '' if not defined $MD5; $detail .= qq(  MD5  ); $tab++; my $SHA = $checked{'SHA'}; $SHA = '' if not defined $SHA; $detail .= qq(  SHA  ); $tab++; my $snmp_v3_authKey = $method{'snmp_v3_authKey'}; $snmp_v3_authKey = '' if not defined $snmp_v3_authKey; $detail .= qq(   SNMP v3 authentication key:   ); $tab++; $detail .= qq( SNMP v3 privacy protocol:  None  ); $tab++; my $AES = $checked{'AES'}; $AES = '' if not defined $AES; $detail .= qq(  AES  ); $tab++; my $DES = $checked{'DES'}; $DES = '' if not defined $DES; $detail .= qq(  DES  ); $tab++; my $snmp_v3_privKey = $method{'snmp_v3_privKey'}; $snmp_v3_privKey = '' if not defined $snmp_v3_privKey; $detail .= qq(    SNMP v3 privacy key:   ); $tab++; my $noAuthNoPriv = $checked{'noAuthNoPriv'}; $noAuthNoPriv = '' if not defined $noAuthNoPriv; $detail .= qq( SNMP v3 security level:  None  ); $tab++; my $authNoPriv = $checked{'authNoPriv'}; $authNoPriv = '' if not defined $authNoPriv; $detail .= qq(  Authentication only  ); $tab++; my $authPriv = $checked{'authPriv'}; $authPriv = '' if not defined $authPriv; $detail .= qq(  Authentication and privacy  ); $tab++; $detail .= qq( ); $tab++; my $snmp_v3_misc = $method{'snmp_v3_misc'}; $snmp_v3_misc = '' if not defined $snmp_v3_misc; $detail .= qq( SNMP v3 misc options: ); $tab++; my $class_method_snmp_ver = $class{$method{'snmp_ver'}}; $class_method_snmp_ver = '' if not defined $class_method_snmp_ver; my $community_strings = $method{'community_strings'}; $community_strings = '' if not defined $community_strings; $detail .= qq( Single community string: ); } elsif ( $method{'type'} eq 'WMI' ) { my %selected = defined( $method{'wmi_type'} ) ? ( $method{'wmi_type'} => 'checked' ) : (); $tab++; my $Server = $selected{'Server'}; $Server = '' if not defined $Server; $detail .= qq( ); $tab++; my $Proxy = $selected{'Proxy'}; $Proxy = '' if not defined $Proxy; $detail .= qq(
WMI type
 Server Select Server if you have one or more GroundWork Passive WMI Servers running on your network.
 Proxy Select Proxy if your Windows checks run through one or more WMI proxy servers. You may specify the servers in Ranges and Filters, or combine this method with an Nmap method and let Nmap discover the WMI proxies.

Auto-Discovery using the Proxy option is currently not implemented.

); } else { my %selected = defined( $method{'script_type'} ) ? ( $method{'script_type'} => 'checked' ) : (); my $batch_mode_checkbox_value = ( defined( $method{'run_mode'} ) && $method{'run_mode'} eq 'Batch Mode' ) ? 'checked' : 'unchecked'; $tab++; $detail .= qq(
Script type  Custom Currently only Custom scripts are supported. Custom scripts can run independent of Ranges and Filters as well as other discovery methods.
); $tab++; $detail .= qq(
Run Mode
 Batch Mode
In batch mode, a script will only be executed once during a discovery. When not in batch mode, scripts are executed once for each host with a matching method.
); $tab++; my $command_line = $method{'command_line'}; $command_line = '' if not defined $command_line; $detail .= qq(
Command Line
Enter the full path, script name, and command line arguments. Command line arguments may include Nagios macros such as \$HOST\$.
); } return $detail, $tab; } # filters sub manage_filters(@) { my %object = %{ $_[1] }; my %filters = %{ $_[2] }; my $tab = $_[3]; use HTML::Tooltip::Javascript; my $tt = HTML::Tooltip::Javascript->new( ## URL path to where wz_tooltip.js is. javascript_dir => $monarch_js, options => { bgcolor => '#000000', default_tip => 'Tip not defined', delay => 0, title => 'Tooltip', }, ); my %options = ( borderwidth => '1', padding => '10', bordercolor => '#000000', bgcolor => '#FFFFFF', width => '755', left => '1', fontsize => '12px' ); $options{'title'} = 'Ranges and Filters'; my $docs = "Ranges and Filters describe the network addresses that will be examined by the discovery method. Various formats are supported, as listed in the help message for the Range/Filter Pattern.\n

\nAddresses may be excluded from the range using the same mechanism. Note that excluded addresses and ranges are never probed regardless of the input order.

"; my $tooltip = $tt->tooltip( $docs, \%options ); my $detail = qq(
Ranges and Filters  ?
); my $color = 'dk'; foreach my $filter ( sort keys %filters ) { my $selected = $object{'filter'}{$filter} ? 'checked' : ''; if ( $color eq 'lt' ) { $color = 'dk'; } elsif ( $color eq 'dk' ) { $color = 'lt'; } $tab++; my $class="row_$color"; $detail .= qq( ); } $options{'title'} = 'Add Filter'; $docs = qq{Range format can be any of:

    \n
  • Unqualified hostname (e.g., mybox)\n
  • Qualified hostname (e.g., mybox.mydomain.com)\n
  • Single address (e.g., 192.168.0.1)\n
  • Address range (e.g., 192.168.0.2 - 192.168.0.10)\n
  • Abbreviated address range: (e.g., 192.168.0.10-12)\n
  • Class C subnet (e.g., 192.168.0.*).\n
  • Abbreviated multiple Class C subnets (e.g., 192.168.42-50)\n
  • CIDR block (e.g., 192.168.144.0/20)\n
  • Comma-separated list of any of the above (e.g., 192.168.0.1, 192.168.0.3)\n

\n

In Class C specifications, the network (.0) and broadcast (.255) addresses will be automatically ignored. Equivalent addresses will be ignored in CIDR blocks with a subnet prefix smaller than /31.

\n

If you are using a discovery script to generate a list of hosts, you need to put a dummy value into this field in order to make the discovery proceed.

\n}; $tooltip = $tt->tooltip( $docs, \%options ); $tab++; $detail .= qq(
Range/Filter Name Type Range/Filter Pattern  
$filter $filters{$filter}{'type'} $filters{$filter}{'filter'}
Range/Filter Name Type Range/Filter Pattern  ?
); $detail .= $tt->at_end; $detail .= qq( ); return $detail; } sub delete_group(@) { my $name = $_[1]; my %methods = %{ $_[2] }; my $tab = 0; my $detail = qq(
Delete "$name"?
Are you sure you want to delete discovery definition $name and optionally its associated methods? Note: if you choose to delete methods, they will also be removed from all other discovery definitions.
); if ( keys %methods ) { my $class = 'row_dk'; foreach my $method ( sort keys %methods ) { if ( $class eq 'row_lt' ) { $class = 'row_dk'; } elsif ( $class eq 'row_dk' ) { $class = 'row_lt'; } $tab++; my $description = $methods{$method}{'description'}; $description = '' if not defined $description; $detail .= qq( ); } } else { $detail .= qq( ); } $detail .= qq(
Method Name Type Description
$method $methods{$method}{'type'} $description
  There are no methods assigned.
); return $detail; } sub discover_disclaimer(@) { my $detail = qq(
Start Discovery?
WARNING: This process may have an adverse impact on the networked environment. You may also need to disable intrusion detection software or any policies which may prevent the auto-configuration process from running.
 Accept   
); return $detail; } ######################################## # Documentation # sub doc() { my %doc = (); $doc{'overview'} = qq(An import/update schema can be used with any data source from which a text delimited file can be extracted.); $doc{'define'} = qq(Enter a unique name and either choose the appropriate schema type or create a new schema from an existing template. Selecting both a schema type and a template will override the template's schema type with your selected type.); # Schema types $doc{'host-import'} = qq(Use host-import for most import/automation tasks. A full range of options is available to import/update hosts. Everything from assigning host groups and contact groups to parents and profiles can be defined.); $doc{'host-profile-sync'} = qq(Use host-profile-sync when an external data source drives the monitor configuration. The data source determines which hosts are added and which hosts are removed based on host profile assignment. For example, using Cacti as the data source with a corresponding host profile, a schema can be defined to sync the configuration database with the Cacti database. Hosts missing from the data source but that are part of the host profile assignment are automatically removed. A full range of options is available to import/update hosts. Everything from assigning host groups and contact groups to parents and profiles can be defined.); $doc{'other-sync'} = qq(Use other-sync to align two pieces of data from an external data source. The other-sync schema type requires that host objects have been previously configured, but you can create configuration groups, host groups, and contact groups on the fly.); # Import data $doc{'data-source'} = qq( Enter the folder and file name of the text delimited data source.

A data source is a text delimited file that contains data records. A data record can be defined to a single line or can span multiple lines. For multiple line records, there must be a unique identifier, usually a host name or address, to tell the parser how to differentiate records. For schema types host-profile-sync and host-import you set the primary record by defining a match to a column of data and assigning it to the primary record object. If the primary record is not defined the parser will use the host name match definition.

If you have comments in your data, define a match to discard the line. For example, match begins-with ⇒ # ⇒ Discard record.

Sample multiline data records using ;; as the text separator

# name;;address;;alias;;hostgroup;;contactgroup;;parent;;profile;;service profile
router-1;;10.20.110.1;;router-1.alps.com;;italy-hosts;;italy-contacts;;;;;;
grenoble;;10.20.110.60;;grenoble.alps.com;;france-hosts;;france-contacts;;router-1;;linux-hosts;;ssh-unix
;;;;;;database-hosts;;database-contacts;;;;;;ssh-mysql
;;;;;;;;;;;;;;ssh-ldap
salzburg;;10.20.110.70;;salzburg.alps.com;;austria-hosts;;austria-contacts;;router-1;;linux-hosts;;ssh-unix
;;;;;;network-hosts;;network-contacts;;;;;;ssh-apache
;;;;;;;;;;;;;;ssh-ldap
innsbruck;;10.20.110.80;;innsbruck.alps.com;;austria-hosts;;austria-contacts;;router-1;;linux-hosts;;ssh-unix
;;;;;;database-hosts;;database-contacts;;;;;;ssh-mysql

); $doc{'delimiter'} = qq(Select from the dropdown, or enable Other and enter your own text delimiter.); # Column definition $doc{'column'}{'position'} = qq(Defines the location of a data column. You can have many matches defined to a column but only one position can be defined per column.); $doc{'column'}{'name'} = qq(Identifies the column of data. It is not a match, but a unique identifier describing the column of data.); # Match definition $doc{'match'}{'order'} = qq(Determines the sequence in which a match is processed. A match order is only useful when used in conjunction with the 'Assign value if undefined' rule or the 'Assign host profile if undefined' rule. It allows the most desirable match to set the property first, and prevents matches of lesser importance overriding the desired match. Properties and objects that can be applied are host name, address, alias and host profile.); $doc{'match'}{'name'} = qq(A unique identifier describing an action on a data point.); # Match types $doc{'type'}{'use-value-as-is'} = qq(use the record exactly as it is, usually to be applied directly to a property or object.); $doc{'type'}{'is-null'} = qq(the match is true if the record has no value.); $doc{'type'}{'exact'} = qq(match the record exactly while ignoring case. Note: Do not use this in combination with columns that contain service-definition records.); $doc{'type'}{'begins-with'} = qq(match the start of the record exactly while ignoring case.); $doc{'type'}{'ends-with'} = qq(match the end of the record exactly while ignoring case.); $doc{'type'}{'contains'} = qq(match a record that contains the string exactly while ignoring case.); $doc{'type'}{'use-perl-reg-exp'} = qq(match using a Perl regular expression.); $doc{'type'}{'service-definition'} = qq(map the record to an existing service object. This match type requires a specific substring definition.); # Match string $doc{'string'}{'other'} = qq(Enter the content you wish to match.); $doc{'string'}{'use-perl-reg-exp'} = qq( Enter a Perl expression to match or capture content.

The actual Perl code used in the match is

  \$value =~ / your pattern match string /i

so therefore DO NOT enter the beginning or ending / characters.

  (\\S+)\\.\\S+\\.\\S+ correct

  /(\\S+)\\.\\S+\\.\\S+/ incorrect

Example: parsing the host name from a fully qualified domain name ...

  (\\S+)\\.\\S+\\.\\S+ will parse host-name from host-name.domain.com.

Some useful expression matching operators:

  \\s matches one space character
  \\s* matches zero or more space characters
  \\s+ matches one or more space characters
  \\S matches one non-space character
  \\S* matches zero or more non-space characters
  \\S+ matches one or more non-space characters
  \\d* matches zero or more numeric characters
  \\d matches one numeric character
  \\d+ matches one or more numeric characters

); $doc{'string'}{'service-delimiter'} = qq( For match type service-definition, enter the substring delimiter for a service definition. A service definition must match an existing service object or the record will be ignored. One to five pieces of information can be included:

  1. Service name (required) — A service name must match an existing service object.
  2. Command arguments (optional) — Provide the arguments to be given to the check command as they would appear in a Nagios definition. If not specified, the definition from the service object will be applied.
  3. Instance name (optional) — Specify an instance name to be appended to the service description. You may wish to use an underscore as the first character.
  4. Instance arguments (required for instance name) — Specify the arguments for the instance.
  5. Misc info (optional) — Miscellaneous information useful for applying other match criteria.

Multiple instances can be specified in a multi-line record. This example uses ;; for the main record delimiter and :: as the service substring delimiter:

# name;;address;;alias;;hostgroup;;contactgroup;;parent;;profile;;service profile;;service
router-1;;10.20.110.1;;router-1.alps.com;;italy-hosts;;italy-contacts;;;;;;;;snmp_if::::_1::1::
;;;;;;network-hosts;;network-contacts;;;;;;;;snmp_if::::_2::2::
;;;;;;;;;;;;;;;;snmp_if::::_3::3::
;;;;;;;;;;;;;;;;snmp_if::::_4::4::
;;;;;;;;;;;;;;;;snmp_if::::_5::5::
;;;;;;;;;;;;;;;;snmp_if::::_6::6::
;;;;;;;;;;;;;;;;snmp_if::::_7::7::
;;;;;;;;;;;;;;;;snmp_if::::_8::8::
;;;;;;;;;;;;;;;;snmp_if::::_9::9::
;;;;;;;;;;;;;;;;snmp_if::::_10::10::
;;;;;;;;;;;;;;;;snmp_if::::_11::11::
;;;;;;;;;;;;;;;;snmp_if::::_12::12::
;;;;;;;;;;;;;;;;snmp_if::::_13::13::
;;;;;;;;;;;;;;;;snmp_if::::_14::14::
;;;;;;;;;;;;;;;;snmp_if::::_15::15::
;;;;;;;;;;;;;;;;snmp_if::::_16::16::
;;;;;;;;;;;;;;;;snmp_if::::_17::17::
;;;;;;;;;;;;;;;;snmp_if::::_18::18::
;;;;;;;;;;;;;;;;snmp_if::::_19::19::
;;;;;;;;;;;;;;;;snmp_if::::_20::20

); $doc{'edit'} = qq(Select or set the values to apply and choose Process Record to import/update the host. Note that if you process an existing host the current configuration is always replaced. Select Discard to remove the record from processing, or select Cancel to return to normal processing.); $doc{'overrides'}{'disable'} = qq(Deselect records, disable overrides and return to normal processing.); $doc{'overrides'}{'enable'} = qq(Deselect records and enable overrides to batch process a group of hosts with values that override the schema matched results.); $doc{'overrides'}{'usage'} = qq(To use overrides, select the records you wish to process, check the override checkbox, and then select one or more values. Next, chose the option to merge or replace. Merge will preserve schema matched values whereas replace does not. Note that if you process an existing host the current configuration is always replaced.); # Rules and objects $doc{'rule'}{'Add if not exists and assign object'} = qq(Creates the object and assigns it to the host. The objects that can be created are: contact groups, configuration groups and host groups.); $doc{'rule'}{'Assign object(s)'} = qq(On match, assign one or more parents, configuration groups, host groups, contact groups, or service profiles.); $doc{'rule'}{'Assign object if exists'} = qq(Test to see if an object is defined and assign it to the host if it does. Objects that can be assigned are: parent hosts, configuration groups, host groups, service profile and contact groups.); $doc{'rule'}{'Assign value to'} = qq(Use the value to set the primary record or assign it to one of the following host properties: name, address, alias, or description.); $doc{'rule'}{'Assign value if undefined'} = qq(Test to see if the primary record or property has been set by a previous match, and if not, assign the value. The options are: primary record, host name, address, alias, or description.); $doc{'rule'}{'Assign host profile if undefined'} = qq(Test to see if the host profile has been set by a previous match, and if not, assign the host profile.); $doc{'rule'}{'Convert dword and assign to'} = qq(Convert a doubleword value (NeDi stores IP addresses as doublewords) and assign it to the primary record or one of the following host properties: primary record, name, address, alias, or description.); $doc{'rule'}{'Discard if match existing host'} = qq(Don't process the record if a matching host record exists. This rule is important to preserve existing host configurations.); $doc{'rule'}{'Resolve to parent'} = qq(This rule indicates that the current column contains the name of the current device' network parent. Note that this rule directive is only available if the "use-value-as-is" matching filter has been selected.); $doc{'rule'}{'Assign host profile'} = qq(Use the match to assign a host profile. For example, if the description contains the word Cisco, assign a network host profile to the record.); $doc{'rule'}{'Assign service'} = qq(This rule assigns a specified service entry to the currently selected host object, with the field data being used as the service entry name. If this rule is selected, you must also choose the service type to be used for the new service entry.); $doc{'rule'}{'Discard record'} = qq(Don't process the record if the match is true. Use this rule to exclude data such as comments or unwanted hosts from processing.); $doc{'rule'}{'Skip column record'} = qq(This rule is used whenever a column contains multiple subordinate fields (as typically occurs when SNMP interfaces have been enumerated), and causes the automation processor to skip the current subordinate record in the current column.); $doc{'process'}{'overview'} = qq(Use the sort and select options (see the mouse-over ? for details) to process records individually or in batches. Use edit when changing a single host, or use Enable Overrides and select a group of hosts to modify and process a batch of hosts. Select and Discard records you do not wish to process. If what you see does not look correct, use Edit Schema to make modifications and try again. For example, a common misstep is selecting the wrong delimiter. Records are displayed in blocks of 100.); $doc{'process'}{'Sort by'} = qq(Records are arranged by their status: New Parent, New Host, Host Exists, Exception and Delete Host. Within each status you can sort by Primary record, Name, Address, and Alias.); $doc{'process'}{'New Parent'} = qq(Select new parents for processing. These records have been flagged because they have child dependencies that are in the new host list. It is therefore desirable to process these records first so that parent child relationships are properly set. If child hosts are processed first, the parent host assignment will be ignored. Note that when fully automated, new parents will always be processed first.); $doc{'process'}{'New Host'} = qq(Select new hosts for processing. You can accept the records as is or after selecting, enable overrides to merge or replace new information. You may also use edit to process each record individually.); $doc{'process'}{'Host Exists'} = qq(Select existing hosts for processing. Processing an existing host will replace the current configuration for that host with the new one specified in the record. For automation purposes, you can define a match task to discard existing hosts from processing.); $doc{'process'}{'Exception'} = qq(Select records flagged as exception. Records missing vital pieces of information (name, address, or alias) cannot be processed. Use the Smart Names Option (Edit Schema) to satisfy incomplete data. You may also use edit to process each record individually.); $doc{'process'}{'Delete Host'} = qq(Select host flagged for deletion. You will only see hosts in this category with the schema type host-profile-sync. Hosts that are assigned to the operative host profile but are not in the data source are flagged for deletion.); return %doc; } 1;