package probe_utils; # IBM(c) 2016 EPL license http://www.eclipse.org/legal/epl-v10.html use strict; use File::Path; use File::Copy; use Time::Local; use Socket; use List::Util qw/sum/; #----------------------------------------- =head3 Description: Format output message depending on probe framework requirement Format is [] : The valid are debug, warning, failed, info and ok Arguments: output: where should the message be output The vaild values are: stdout : print message to STDOUT a file name: print message to the specified "file name" tag: the type of message, the valid values are: d: debug w: warning f: failed o: ok i: info If tag is NULL, output message without a tag msg: the information need to output Returns: 1 : Failed 0 : success =cut #---------------------------------------- sub send_msg { my $output = shift; $output = shift if (($output) && ($output =~ /probe_utils/)); my $tag = shift; my $msg = shift; my $flag=""; if ($tag eq "d") { $flag = "[debug] :"; } elsif ($tag eq "w") { $flag = "[warning]:"; } elsif ($tag eq "f") { $flag = "[failed] :"; } elsif ($tag eq "o") { $flag = "[ok] :"; } elsif ($tag eq "i") { $flag = "[info] :"; } if ($output eq "stdout") { print "$flag$msg\n"; } elsif($output) { syswrite $output, "$flag$msg\n"; } else { if (!open(LOGFILE, ">> $output")) { return 1; } print LOGFILE "$flag$msg\n"; close LOGFILE; } return 0; } #------------------------------------------ =head3 Description: Test if a string is a IP address Arguments: addr: the string want to be judged Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_ip_addr { my $addr = shift; $addr = shift if (($addr) && ($addr =~ /probe_utils/)); return 0 unless ($addr); return 0 if ($addr !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); return 0 if ($1 > 255 || $1 == 0 || $2 > 255 || $3 > 255 || $4 > 255); return 1; } #------------------------------------------ =head3 Description: Test if a IP address belongs to a network Arguments: net : network address, such like 10.10.10.0 mask: network mask. suck like 255.255.255.0 ip: a ip address Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_ip_belong_to_net { my $net = shift; $net = shift if (($net) && ($net =~ /probe_utils/)); my $mask = shift; my $targetip = shift; return 0 if ($net !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); return 0 if ($mask !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); return 0 if (!is_ip_addr($targetip)); my $bin_mask = 0; $bin_mask = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($mask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); my $bin_ip = 0; $bin_ip = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($targetip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); my $tmp_net = $bin_mask & $bin_ip; my $bin_net = 0; $bin_net = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($net =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); return 0 if ($tmp_net != $bin_net); return 1; } #------------------------------------------ =head3 Description: Get distro name of current operating system Arguments: None Returns: A string, include value are sles, redhat and ubuntu =cut #------------------------------------------ sub get_os { my $os = "unknown"; my $output = `cat /etc/*release* 2>&1`; if ($output =~ /suse/i) { $os = "sles"; } elsif ($output =~ /Red Hat/i) { $os = "redhat"; } elsif ($output =~ /ubuntu/i) { $os = "ubuntu"; } return $os; } sub _netplan_get { my $key = shift; my ($val, $error) = _capture_command( sub { my $fh = shift; return scalar <$fh>; }, 1, 'netplan', 'get', $key ); return if defined $error; chomp $val if defined $val; return $val; } sub _command_available { my $cmd = shift; for my $dir (split /:/, $ENV{PATH} || '') { next unless $dir; return 1 if -x "$dir/$cmd"; } return 0; } sub _capture_command { my ($read_output, $exec_failure_status, @command) = @_; my $pid = open(my $fh, '-|'); unless (defined $pid) { my $error = "unable to start '$command[0]': $!"; return wantarray ? (undef, $error) : undef; } if ($pid == 0) { open(STDERR, '>', '/dev/null'); exec @command; exit $exec_failure_status; } my $output = $read_output->($fh); close $fh; my $status = $?; if ($status) { my $error; if ($status == -1) { $error = "failed to execute: $!"; } elsif ($status & 127) { $error = 'terminated by signal ' . ($status & 127); } else { $error = 'exited with status ' . ($status >> 8); } $error = "'$command[0]' $error"; return wantarray ? (undef, $error) : undef; } return wantarray ? ($output, undef) : $output; } sub _capture_command_output { my @command = @_; my ($output, $error) = _capture_command( sub { my $fh = shift; local $/; return scalar <$fh>; }, 127, @command ); return wantarray ? (undef, $error) : undef if defined $error; $output = '' unless defined $output; return wantarray ? ($output, undef) : $output; } sub _networkd_config_dirs { return ('/run/systemd/network', '/etc/systemd/network'); } sub _networkd_name_matches { my ($names, $nic) = @_; foreach my $name (split /\s+/, $names) { return 1 if $name eq $nic; } return 0; } sub _networkd_address_matches { my ($addresses, $ip) = @_; foreach my $address (split /\s+/, $addresses) { return 1 if $address =~ /^\Q$ip\E(?:\/\d+)?$/; } return 0; } sub _networkd_file_has_static_ip { my ($file, $nic, $ip) = @_; my $section = ''; my $matched_nic = 0; my $matched_ip = 0; my $dhcp4 = 0; open(my $fh, '<', $file) or return 0; while (my $line = <$fh>) { chomp $line; $line =~ s/^\s+|\s+$//g; next if $line eq '' || $line =~ /^[#;]/; $line =~ s/\s*[#;].*$//; if ($line =~ /^\[([^\]]+)\]$/) { $section = lc $1; next; } if ($section eq 'match' && $line =~ /^Name\s*=\s*(.+)$/i) { $matched_nic = 1 if _networkd_name_matches($1, $nic); next; } if ($section eq 'network' && $line =~ /^Address\s*=\s*(.+)$/i) { $matched_ip = 1 if _networkd_address_matches($1, $ip); next; } if ($section eq 'network' && $line =~ /^DHCP\s*=\s*(.+)$/i) { $dhcp4 = 1 if $1 =~ /^(?:yes|true|ipv4|both)$/i; } } close $fh; return $matched_nic && $matched_ip && !$dhcp4; } sub _networkd_has_static_ip { my ($nic, $ip) = @_; foreach my $dir (_networkd_config_dirs()) { next unless -d $dir; opendir(my $dh, $dir) or next; foreach my $file (sort grep { /\.network$/ } readdir($dh)) { return 1 if _networkd_file_has_static_ip("$dir/$file", $nic, $ip); } closedir $dh; } return 0; } sub _netplan_has_static_ip { my ($nic, $ip) = @_; my $netplan_get_supported = 0; if (_command_available('netplan')) { (my $escaped_nic = $nic) =~ s/\./\\./g; for my $devtype (qw(ethernets bonds bridges vlans)) { my $dev_obj = _netplan_get("$devtype.$escaped_nic"); $netplan_get_supported = 1 if defined $dev_obj; next unless defined $dev_obj; next if ($dev_obj eq 'null' || $dev_obj eq ''); my $addresses = _netplan_get("$devtype.$escaped_nic.addresses"); next unless defined $addresses; next if ($addresses eq 'null' || $addresses eq ''); next unless $addresses =~ /(?:^|[\s\[,"'])\Q$ip\E(?:\/\d+)?(?:$|[\s\],"'])/m; my $dhcp_val = _netplan_get("$devtype.$escaped_nic.dhcp4"); return 0 if defined $dhcp_val && $dhcp_val =~ /true/i; return 1; } } return 0 if $netplan_get_supported; # Older netplan releases can render networkd files but do not support # "netplan get", so use the generated files as a conservative fallback. return _networkd_has_static_ip($nic, $ip); } sub dhcp_query_reply_mac { my ($reply, $node, $ip) = @_; return unless defined $reply && defined $node && defined $ip; return $1 if $reply =~ /\Q$node\E:\s+ip-address\s*=\s*\Q$ip\E,?\s+hardware-address\s*=\s*([0-9a-f:]+)/i; return; } sub dhcp_query_reply_matches { my ($reply, $node, $ip, $mac) = @_; return 0 unless defined $mac; my $reply_mac = dhcp_query_reply_mac($reply, $node, $ip); return 0 unless defined $reply_mac; return lc($reply_mac) eq lc($mac); } #------------------------------------------ =head3 Description: Test if a IP address is a static IP address Arguments: ip: a ip address nic: the network adapter which ip belongs to Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_static_ip { my $ip = shift; $ip = shift if (($ip) && ($ip =~ /probe_utils/)); my $nic = shift; my $os = get_os(); my $rst = 0; `which nmcli > /dev/null 2>&1`; unless ($?) { my $device_connection = `nmcli device show $nic 2>&1 | grep GENERAL.CONNECTION`; my $net_name; if ($device_connection =~ /GENERAL.CONNECTION:\s*(.+)/) { $net_name = $1; } if ($net_name) { my $ipv4_method = `nmcli con show "$net_name" 2>&1 | grep -E 'ipv4.method'`; unless ($?) { if ($ipv4_method =~ /manual/) { $rst = 1; } return $rst; } } } if ($os =~ /redhat/) { my $output1 = `cat /etc/sysconfig/network-scripts/ifcfg-$nic 2>&1 |grep -i IPADDR`; my $output2 = `cat /etc/sysconfig/network-scripts/ifcfg-$nic 2>&1 |grep -i BOOTPROTO`; $rst = 1 if (($output1 =~ /$ip/) && ($output2 =~ /static|none/i)); } elsif ($os =~ /sles/) { my $output1 = `cat /etc/sysconfig/network/ifcfg-$nic 2>&1 |grep -i IPADDR`; my $output2 = `cat /etc/sysconfig/network/ifcfg-$nic 2>&1 |grep -i BOOTPROTO`; $rst = 1 if (($output1 =~ /$ip/) && ($output2 =~ /static/i)); } elsif ($os =~ /ubuntu/) { return 1 if _netplan_has_static_ip($nic, $ip); my $output = `cat /etc/network/interfaces 2>&1`; $rst = 1 if (($output =~ /iface\s+\Q$nic\E\s+inet\s+static/i) && ($output =~ /\baddress\s+\Q$ip\E(?:\/\d+)?\b/i)); } return $rst; } #------------------------------------------ =head3 Description: Test if SELinux is opened in current operating system Arguments: None Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_selinux_enable { if (-e "/usr/sbin/selinuxenabled") { `/usr/sbin/selinuxenabled`; if ($? == 0) { return 1; } else { return 0; } } else { return 0; } } #------------------------------------------ =head3 Description: Test if SELinux is enforcing in current operating system Arguments: None Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_selinux_enforcing { my $retval = 0; if (-e "/usr/sbin/getenforce") { my $enforce_mode = `/usr/sbin/getenforce`; chomp $enforce_mode; if ($enforce_mode eq "Enforcing") { $retval = 1; } } return $retval; } #------------------------------------------ =head3 Description: Test if firewall is opened in current operating system Arguments: None Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_firewall_open { my $output; my $rst = 0; my $output = `iptables -nvL -t filter 2>&1`; if ($output =~ /DROP|RETURN/) { # If output contains DROP or RETURN rules, assume firewall # is blocking some traffic $rst=1; } return $rst; } #------------------------------------------ sub _tcp_listener_output { my ($include_process, $errormsg_ref) = @_; my @commands = $include_process ? (['ss', '-lntp'], ['netstat', '-tnlp']) : (['ss', '-lnt'], ['netstat', '-ant']); my @available_commands; my @errors; foreach my $command (@commands) { next unless _command_available($command->[0]); push @available_commands, $command->[0]; my ($output, $error) = _capture_command_output(@$command); return $output if defined $output; push @errors, $error || "'$command->[0]' failed"; } if ($errormsg_ref) { if (@available_commands) { $$errormsg_ref = 'Unable to inspect TCP listeners: ' . join('; ', @errors); } else { $$errormsg_ref = "Unable to inspect TCP listeners: neither 'ss' nor 'netstat' is available"; } } return; } sub _tcp_listener_output_has_port { my ($output, $port, $process_pattern) = @_; return 0 unless defined($output) && defined($port); return 0 unless $port =~ /^\d+$/ && $port > 0 && $port <= 65535; foreach my $line (split /\n/, $output) { $line =~ s/^\s+|\s+$//g; my @fields = split /\s+/, $line; next unless @fields >= 4; next unless grep { $_ eq 'LISTEN' } @fields; next unless $fields[3] =~ /:(\d+)$/ && $1 == $port; next if defined($process_pattern) && $line !~ $process_pattern; return 1; } return 0; } =head3 Description: Test if a TCP port has a listening socket. Prefer ss on modern Linux systems and fall back to netstat for older installations. Arguments: port: TCP port number process_pattern: optional regular expression that must match the listener line errormsg_ref: optional output reference populated when listener inspection cannot be performed Returns: 1 : listening 0 : inspected successfully and not listening undef : listener inspection could not be performed =cut sub is_tcp_port_listening { my $port = shift; $port = shift if defined($port) && $port =~ /probe_utils/; my $process_pattern = shift; my $errormsg_ref = shift; my $output = _tcp_listener_output(defined($process_pattern), $errormsg_ref); return unless defined $output; return _tcp_listener_output_has_port($output, $port, $process_pattern); } #------------------------------------------ =head3 Description: Test if http service is ready to use in current operating system Arguments: ip: http server's ip errormsg_ref: (output attribute) if there is something wrong for HTTP service, this attribute save the possible reason. Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_http_ready { my $mnip = shift; $mnip = shift if (($mnip) && ($mnip =~ /probe_utils/)); my $httpport = shift; my $installdir = shift; my $errormsg_ref = shift; my $listener_error; my $http_listening = is_tcp_port_listening($httpport, qr/(?:httpd|apache)/, \$listener_error); unless (defined $http_listening) { $$errormsg_ref = $listener_error; return 0; } if (!$http_listening) { $$errormsg_ref = "The port defined in 'site' table HTTP is not listening"; return 0; } my $test_file = "efibootmgr"; my $http = "http://$mnip:$httpport/$installdir/postscripts/$test_file"; my %httperror = ( "400" => "The request $http could not be understood by the server due to malformed syntax", "401" => "The request requires user authentication.", "403" => "The server understood the request, but is refusing to fulfill it.", "404" => "The server has not found anything matching the test Request-URI $http.", "405" => "The method specified in the Request-Line $http is not allowe.", "406" => "The method specified in the Request-Line $http is not acceptable.", "407" => "The wget client must first authenticate itself with the proxy.", "408" => "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time.", "409" => "The request could not be completed due to a conflict with the current state of the resource.", "410" => "The requested resource $http is no longer available at the server and no forwarding address is known.", "411" => "The server refuses to accept the request without a defined Content- Length.", "412" => "The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server.", "413" => "The server is refusing to process a request because the request entity is larger than the server is willing or able to process.", "414" => "The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret.", "415" => "The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method.", "416" => "Requested Range Not Satisfiable", "417" => "The expectation given in an Expect request-header field could not be met by this server", "500" => "The server encountered an unexpected condition which prevented it from fulfilling the request.", "501" => "The server does not recognize the request method and is not capable of supporting it for any resource.", "502" => "The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the reques.", "503" => "The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.", "504" => "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI or some other auxiliary server it needed to access in attempting to complete the request.", "505" => "The server does not support, or refuses to support, the HTTP protocol version that was used in the request message."); my $tmpdir = "/tmp/xcatprobe$$/"; if(! mkpath("$tmpdir")){ $$errormsg_ref = "Prepare test environment error: $!"; return 0; } my @outputtmp = `wget -O $tmpdir/$test_file $http 2>&1`; my $rst = $?; $rst = $rst >> 8; if ((!$rst) && (-e "$tmpdir/$test_file")) { unlink("$tmpdir/$test_file"); rmdir ("$tmpdir"); return 1; } elsif ($rst == 4) { $$errormsg_ref = "Network failure, the server refuse connection. Please check if httpd service is running first."; } elsif ($rst == 5) { $$errormsg_ref = "SSL verification failure, the server refuse connection"; } elsif ($rst == 6) { $$errormsg_ref = "Username/password authentication failure, the server refuse connection"; } elsif ($rst == 8) { my $returncode = $outputtmp[2]; chomp($returncode); $returncode =~ s/.+(\d\d\d).+/$1/g; if(exists($httperror{$returncode})){ $$errormsg_ref = $httperror{$returncode}; }else{ #should not hit this block normally $$errormsg_ref = "Unknown return code of wget <$returncode>."; } } unlink("$tmpdir/$test_file"); if(! rmdir ("$tmpdir")){ $$errormsg_ref .= " Clean test environment error(rmdir $tmpdir): $!"; } return 0; } #------------------------------------------ =head3 Description: Test if tftp service is ready to use in current operating system Arguments: ip: tftp server's ip Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_tftp_ready { my $mnip = shift; $mnip = shift if (($mnip) && ($mnip =~ /probe_utils/)); my $tftpdir = shift; my $test_dir = $tftpdir . "/tftptest/"; my $old_umask = umask(022); system("mkdir -p $test_dir"); rename("/$test_dir/tftptestt.tmp", "/$test_dir/tftptestt.tmp.old") if (-e "/$test_dir/tftptestt.tmp"); rename("./tftptestt.tmp", "./tftptestt.tmp.old") if (-e "./tftptestt.tmp"); system("date > /$test_dir/tftptestt.tmp"); umask($old_umask); my $output = `tftp -4 -v $mnip -c get /tftptest/tftptestt.tmp 2>&1`; if ((!$?) && (-s "./tftptestt.tmp")) { unlink("./tftptestt.tmp"); rename("./tftptestt.tmp.old", "./tftptestt.tmp") if (-e "./tftptestt.tmp.old"); rename("/$test_dir/tftptestt.tmp.old", "/$test_dir/tftptestt.tmp") if (-e "/$test_dir/tftptestt.tmp.old"); system("rm -rf $test_dir"); return 1; } else { rename("./tftptestt.tmp.old", "./tftptestt.tmp") if (-e "./tftptestt.tmp.old"); rename("/$test_dir/tftptestt.tmp.old", "/$test_dir/tftptestt.tmp") if (-e "/$test_dir/tftptestt.tmp.old"); system("rm -rf $test_dir"); return 0; } } #------------------------------------------ =head3 Description: Test if DNS service is ready to use in current operating system Arguments: ip: DNS server's ip Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_dns_ready { my $mnip = shift; $mnip = shift if (($mnip) && ($mnip =~ /probe_utils/)); my $serverip = shift; my $hostname = shift; my $domain = shift; my $output = `nslookup -type=A $mnip $serverip 2>&1`; if ($?) { return 0; } else { chomp($output); my $tmp = grep {$_ =~ "Server:[\t\s]*$serverip"} split(/\n/, $output); return 0 if ($tmp == 0); $tmp = grep {$_ =~ "name = $hostname\.$domain"} split(/\n/, $output); return 0 if ($tmp == 0); return 1; } } #------------------------------------------ =head3 Description: Calculate network address from ip and netmask Arguments: ip: ip address mask: network mask Returns: network : The network address =cut #------------------------------------------ sub get_network { my $ip = shift; $ip = shift if (($ip) && ($ip =~ /probe_utils/)); my $mask = shift; my $net = ""; return $net if (!is_ip_addr($ip)); return $net if ($mask !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); my $bin_mask = unpack("N", inet_aton($mask)); my $bin_ip = unpack("N", inet_aton($ip)); my $net_int32 = $bin_mask & $bin_ip; $net = ($net_int32 >> 24) . "." . (($net_int32 >> 16) & 0xff) . "." . (($net_int32 >> 8) & 0xff) . "." . ($net_int32 & 0xff); return "$net/$mask"; } #------------------------------------------ =head3 Description: Check if the free space of specific directory is more than expected value Arguments: targetdir: The directory needed to be checked expect_free_space: the expected free space for above directory Returns: 0: the free space of specific directory is less than expected value 1: the free space of specific directory is more than expected value 2: the specific directory isn't mounted on standalone disk. it is a part of "/" =cut #------------------------------------------ sub is_dir_has_enough_space{ my $targetdir=shift; $targetdir = shift if (($targetdir) && ($targetdir =~ /probe_utils/)); my $expect_free_space = shift; my @output = `df -k`; foreach my $line (@output){ chomp($line); my @line_array = split(/\s+/, $line); if($line_array[5] =~ /^$targetdir$/){ my $left_space = $line_array[3]/1048576; if($left_space >= $expect_free_space){ return 1; }else{ return 0; } } } return 2; } #------------------------------------------ =head3 Description: Convert node range in Regular Expression to a node name array Arguments: noderange : the range of node Returns: An array which contains each node name =cut #------------------------------------------ sub parse_node_range { my $noderange = shift; $noderange= shift if (($noderange) && ($noderange =~ /probe_utils/)); my @nodeslist = `nodels $noderange`; chomp @nodeslist; return @nodeslist; } #------------------------------------------ =head3 Description: Test if chrony service is ready to use in current operating system Arguments: errormsg_ref: (output attribute) if there is something wrong for chrony service, this attribute save the possible reason. Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_chrony_ready { my $errormsg_ref = shift; $errormsg_ref = shift if (($errormsg_ref) && ($errormsg_ref =~ /probe_utils/)); my $chronycoutput = `chronyc tracking 2>&1`; if ($?) { if ($chronycoutput =~ /Cannot talk to daemon/) { $$errormsg_ref = "chronyd service is not running! Please setup ntp in current node"; return 0; } $$errormsg_ref = "command 'chronyc tracking' failed, could not get status of ntp service"; return 0; } if ($chronycoutput =~ /Leap status : (.+)/) { my $status = $1; if ($status eq "Not synchronised") { $$errormsg_ref = "chronyd did not synchronize."; return 0; } } return 1; } #------------------------------------------ =head3 Description: Test if ntp service is ready to use in current operating system Arguments: errormsg_ref: (output attribute) if there is something wrong for ntp service, this attribute save the possible reason. Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_ntp_ready{ my $errormsg_ref = shift; $errormsg_ref = shift if (($errormsg_ref) && ($errormsg_ref =~ /probe_utils/)); my $cmd = 'ntpq -c "rv 0"'; $| = 1; #wait 5 seconds for ntpd synchronize at most for (my $i = 0; $i < 5; ++$i) { if(!open(NTP, $cmd." 2>&1 |")){ $$errormsg_ref = "Can't start ntpq: $!"; return 0; }else{ while() { chomp; if (/^associd=0 status=(\S{4}) (\S+),/) { my $leap=$2; last if ($leap =~ /(sync|leap)_alarm/); if ($leap =~ /leap_(none|((add|del)_sec))/){ close(NTP); return 1; } #should not hit below 3 lines normally $$errormsg_ref = "Unexpected ntpq output ('leap' status <$leap>), please contact xCAT team"; close(NTP); return 0; }elsif(/Connection refused/) { $$errormsg_ref = "ntpd service is not running! Please setup ntp in current node"; close(NTP); return 0; }else{ #should not hit this block normally $$errormsg_ref = "Unexpected ntpq output <$_>, please contact xCAT team"; close(NTP); return 0; } } } close(NTP); sleep 1; } $$errormsg_ref = "ntpd did not synchronize."; return 0; } #------------------------------------------ =head3 Description: Test if rsyslog service is ready to use in current operating system Arguments: errormsg_ref: if there is something wrong for ntp service, this attribute save the possible reason. Returns: 1 : yes 0 : no =cut #------------------------------------------ sub is_rsyslog_ready { my $errormsg_ref = shift; $errormsg_ref= shift if (($errormsg_ref) && ($errormsg_ref =~ /probe_utils/)); my $is_active = 1; my $tmp = `pidof systemd`; chomp($tmp); if ($tmp) { `systemctl is-active --quiet rsyslog 2>&1`; if ($?) { $is_active = 0; } } else { my $output = `service rsyslog status 2>&1 | grep "Active: active (running)"`; if (!$output) { $is_active = 0; } } if (!$is_active) { $$errormsg_ref = "rsyslog service is not running! Please check on current node"; return 0; } return 1; } #------------------------------------------ =head3 Description: Convert second to time Arguments: second_in : the time in seconds Returns: xx:xx:xx xx hours xx minutes xx seconds =cut #------------------------------------------ sub convert_second_to_time { my $second_in = shift; $second_in = shift if (($second_in) && ($second_in =~ /probe_utils/)); my @time = (); my $result; if ($second_in == 0) { return "00:00:00"; } my $count = 0; while ($count < 3) { my $tmp_second; if ($count == 2) { $tmp_second = $second_in % 100; } else { $tmp_second = $second_in % 60; } if ($tmp_second < 10) { push @time, "0$tmp_second"; } else { push @time, "$tmp_second"; } $second_in = ($second_in - $tmp_second) / 60; $count++; } my @time_result = reverse @time; $result = join(":", @time_result); return $result; } #------------------------------------------ =head3 Description: Call get_files_recursive to get all files under given dir, and save to target file Arguments: dir: the dir want to get files target_file: the file to save files list =cut #------------------------------------------ sub list_files_to_file { my $src_dir = shift; $src_dir = shift if (($src_dir) && ($src_dir =~ /probe_utils/)); my $target_file = shift; my $errormsg_ref = shift; my @files = (); get_files_recursive("$src_dir", \@files); my $all_file = join("\n", @files); if (!open f,"> $target_file") { $$errormsg_ref = "Can not open file $target_file to save files list"; return 1; } print f $all_file; close f; return 0; } #------------------------------------------ =head3 Description: Get all files under the given dir Arguments: dir: the dir want to get files files_path_ref: list of all files =cut #------------------------------------------ sub get_files_recursive { my $dir = shift; my $files_path_ref = shift; my $fd = undef; opendir($fd, $dir); for (; ;) { my $direntry = readdir($fd); last unless (defined($direntry)); next if ($direntry =~ m/^\.\w*/); next if ($direntry eq '..'); my $target = "$dir/$direntry"; if (-d $target) { get_files_recursive($target, $files_path_ref); } else { push(@{$files_path_ref}, glob("$target\n")); } } closedir($fd); } #------------------------------------------ =head3 Description: print table Arguments: content: double dimensional array has_title: whether has title in content eg: @content = ($title, @content1, @content2, ...... ); $has_title = 1; print_table(\@content, $has_title); or @content = (@content1, @content2, ...... ); $has_title = 0; print_table(\@content, $has_title); Ouput: -------------------------- | xxxxxxx | -------------------------- | xxx | xxxx | xx | xx | -------------------------- | xx | xxxx | xxxx | xx | -------------------------- or -------------------------- | xxx | xxxx | xx | xx | -------------------------- | xx | xxxx | xxxx | xx | -------------------------- =cut #------------------------------------------ sub print_table { my $content = shift; $content = shift if (($content) && ($content =~ /probe_utils/)); my $has_title = shift; my $title; if ($has_title) { $title = shift(@$content); } my @length_array; foreach my $row (@$content) { for (my $i = 0; $i < @{$row}; $i++) { my $ele_length = length(${$row}[$i]); $length_array[$i] = $ele_length if ($length_array[$i] < $ele_length); } } my @content_new; my @row_new; my $row_line; my $whole_length; foreach my $row (@$content) { @row_new = (); for (my $i = 0; $i < @{$row}; $i++) { push @row_new, ${$row}[$i] . " " x ($length_array[$i] - length(${$row}[$i])); } $row_line = "| " . join(" | ", @row_new) . " |"; push @content_new, $row_line; } $whole_length = length($row_line); my $title_new; my $title_length = length($title); if ($has_title) { if ($whole_length - 1 <= $title_length) { $title_new = $title; } else { $title_new = " " x (($whole_length - 2 - $title_length)/2) . "$title"; $title_new .= " " x ($whole_length - 2 - length($title_new)); $title_new = "|" . $title_new . "|"; } } my $format_line = "-" x $whole_length; print $format_line . "\n" if ($has_title); print $title_new . "\n" if ($has_title); print $format_line . "\n"; foreach (@content_new) { print $_ . "\n"; } print $format_line . "\n"; } 1;