use Net::Domain qw(hostfqdn);
use strict;
################################################################################
# printHWInfo
# sub routine to print information about hardware on current host (required for
# rac provisioning) in simple xml form.
################################################################################
sub printHWInfo {
print "";
print "";
&printDiskInfo;
&printNICInfo;
&printDriveLetterInfo;
print "";
}
################################################################################
# printDiskInfo
# sub routine to print disk information in simple xml form.
################################################################################
sub printDiskInfo() {
# get list of disks.
my @diskpartOutput = &getDiskpartOutput ("list disk");
return if (scalar(@diskpartOutput) < 1);
# get output as records.
my @disks = &getOutputRecords ($diskpartOutput[0]);
return if (scalar(@disks) == 0);
print "";
# output elements are in form (Disk|Status|Size|Free|Dyn|Gpt)
# get partition info for each disk.
foreach (@disks) {
# get partition list in disk.
my @diskInfo = split /\|/;
next if ($diskInfo[4] ne ""); # we're not interested in dynamic disks.
# we have a non-dynamic disk here.
my $disk = $diskInfo[0];
# get disk details.
@diskpartOutput =
&getDiskpartOutput ("select $disk", "detail disk");
next if (scalar(@diskpartOutput) < 2);
# process output of second command.
my $diskDetails = $diskpartOutput[1];
# get disk id from output.
my ($diskID) = ($diskDetails =~ /.*?\s+id\s*:(.*?)\n.*/i);
$diskID =~ s/^\s+//;
$diskID =~ s/\s+$//;
# print disk info.
my $freeSpaceInDisk = &getSizeInMB ($diskInfo[3]);
my $diskNum = (split(/\s+/, $disk))[1];
print "";
# get partition info.
@diskpartOutput =
&getDiskpartOutput ("select $disk", "list partition");
if (scalar(@diskpartOutput) < 2) {
print "";
next;
}
# get output of second command as records.
my @partitions = &getOutputRecords ($diskpartOutput[1]);
# xml for partitions.
my $partitionXML = "";
my $decrement = 0;
# output elements are in form (Partition|Type|Size|Offset)
foreach (@partitions) {
my @partitionInfo = split /\|/;
if ($partitionInfo[1] !~ /logical/i) { # this is not a logical partition.
# there is ideally only one 'non-logical' partition
# (the extended partition itself)
$decrement++;
next;
}
# ok, a logical partition. let us obtain details for this.
my $partition = $partitionInfo[0];
# get details.
my $partitionSize = &getSizeInMB ($partitionInfo[2]);
@diskpartOutput =
&getDiskpartOutput ("select $disk",
"select $partition",
"detail partition");
next if (scalar(@diskpartOutput) < 3);
# get output of third command as records.
my @partitionDetails = &getOutputRecords ($diskpartOutput[2]);
# output elements are in form
# (Volume|Ltr|Label|Fs (format)|Type (logical etc)|Size|Status|Info)
next if (scalar (@partitionDetails) == 0); # no detailed info available.
# get fields.
my @details = split (/\|/, $partitionDetails[0]);
# correct partition number is the sequential logical-partition number
# (extended-partition number must not be considered)
my $partitionNum = (split(/\s+/, $partition))[1] - $decrement;
my $driveLtr = $details[1];
$driveLtr = "$driveLtr:" if ($driveLtr ne "");
my $formatType = $details[3];
# add partition info
# (PartitionNum, Size (in MB), FormatType, DriveLtr).
print "";
} # end partition loop.
# end disk info.
print "";
} # end disk loop.
print "";
}
################################################################################
# printNICInfo
# sub routine to print information about NICs on current host in simple xml
# form.
################################################################################
sub printNICInfo {
my $cmdOutput;
{
local $/;
undef $/;
my $netShExe = $ENV{'systemroot'}."\\system32\\netsh.exe";
die "Could not find netsh executable" if (! -e $netShExe);
open (IN, "$netShExe interface ip show address |")
or die "Cannot read netsh output: $?";
$cmdOutput = ;
close IN;
}
my @interfaces = split("\n{2,}", $cmdOutput);
return if (scalar(@interfaces) == 0);
print "";
foreach (@interfaces) {
my ($interfaceName, $ipAddr, $subnetMask) =
(/"(.*)".*?IP.*?:(.*?)\n.*?SubnetMask.*?:(.*?)\n.*/is);
# trim values.
$interfaceName =~ s/^\s+//; $interfaceName =~ s/\s+$//;
$ipAddr =~ s/^\s+//; $ipAddr =~ s/\s+$//;
$subnetMask =~ s/^\s+//; $subnetMask =~ s/\s+$//;
# replace special characters in interface name (& < > " ').
$interfaceName =~ s/&/&/g;
$interfaceName =~ s/</g;
$interfaceName =~ s/>/>/g;
$interfaceName =~ s/"/"/g;
$interfaceName =~ s/'/'/g;
# get subnet using ipaddr and subnet mask.
my @ipAddrArr = split("\\\.", $ipAddr);
my @subnetMaskArr = split("\\\.", $subnetMask);
my @subnetArr;
for (my $i = 0; $i <= $#ipAddrArr; $i++) {
push (@subnetArr, (int($ipAddrArr[$i]) & $subnetMaskArr[$i]));
}
my $subnet = join(".", @subnetArr);
print "";
}
print "";
}
################################################################################
# printDriveLetterInfo
# sub routine to print out information about used / available drive letters on
# this host (printed as XML element with comma-separated drive letters).
################################################################################
sub printDriveLetterInfo {
# 1. get local drive info using diskpart.
my @diskpartOutput = &getDiskpartOutput ("list volume");
return if (scalar(@diskpartOutput) < 1);
# get output as records.
my @volumes = &getOutputRecords ($diskpartOutput[0]);
return if (scalar(@volumes) == 0);
# output elements are in form (Volume|DriveLtr|Label|Fs|Type|Size|Status|Info)
# retrieve used letters.
my %count;
foreach (@volumes) {
# get drive letter for volume.
my @volumeInfo = split /\|/;
my ($driveLetter) = uc ($volumeInfo[1]);
if ($driveLetter =~ /(.*):/i) { # remove trailing characters, if any.
$driveLetter = $1;
}
# set count for this letter.
$count{$driveLetter} = 1 if ($driveLetter ne "");
}
# 2. retrieve mapped drive info using 'net use'.
my $netExe = $ENV{'systemroot'}."\\system32\\net.exe";
die "Could not find net executable" if (! -e $netExe);
open (NETUSE, "$netExe use |")
or die "Cannot parse mapped drive information.";
# read output of above command.
my @output;
@output = ;
close NETUSE;
# get mapped drive letters.
foreach (@output) {
$count{uc($1)} = 1 if (/.*?\s*([A-Za-z]):\s*.*?/i);
}
# 3. assign used / available drive letters.
my @usedDriveLetters;
my @availableDriveLetters;
foreach ('A'..'Z') {
my $driveLtr = "$_:";
if ($count{$_} == 1) { push (@usedDriveLetters, $driveLtr); }
else { push (@availableDriveLetters, $driveLtr); }
}
# 4. print necessary info.
print "";
}
################################################################################
# getDiskpartOutput
# sub routine to retrieve output for a diskpart command.
# param : the diskpart command.
# return: an array representing all non-blank lines of output for the given cmd.
# in case of multiple commands, the individual outputs are separated
# by blank lines.
################################################################################
sub getDiskpartOutput {
my (@diskpartCmds) = @_;
my $tmp = $ENV{TEMP}; # temporary location.
if ($tmp eq "") {
$tmp = $ENV{TMP};
}
if ($tmp eq "") {
$tmp = "C:\\temp";
}
my $tmpFile = "$tmp\\$$.out"; # for storing output.
# execute diskpart.
my $diskpartExe = $ENV{'systemroot'}."\\system32\\diskpart.exe";
die "Could not find diskpart executable" if (! -e $diskpartExe);
open (DISKPART, "| $diskpartExe > $tmpFile")
or die "Cannot execute diskpart utility: $?";
foreach my $diskpartCmd (@diskpartCmds) {
print DISKPART "$diskpartCmd\n";
}
print DISKPART "exit\n";
close DISKPART;
# read output for the command executed.
my $cmdOutput;
{
local $/;
undef $/;
open (IN, $tmpFile)
or die "Cannot read diskpart output: $?";
$cmdOutput = ;
close IN;
unlink ($tmpFile);
}
my $prompt = "DISKPART>";
# only store output between first and last prompts.
$cmdOutput =~ s/.*?$prompt(.*)$prompt.*/$1/is;
# trim output of blank lines.
$cmdOutput =~ s/^\s*\n//gs; # from start.
$cmdOutput =~ s/\n\s*$//gs; # from end.
$cmdOutput =~ s/\n\s*\n/\n/gs; # from between.
# return output as an array (outputs are separated by prompts).
return split(/\n\s*?$prompt\s*?\n/, $cmdOutput);
}
################################################################################
# getOutputRecords
# sub routine to retrieve records from the output of a diskpart command.
# param : the output as a string.
# return: an array, with each element representing fields in the output list.
# the fields in the records are separated by a pipe (|).
################################################################################
sub getOutputRecords {
my ($output) = @_;
my @outArr = split("\n", $output);
# get position of end of header.
my $headerEndPos = &getHeaderEndIndex(@outArr);
if ($headerEndPos == -1) {
# no such output.
@outArr = ();
return @outArr;
}
# end of header represents field positions. so get field positions.
my @fieldPosArr = &getFieldPositions($outArr[$headerEndPos]);
# chop header from output.
splice (@outArr, 0 , $headerEndPos + 1);
for (my $i = 0; $i <= $#outArr; $i++) {
my @out; # temporary array to hold fields.
for (my $j = 0; $j <= $#fieldPosArr; $j+=2) {
# obtain field, trim it and add it to array.
my $field = substr($outArr[$i], $fieldPosArr[$j], $fieldPosArr[$j+1]);
$field =~ s/^\s+//;
$field =~ s/\s+$//;
push(@out, $field);
}
# separate fields with a pipe.
$outArr[$i] = join("|", @out);
}
return @outArr;
}
################################################################################
# getHeaderEndIndex
# sub routine to retrieve end of header for a diskpart command output.
# param : the diskpart command output as an array (one element = one o/p line).
# return: the index of element representing the end of header,
# -1 if there is no header.
################################################################################
sub getHeaderEndIndex {
my (@disks) = @_;
for (my $i = 0; $i <= $#disks; $i++) {
return $i if ($disks[$i] =~ /^\s*(\s+\-+)*\s*$/);
}
return -1;
}
################################################################################
# getFieldPositions
# sub routine to compute starting positions and lengths of fields in a tabular
# diskpart output.
# param : a reference string consisting of '-'s representing fields and spaces
# for the gaps between fields.
# return: an array of pairs of elements representing offset / length of each
# field in the output.
################################################################################
sub getFieldPositions {
my ($refStr) = @_;
my @posArr;
# field begins at '-' and ends before ' '.
my $start = index($refStr, "-");
my $end = index($refStr, " ", $start) if ($start != -1);
while ($start != -1) {
push(@posArr, $start);
push(@posArr, $end - $start);
$start = index($refStr, "-", $end);
if ($start != -1) {
$end = index($refStr, " ", $start);
if ($end == -1) {
# make 'end' point to one element after all the '-'s.
for ($end = $start; $end <= length($refStr) && substr($refStr, $end, 1) == "-"; $end++) {}
}
}
}
return @posArr;
}
################################################################################
# getSizeInMB
# sub routine to return the size in MB, given a size string.
# param : a string in the form 'size unit'. where 'unit' is B, KB, MB or GB.
# return: the size, converted to MB.
################################################################################
sub getSizeInMB {
my ($sizeStr) = @_;
my ($size, $unit) = split (/\s+/, $sizeStr);
return $size if ($size eq "" || $size == 0);
if ($unit !~ /mb/i) {
$size = "$size.0";
if ($unit =~ /^b$/i) { $size = $size / 1000000; }
elsif ($unit =~ /^kb$/i) { $size = $size / 1000; }
elsif ($unit =~ /^gb$/i) { $size = $size * 1000; }
}
return $size;
}
############################# end of sub routines ##############################
&printHWInfo;