#!/usr/bin/perl # (c) Dario Rodriguez 2011 # Intel DCM Discovery use POSIX qw(setsid strftime strftime ceil); use strict; use warnings; use IO::Socket::INET; use NetAddr::IP; # Default lib dir for RPM and DEB packages BEGIN { push @INC, '/usr/lib/perl5'; } use PandoraFMS::Tools; use PandoraFMS::DB; use PandoraFMS::Core; use PandoraFMS::Config; sub getParam($) { my $param = shift; $param = "--".$param; my $value_aux = undef; my $i; for($i=0; $i<$#ARGV; $i++) { if ($param eq $ARGV[$i]) { $value_aux = $ARGV[$i+1]; } } return $value_aux; } ########################################################################## # Global variables so set behaviour here: my $target_timeout = 5; # Fixed to 5 secs by default ########################################################################## # Code begins here, do not touch ########################################################################## my $pandora_conf = "/etc/pandora/pandora_server.conf"; my $task_id = $ARGV[0]; # Passed automatically by the server my $target_group = $ARGV[1]; # Defined by user my $create_incident = $ARGV[2]; # Defined by user # Used Custom Fields in this script my $target_network = getParam("net"); # Filed1 defined by user my $username = getParam("ipmi_user"); # Field2 defined by user my $password = getParam("ipmi_pass"); # Field3 defined by user my $dcm_server = getParam("dcm_server"); my $dcm_port = getParam("dcm_port"); my $derated_power = getParam("derated_power"); ########################################################################## # Update recon task status. ########################################################################## sub update_recon_task ($$$) { my ($dbh, $id_task, $status) = @_; db_do ($dbh, 'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?', time (), $status, $id_task); } ########################################################################## # Show help ########################################################################## sub show_help { print "\nSpecific Pandora FMS Intel DCM Discovery\n"; print "(c) Artica ST 2011 \n\n"; print "Usage:\n\n"; print " $0 more params, see doc\n\n"; exit; } sub ipmi_ping ($$$$) { my ($conf, $addr, $user, $pass) = @_; my $cmd = "ipmiping $addr -c 3"; my $res = `$cmd`; if ($res =~ /100\.0% packet loss/) { return 0; } #If we have credentials we must check it if (defined($user) && defined($pass) && $user && $pass) { # Check if the credentials are valid $cmd = "bmc-info -h $addr -u $user -p $pass 2>&1"; $res = `$cmd`; if ($? != 0) { logger ($conf, "[Intel DCM Discovery] Host $addr reports a BMC error", 1); return 0; } } return 1; } sub create_custom_fields ($) { my $dbh = shift; my $id_entity = get_db_value($dbh, 'SELECT id_field FROM tagent_custom_fields WHERE name = "DCM_Entity_Id"'); if (!$id_entity) { db_insert ($dbh, 'id_field', 'INSERT INTO tagent_custom_fields (name, display_on_front) VALUES ("DCM_Entity_Id", 0)'); } my $id_derated = get_db_value($dbh, 'SELECT id_field FROM tagent_custom_fields WHERE name = "DCM_Entity_Derated_Power"'); if (!$id_derated) { db_insert ($dbh, 'id_field', 'INSERT INTO tagent_custom_fields (name, display_on_front) VALUES ("DCM_Entity_Derated_Power", 0)'); } ($id_derated, $id_entity) } sub set_dcm_derated_power ($$$$) { my ($dbh, $id_agent, $id_field, $derated_power) = @_; db_insert ($dbh, 'id_field', 'INSERT INTO tagent_custom_data (id_field, id_agent, description) VALUES (?, ? ,?)', $id_field, $id_agent, $derated_power); } sub set_dcm_id ($$$$) { my ($dbh, $id_agent, $id_field, $dcm_id) = @_; db_insert ($dbh, 'id_field', 'INSERT INTO tagent_custom_data (id_field, id_agent, description) VALUES (?, ? ,?)', $id_field, $id_agent, $dcm_id); } sub create_dcm_entity ($$$$$$$$) { my ($dbh, $dcm_server, $dcm_port, $agent_name, $agent_address, $derated_power, $bmc_user, $bmc_pass)= @_; my $plugin_command = get_db_value($dbh, 'SELECT execute FROM tplugin WHERE name = "'.safe_input("Intel DCM Plugin").'"'); my $command = safe_output($plugin_command)." --server \"".$dcm_server."\" --port ".$dcm_port; $command .= " --action 'add_entity' --type 'NODE' --value '$agent_name'"; $command .= " --address '$agent_address' --derated_power '$derated_power'"; $command .= " --connector 'com.intel.dcm.plugin.Nm15Plugin'"; $command .= " --bmc_user '$bmc_user' --bmc_pass '$bmc_pass'"; my $res = `$command`; return $res; } sub create_metric_modules($$$$$$) { my ($conf, $dbh, $id_agent, $dcm_id, $dcm_server, $dcm_port) = @_; my @modules_array = ( {"name" => "Managed Nodes Energy", "desc" => "The total energy consumed by all managed nodes in the specified entity, in Wh", "value" => "mnged_nodes_energy"}, {"name" => "Managed Nodes Energy Bill", "desc" => "The total power bill for all energy consumed by all managed nodes in the specified entity", "value" => "mnged_nodes_energy_bill"}, {"name" => "IT Equipment Energy", "desc" => "The total energy consumed by IT equipment, including managed nodes, unmanaged nodes and other IT equipment in the selected entity, in Wh", "value" => "it_eqpmnt_energy"}, {"name" => "IT Equipment Energy Bill", "desc" => "The calculated power bill for IT equipment, including managed nodes, unmanaged nodes and other IT equipment in the selected entity", "value" => "it_eqpmnt_energy_bill"}, {"name" => "Calculated Cooling Energy", "desc" => "The energy needed to cool the selected entity, in Wh", "value" => "calc_cooling_energy"}, {"name" => "Calculated Cooling Energy Bill", "desc" => "The calculated power bill for the energy needed to cool the selected entity", "value" => "calc_cooling_energy_bill"}, {"name" => "Managed Nodes Power", "desc" => "The total average power consumption by the managed nodes in the selected entity, in watts", "value" => "mnged_nodes_pwr"}, {"name" => "IT Equipment Power", "desc" => "Provides the total average power consumption by IT equipment, including managed nodes, unmanaged nodes and other IT equipment in the selected entity in watts", "value" => "it_eqpmnt_pwr"}, {"name" => "Calculated Cooling Power", "desc" => "Provides the average cooling power based on the IT_EQPMNT_PWR multiplied by COOLING_MULT in watts", "value" => "calc_cooling_pwr"}, {"name" => "Avg. Power Per Dimension", "desc" => "The average power consumption per dimension", "value" => "avg_pwr_per_dimension"}, {"name" => "Derated power", "desc" => "Adds the de-rated values of all the nodes in the entity to the nameplate power value of all unmanaged nodes and equipment associated with the entity, as defined by NAMEPLATE_PWR_UNMNGD_EQPMNT", "value" => "derated_pwr"}, {"name" => "Inlet Temperature Span", "desc" => "The average inlet temperature differential between the highest and lowest node temperature in a group (degC/degF)", "value" => "inlet_temperature_span"}); my $plugin_action = "--action \'metric_data\' --entity_id \'".$dcm_id."\'"; my $id_plugin = get_db_value($dbh, 'SELECT id FROM tplugin WHERE name = "'.safe_input("Intel DCM Plugin").'"'); foreach my $mod (@modules_array) { my %aux_mod = %{$mod}; my $aux_params = $plugin_action." --value \'".$aux_mod{'value'}."\'"; my %parameters; $parameters{"nombre"} = safe_input($aux_mod{'name'}); $parameters{"id_tipo_modulo"} = 1; $parameters{"id_agente"} = $id_agent; $parameters{"id_plugin"} = $id_plugin; $parameters{"ip_target"} = $dcm_server; $parameters{"tcp_port"} = $dcm_port; $parameters{"id_modulo"} = 4; $parameters{"max_timeout"} = 300; $parameters{"descripcion"} = $aux_mod{'desc'}; $parameters{"plugin_parameter"} = $aux_params; pandora_create_module_from_hash ($conf, \%parameters, $dbh); } } sub create_query_modules($$$$$$) { my ($conf, $dbh, $id_agent, $dcm_id, $dcm_server, $dcm_port) = @_; my @modules_array = ( {"name" => "Max. Power", "desc" => "The maximum power consumed by any single node/enclosure", "value" => "max_pwr"}, {"name" => "Avg. Power", "desc" => "The average power consumption across all nodes/enclosures", "value" => "avg_pwr"}, {"name" => "Min. Power", "desc" => "The minimum power consumed by any single node/enclosure", "value" => "min_pwr"}, {"name" => "Max. Avg. Power", "desc" => "The maximum of group sampling (in a monitoring cycle) power in specified aggregation period for the sum of average power measurement in a group of nodes/enclosures within the specified entity", "value" => "max_avg_pwr"}, {"name" => "Total Max. Power", "desc" => "The maximum of group sampling (in a monitoring cycle) power in specified aggregation period for sum of maximum power measurement in a group of nodes/enclosures within the specified entity", "value" => "total_max_pwr"}, {"name" => "Total Avg. Power", "desc" => "The average (in specified aggregation period) of group power for sum of average power measurement in a group of nodes/enclosures within the specified entity", "value" => "total_avg_pwr"}, {"name" => "Max. Avg. Power Capping", "desc" => "The maximum of group sampling (in a monitoring cycle) power in specified aggregation period for the sum of average power measurement in a group of nodes/enclosures with power capping capability", "value" => "max_avg_pwr_cap"}, {"name" => "Total Max. Power Capping", "desc" => "The maximum group sampling (in a monitoring cycle) power in specified aggregation period for sum of maximum power measurement in a group of nodes/enclosures with power capping capability", "value" => "total_max_pwr_cap"}, {"name" => "Total Avg. Power Capping", "desc" => "The average (in specified aggregation period) of group power for sum of average power measurement in a group of nodes/enclosures with power capping capability", "value" => "total_avg_pwr_cap"}, {"name" => "Total Min. Power", "desc" => "The minimal group sampling (in a monitoring cycle) power in specified aggregation period for sum of minimum power measurement in a group of nodes/enclosures within the specified entity", "value" => "total_min_pwr"}, {"name" => "Min. Avg. Power", "desc" => "The minimal group sampling (in a monitoring cycle) power in specified aggregation period for sum of average power measurement in a group of nodes/enclosures within the specified entity", "value" => "min_avg_pwr"}, {"name" => "Max. Inlet Temperature", "desc" => "The maximum temperature for any single node within the specified entity", "value" => "max_inlet_temp"}, {"name" => "Avg. Inlet Temperature", "desc" => "The average temperature for any single node within the specified entity", "value" => "avg_inlet_temp"}, {"name" => "Min. Inlet Temperature", "desc" => "The minimum temperature for any single node within the specified entity", "value" => "min_inlet_temp"}, {"name" => "Instantaneous Power", "desc" => "The instantaneous power consumption of a specified node/enclosure or the sum of the instantaneous power of the nodes/enclosures within the specified entity", "value" => "ins_pwr"}); my $plugin_action = "--action \'query_data\' --entity_id \'".$dcm_id."\'"; my $id_plugin = get_db_value($dbh, 'SELECT id FROM tplugin WHERE name = "'.safe_input("Intel DCM Plugin").'"'); foreach my $mod (@modules_array) { my %aux_mod = %{$mod}; my $aux_params = $plugin_action." --value \'".$aux_mod{'value'}."\'"; my %parameters; $parameters{"nombre"} = safe_input($aux_mod{'name'}); $parameters{"id_tipo_modulo"} = 1; $parameters{"id_agente"} = $id_agent; $parameters{"id_plugin"} = $id_plugin; $parameters{"ip_target"} = $dcm_server; $parameters{"tcp_port"} = $dcm_port; $parameters{"id_modulo"} = 4; $parameters{"max_timeout"} = 300; $parameters{"descripcion"} = $aux_mod{'desc'}; $parameters{"plugin_parameter"} = $aux_params; pandora_create_module_from_hash ($conf, \%parameters, $dbh); } } ########################################################################## ########################################################################## # M A I N C O D E ########################################################################## ########################################################################## if ($#ARGV == -1){ show_help(); } # Pandora server configuration my %conf; $conf{"quiet"} = 0; $conf{"verbosity"} = 1; # Verbose 1 by default $conf{"daemon"}=0; # Daemon 0 by default $conf{'PID'}=""; # PID file not exist by default $conf{'pandora_path'} = $pandora_conf; # Read config file pandora_load_config (\%conf); # Connect to the DB my $dbh = db_connect ('mysql', $conf{'dbname'}, $conf{'dbhost'}, '3306', $conf{'dbuser'}, $conf{'dbpass'}); # Start the network sweep # Get a NetAddr::IP object for the target network my $net_addr = new NetAddr::IP ($target_network); if (! defined ($net_addr)) { logger (\%conf, "Invalid network " . $target_network . " for Intel DCM Discovery task", 1); update_recon_task ($dbh, $task_id, -1); return -1; } #Get id of custom fields my ($id_derated_cf, $id_entity_cf) = create_custom_fields($dbh); # Scan the network for hosts my ($total_hosts, $hosts_found, $addr_found) = ($net_addr->num, 0, ''); my $last = 0; for (my $i = 1; $net_addr <= $net_addr->broadcast; $i++, $net_addr++) { if($last == 1) { last; } my $net_addr_temp = $net_addr + 1; if($net_addr eq $net_addr_temp) { $last = 1; } if ($net_addr =~ /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.(\d{1,3})\b/) { if($1 eq '0' || $1 eq '255') { next; } } my $addr = (split(/\//, $net_addr))[0]; $hosts_found ++; # Update the recon task update_recon_task ($dbh, $task_id, ceil ($i / ($total_hosts / 100))); my $alive = 0; if (ipmi_ping (\%conf, $addr, $username, $password) == 1) { $alive = 1; } next unless ($alive > 0); # Resolve the address my $host_name = gethostbyaddr(inet_aton($addr), AF_INET); $host_name = $addr unless defined ($host_name); logger(\%conf, "Intel DCM Device found host $host_name.", 10); # Check if the agent exists my $agent_id = get_agent_id($dbh, $host_name); # If the agent exists go for the next if($agent_id != -1) { next; } # Create DCM Entity my $dcm_id = create_dcm_entity ($dbh, $dcm_server, $dcm_port, $host_name, $addr, $derated_power, $username, $password); # Create a new agent $agent_id = pandora_create_agent (\%conf, $conf{'servername'}, $host_name, $addr, $target_group, 0, 11, 'Created by Intel DCM Discovery', 300, $dbh); # Create modules create_query_modules(\%conf, $dbh, $agent_id, $dcm_id, $dcm_server, $dcm_port); create_metric_modules(\%conf, $dbh, $agent_id, $dcm_id, $dcm_server, $dcm_port); # Set custom fields set_dcm_derated_power ($dbh, $agent_id, $id_derated_cf, $derated_power); set_dcm_id ($dbh, $agent_id, $id_entity_cf, $dcm_id); # Generate an event pandora_event (\%conf, "[RECON] New Intel DCM host [$host_name] detected on network [" . $target_network . ']', $target_group, $agent_id, 2, 0, 0, 'recon_host_detected', 0, $dbh); } # Mark recon task as done update_recon_task ($dbh, $task_id, -1); # End of code