#!/usr/bin/perl # # Common Routines by Steffen Plotner # package common; use strict; require Exporter; #use Net::SNMP; #use DBI; use List::Util 'shuffle'; use Data::Dumper; use Carp; use Sys::Hostname; our @ISA = qw(Exporter); our @EXPORT = qw( get_db db_exec db_exec_rows db_exec_row db_exec_val get_if_ip_from_if_name logger logger_err round_int parallelize parallelize_with_arg_combine hex_dump date_time_string get_time_units seconds_to_units host_info ); use config; #------------------------------------------------------------------------------- # Get a DBI connection #------------------------------------------------------------------------------- sub get_db { my($db_name) = @_; my $db_conn = DBI->connect($cfg_db{$db_name}{'db_dbi'}, $cfg_db{$db_name}{'db_user'}, $cfg_db{$db_name}{'db_pass'}, $cfg_db{$db_name}{'db_attr'}); die "Unable to connect to server $DBI::errstr" unless $db_conn; } #---------------------------------------------------------------------------- # db_exec_rows (returns statement handle, so that you can enum rows) # # in: connection, sql statement, parameter value array # out: DBI statement handle, rows affected # #---------------------------------------------------------------------------- sub db_exec_rows { my($conn, $sql, @parm_vals) = @_; # # statement preparation # my $stm = $conn->prepare($sql); logger("db> $sql"); # # bind parameter values # my $parm_count = 1; foreach my $parm_val (@parm_vals) { logger("db> parm $parm_count=$parm_val"); $stm->bind_param($parm_count, $parm_val); $parm_count++; } # # handle exceptions, tell operator stack trace... # my $row_count; eval { $row_count = $stm->execute(); }; if ($@) { logger_err("err> sql: $sql"); logger_err("err> val: [".join('] [',@parm_vals).']'); print STDERR Carp::longmess("sql statement error"); die "db_exec_rows: $@"; } logger("db> row_count=$row_count"); return ($stm, $row_count); } #---------------------------------------------------------------------------- # db_exec_row (returns as a hash a single row from a sql statement) # # in: connection, sql statement, parameter value array # out: row hash (could be undef), rows affected # #---------------------------------------------------------------------------- sub db_exec_row { my($conn, $sql, @parm_vals) = @_; my($stm, $row_count) = db_exec_rows($conn, $sql, @parm_vals); if ( $row_count > 1 ) { logger_err("err> db_exec_val received row_count>1 $sql"); exit(1); } return ($stm->fetchrow_hashref()); } #---------------------------------------------------------------------------- # db_exec_row (returns first field value from a single row from a sql statement) # # in: connection, sql statement, parameter value array # out: field value, rows affected # #---------------------------------------------------------------------------- sub db_exec_val { my($conn, $sql, @parm_vals) = @_; my($stm, $row_count) = db_exec_rows($conn, $sql, @parm_vals); if ( $row_count > 1 ) { logger("err> db_exec_val received row_count>1 $sql"); exit(1); } my $val; $stm->bind_col(1,\$val); $stm->fetch(); logger("db> $sql val=$val"); return $val; } #---------------------------------------------------------------------------- # db_exec (exec sql statement) # # in: connection, sql statement, parameter value array # out: rows affected # #---------------------------------------------------------------------------- sub db_exec { my($conn, $sql, @parm_vals) = @_; my($stm, $row_count) = db_exec_rows($conn, $sql, @parm_vals); return ($row_count); } #------------------------------------------------------------------------------- # Get interface IP from interface name # # In: $if interface name # Out: $ip ip address of interface or undef if interface not found # # Note: Calling this function in parallelized mode might return a null! # #------------------------------------------------------------------------------- sub get_if_ip_from_if_name { my($if) = @_; my $cmd = $cfg{'ifconfig'}." $if 2> /dev/null"; my $tmp = `$cmd`; my $ip = undef; if ( $tmp =~ /inet addr:(\d+\.\d+\.\d+\.\d+)/ ) { $ip = $1; } return $ip; } #------------------------------------------------------------------------------- # Log # # In: $msg message #------------------------------------------------------------------------------- sub logger { my ($msg) = @_; if ( $cfg{'verbose'}) { my $stamp = date_time_string(); my $host = hostname(); print "$stamp $host - $msg\n"; } } sub logger_err { my ($msg) = @_; my $stamp = date_time_string(); my $host = hostname; print STDERR "$stamp $host - $msg\n"; } #------------------------------------------------------------------------------- # round a number # In: $_[0] a number # Out: $int an integer #------------------------------------------------------------------------------- sub round_int { ($_[0] > 0) ? int $_[0]+0.5 : int $_[0]-0.5 } #------------------------------------------------------------------------------- # Parallelize a series of tasks. Supply the number of tasks to be run simultaneously # which function it should call and its parameters # # In: $max_pids maximum number of children processes # $func_ref reference to a function to be called \&funcname # $func_arg reference to fixed function arguments # @task_list task list with items that are split among the workers # Out: 1|0 1 = success, 0 = failure # # The callback function func_ref, should accept two arguments, a scalar task entry # from the task_list and optionally a hash consisting of func_arg (fixed args) # #------------------------------------------------------------------------------- sub parallelize { my($max_pids, $func_ref, $func_args, @task_list) = @_; return parallelize_with_arg_combine($max_pids, $func_ref, $func_args, undef, @task_list); } sub parallelize_with_arg_combine { my($max_pids, $func_ref, $func_args, $arg_combine, @task_list) = @_; # # detect if we are running as a job under PBS # my $is_pbs=0; if ( defined($ENV{'PBS_ENVIRONMENT'}) && $ENV{'PBS_ENVIRONMENT'} eq "PBS_BATCH") { logger("parallelize> PBS: $ENV{'PBS_ENVIRONMENT'}, pbs job overrides max configured workers ($max_pids)!"); $is_pbs=1; $max_pids = $ENV{'PBS__NODES'}; } logger("parallelize> max workers = $max_pids"); logger("parallelize> tasks = ".scalar(@task_list)); # # by randomizing the list we get better execution times for # certain jobs [cannot do that with PBS as each node MUST get # its slice of work and therefore the overall list MUST stay consistent across all nodes!] # #@task_list = shuffle(@task_list); # # simply (!) spread the jobs across the max_pids (i.e. workers) # my @workers; my $idx = 0; while (@task_list) { my $item = shift @task_list; if ( $is_pbs ) { # the VNODENUM is zero based my $vnode = $ENV{'PBS_VNODENUM'}; if ( $vnode == $idx ) { logger("parallelize: PBS virtual node: $vnode (item=$item)"); push @{$workers[$idx]}, $item; } } else { push @{$workers[$idx]}, $item; } $idx++; if ( $idx >= $max_pids ) { $idx = 0; } } #print Dumper \@workers; #exit; # # now fork each concurrent worker, PBS based jobs are virtual node # number specific # my @children; foreach my $worker ( @workers ) { if ( !defined($worker) ) { next; } my @args = @{$worker}; my $pid; # parent if ( $pid = fork() ) { push @children, $pid; } # child elsif ( defined $pid ) { logger("parallelize> worker $$ running"); # # if the caller supports it, combine arguments with # the arg_combine delimiter to reduce the number called # func_refs. This only benefits the parallize2.plx script # which does shell command parallelization. I guess a # perl program could benefit too by having a function called # once with all arguments concatented - hmmm. # if ( defined($arg_combine) ) { my $job_args = join($arg_combine, @args); &$func_ref($job_args, %{$func_args}); } else { foreach my $arg (@args) { &$func_ref($arg, %{$func_args}); } } exit(0); } else { logger("parallelize> unable to fork: $!"); return 0; } } logger("parallelize> all workers are executing"); while(@children) { my $pid = pop @children; logger("parallelize> worker $pid waiting..."); waitpid($pid, 0); logger("parallelize> worker $pid done"); } logger("parallelize> complete"); return 1; } #------------------------------------------------------------------------------- # hex dump data #------------------------------------------------------------------------------- sub hex_dump { my ($packet) = @_; my $length = length($packet); my $i; for ( $i=0; $i<$length; $i++ ) { printf("%02x ",ord(substr($packet,$i,1))); if ( ($i+1) % 16 == 0 ) { print "\n"; } } print "\n"; } #------------------------------------------------------------------------------- # return current date time string #------------------------------------------------------------------------------- sub date_time_string { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); $year=$year+1900; $mon++; my $event_date = sprintf("%04d-%02d-%02d %02d:%02d:%02d",$year,$mon,$mday,$hour,$min,$sec); return $event_date; } #------------------------------------------------------------------------------- # # returns time and units based on input string such as 1s, 1m, 1h # #------------------------------------------------------------------------------- sub get_time_units { my ($time) = @_; my $unit; my $seconds; if ( $time =~ /(\d+)s/ ) { $seconds = $1; $unit = 's'; } elsif ( $time =~ /(\d+)m/ ) { $seconds = $1 * 60; $unit = 'm'; } elsif ( $time =~ /(\d+)h/) { $seconds = $1 * 60 * 60; $unit = 'h'; } else { $seconds = $time; $unit = 's'; } return ($seconds, $unit); } #------------------------------------------------------------------------------- # converts seconds into given units #------------------------------------------------------------------------------- sub seconds_to_units { my ($seconds, $unit) = @_; my $time; if ( $unit eq 's' ) { $time = $seconds; } elsif ( $unit eq 'm' ) { $time = sprintf("%.2f",$seconds / 60); } elsif ( $unit eq 'h' ) { $time = sprintf("%.2f",$seconds / 60 / 60); } return $time; } #------------------------------------------------------------------------------- # get host info from systemd #------------------------------------------------------------------------------- sub host_info { my ($db_conn,$server_ip) = @_; my $server_name = db_exec_val($db_conn,"SELECT strDisplayName FROM tblIPDiscovery WHERE strIPv4=?",($server_ip)); my $ipid = db_exec_val($db_conn,"SELECT intIPID FROM tblIPDiscovery WHERE strIPv4=?",($server_ip)); return ( $server_name, $ipid); } 1;