#!/usr/bin/perl -w
##################################################################
# (C) Copyright 2010-2015 Hewlett-Packard Development Company, L.P.
# @(#) utilities for Serviceguard Easy Deployment
# @(#) Product Name : HP Serviceguard
# @(#) Product Version : A.12.10.00
# @(#) Patch Name : 
###################################################################

use strict;

use Getopt::Long qw(GetOptions HelpMessage :config bundling no_ignore_case);
use Pod::Usage;
use Sys::Hostname;
use File::Path;
my $sgeasylib = undef;
my $sgconf = undef;
my @file_out = undef;

BEGIN {
    my $sgconffile = "/etc/cmcluster.conf";
    if (-f $sgconffile) {
        @file_out = qx(cat $sgconffile);
    } else {
        print STDERR "ERROR: Unable to run sgdeployvolumes".
                     "command on unsupported OS\n";    
      exit 1;
    }
    
    my @sglib_out = grep(/^SGLIB/, @file_out);
    my @split_sglib_name = split(/=/,$sglib_out[0]);
    chomp($split_sglib_name[1]);
    $sgeasylib = $split_sglib_name[1];
}

use lib "$sgeasylib";
use EDTools;


=head1 NAME

sgdeployvolumes - sgdeployvolumes is called to get the physical disks associated with LVM or VxVM volume groups

=cut

# arguments
#  -i <package ascii file path>  -o "option string" --optionstring String  of variable number of args
#
# example:-    
#  sgdeployvolumes -i /tmp/pkg1.ascii -o lvm_vgs="vglock|vg_dd0" -o vxvm_dgs="dg_dd0|dg_dd1"

=head1 SYNOPSIS

sgdeployvolumes -i <package_ascii_file> -o "options string"

=cut

my $sgeasyTool;
my @opt_opstring;
my $errors = 0;
my @lvm_vgs_list;
my @vxvm_dgs_list;
my @disk_list = "";
my $pkgfilepath;
my @final_pv_info = ();
my $CMD_PATH = undef;
my $VXADM_CMD_PATH= undef;
my $MC_SCRIPT_PATH=undef;


my $sgconffile = "/etc/cmcluster.conf";
if (-f $sgconffile) {
    @file_out = qx(cat $sgconffile);
} else {
    print STDERR "ERROR: Unable to run sgdeployvolumes".
                 "command on unsupported OS\n";    
  exit 1;
}

my @sgconf_out = grep(/^SGCONF/, @file_out);
my @split_sgconf_name = split(/=/,$sgconf_out[0]);
chomp($split_sgconf_name[1]);
$sgconf = $split_sgconf_name[1];

if ($^O eq 'hpux') {
    $CMD_PATH = "/usr/sbin";
    $MC_SCRIPT_PATH="/opt/cmcluster/toolkit/DRS/bin/deploymc";    
}
else {
    $CMD_PATH = "/sbin";
    $VXADM_CMD_PATH = "/usr/sbin";
    $MC_SCRIPT_PATH="/opt/drenabler/utils/deploymc";    
}

exit (1) if (parse_args());

exit(1) if (validate_and_setup_args());

exit (1) if (configure_vg_or_dg());

exit(0);


sub parse_args{

    GetOptions(
	"optionstring|o=s" => \@opt_opstring,
        "package_ascii_file|i=s" => \$pkgfilepath,
	"help|h|?"    => sub { pod2usage(-verbose => 1) },
         ) || pod2usage("Invalid argument");

    if (@ARGV){
        pod2usage("Unknown arguments on command line : " . join (" ", @ARGV));
        return 1;
    }
}

sub validate_and_setup_args {

    my $lvm_vgs;
    my $vxvm_dgs;
    $sgeasyTool = new EDTools("sgdeployvolumes");
    $sgeasyTool->screen_and_log("Running sgdeploylvm......");
    if (!$pkgfilepath) {
        $sgeasyTool->screen_and_log("ERROR: Package ascii file path not specified\n");
        return 1;
    }
    if (! -f $pkgfilepath) {
        $sgeasyTool->screen_and_log("ERROR: Package ascii file does not exist\n");
        return 1;
    }
    if (!scalar (@opt_opstring)) {
        $sgeasyTool->screen_and_log("ERROR: No option (-o) specified\n");
        return 1;
    }
    foreach my $opstring (@opt_opstring) {

        my @key_value = split('=', $opstring); 

        if ($key_value[0] eq "lvm_vgs") {
            $lvm_vgs = $key_value[1];
            if (!$lvm_vgs) {
                $sgeasyTool->screen_and_log("ERROR: No LVM VG value specified\n");
                return 1;
            }
        } elsif ($key_value[0] eq "vxvm_dgs") {
            $vxvm_dgs = $key_value[1];
            if (!$vxvm_dgs) {
                $sgeasyTool->screen_and_log("ERROR: No VxVM DG value specified\n");
                return 1;
            }
        } else {
            $sgeasyTool->screen_and_log("ERROR: Invalid option $key_value[0] specified\n");
            return 1;
        }
    }
   
    if ($lvm_vgs) {
        $lvm_vgs =~ s/\s+//g;
        @lvm_vgs_list = split('\|',$lvm_vgs);
        if (!@lvm_vgs_list) {
            $sgeasyTool->screen_and_log("ERROR: Invalid LVM VG value specified\n");
            return 1;
        }
        foreach my $lvm_vg (@lvm_vgs_list) {
            if (!$lvm_vg) {
                $sgeasyTool->screen_and_log("ERROR: lvm_vgs option is syntactically incorrect\n");
                return 1;
            }
        }
    } 
    if ($vxvm_dgs) {
        @vxvm_dgs_list = split('\|',$vxvm_dgs);
        if (!@vxvm_dgs_list) {
            $sgeasyTool->screen_and_log("ERROR: Invalid VxVM DG value specified\n");
            return 1;
        }
        foreach my $vxvm_dg (@vxvm_dgs_list) {
            if (!$vxvm_dg) {
                $sgeasyTool->screen_and_log("ERROR: vxvm_dgs option is syntactically incorrect\n");
                return 1;
            }
        }
    }
    return 0; 
}

sub configure_vg_or_dg {
  
    # LVM VGs are specified. get the physical disks for these VGs. 
    if (@lvm_vgs_list) {
        my $lvmadm_cmd = undef;
        if ($^O eq 'hpux') {
            $lvmadm_cmd = "$CMD_PATH/lvmadm -l";
        }
        else {
            $lvmadm_cmd = "$CMD_PATH/vgdisplay --noheadings --separator : -C -o vg_name,pv_name "; 
        }
        my $ret = 0;
        my @cmd_output = ();
        if ($^O eq 'hpux') {
            ($ret, @cmd_output) = runandCaptureCommand($lvmadm_cmd);
        }
        if ($ret) {
            $sgeasyTool->screen_and_log("ERROR: Could not execute lvmadm command\n");
            return 1;
        }
        my $lvmadm_cmd_vg = $lvmadm_cmd;
        foreach my $lvm_vg (@lvm_vgs_list) {
            if ($^O eq 'linux') {
                @cmd_output = ();
                $lvmadm_cmd_vg = $lvmadm_cmd_vg . " " . $lvm_vg;
                $lvmadm_cmd_vg = $lvmadm_cmd_vg . " " . "2>/dev/null";
                
                # We have to run the LVM command for each VG because multiple
                # disks for a VG are listed in different lines in Linux.
                # For ex:
                # testvg:/dev/sdi
                # testvg:/dev/sdm8
 
                ($ret, @cmd_output) = runandCaptureCommand($lvmadm_cmd_vg);
                if ($ret != 0) {
                    $sgeasyTool->screen_and_log("ERROR:Invalid LVM VG specified\n"); 
                    return 1; 
                }
                # call the linux related function to populate the disk names for VG
                return 1 if (populate_linux_disk_info_for_vg($lvm_vg,\@cmd_output));
                $lvmadm_cmd_vg = $lvmadm_cmd;
            }
            # In HP-UX we call the populate disk info for each VG
            # the cmd_output is already initialized above for HP-UX.
            else {
                return 1 if (populate_disk_info_for_vg($lvm_vg, \@cmd_output));
            }
        }
    }
    if (@vxvm_dgs_list) {
        my $vxvmadm_cmd = "$CMD_PATH/vxdisk -s list";
        my ($ret, @cmd_output) = runandCaptureCommand($vxvmadm_cmd);
        if ($ret) {
            $sgeasyTool->screen_and_log("ERROR: Could not execute $vxvmadm_cmd command\n");
            return 1;
        }
        foreach my $vxvm_dg (@vxvm_dgs_list) {
            return 1 if (populate_disk_info_for_dg($vxvm_dg, \@cmd_output));
        }
        
    } 
    if (@final_pv_info) {
        if (-f $MC_SCRIPT_PATH) { 
            my $final_pv_list = "";
            foreach my $pv (@final_pv_info) {
                $final_pv_list = $final_pv_list . $pv;    
            }
            my $DEPLOY_MC_CMD = $MC_SCRIPT_PATH . " -i $pkgfilepath -p \"" . $final_pv_list . "\"";
            my ($ret , @cmd_output) = runandCaptureCommand($DEPLOY_MC_CMD);
            if (@cmd_output) {
                print @cmd_output;
            }
            return 1 if ($ret); 
        }
    }
    return 0;
}

sub runandCaptureCommand {
    my ($myCmd) = @_;
    my @lines = ();
    my @output = ();
    open(CMD, "$myCmd |") or return ($?>>8 , @output);
    while (<CMD>) {
        push(@output, $_);
        last if eof(CMD);
    }
    close(CMD);
    my $exitcode = ($? >> 8);
    return($exitcode, @output);
}
sub populate_linux_disk_info_for_vg {
    my ($lvm_vg, $cmd_output) = @_;
    for (my $_idx = 0; $_idx <scalar(@$cmd_output) ; $_idx++) {
        $$cmd_output[$_idx] =~ s/\s+//g;
        my @lvm_vg_line_info = split(":",$$cmd_output[$_idx]);
        if (@final_pv_info) {
            push (@final_pv_info, "|");
        }
        chomp($lvm_vg_line_info[1]);
        push (@final_pv_info, $lvm_vg_line_info[1]);
    }
    return 0;
}
sub populate_disk_info_for_vg {
    my ($lvm_vg, $cmd_output) = @_;
    my $lvm_vg_to_match = "/dev/" . $lvm_vg;
    my @disk_info_for_vg = ();
    my $found = 0;
    for (my $_idx = 0; $_idx <=@$cmd_output ; $_idx++) {
        if ($$cmd_output[$_idx] && $$cmd_output[$_idx] =~ "VG Name") {
            $$cmd_output[$_idx] =~ s/\s+/ /g;
            my @lvm_vg_line_info = split(" ",$$cmd_output[$_idx]);
            if ($lvm_vg_line_info[2] eq  $lvm_vg_to_match) {
                $found = 1;
                # we have found the VG provided.
                # get the PV entries
                $_idx = $_idx + 1;
                while ($$cmd_output[$_idx] && ($$cmd_output[$_idx] =~ "PV Name"|| 
                       $$cmd_output[$_idx] =~ /dev/)) {
                    $$cmd_output[$_idx] =~ s/\s+/ /g;
                    my @disk_info = (); 
                    if ($$cmd_output[$_idx] =~ "PV Name") {
                        @disk_info = split(" ",$$cmd_output[$_idx]);
                        return 1 if (!$disk_info[2]);
                        push (@disk_info_for_vg,$disk_info[2]);
                    }
                    else {
                        return 1 if (!$$cmd_output[$_idx]);
                        push (@disk_info_for_vg, $$cmd_output[$_idx]);
                    }
                    $_idx = $_idx + 1;
                }
                last;
            }
        } 
    }
    if (!$found) {
        $sgeasyTool->screen_and_log("ERROR: LVM VG $lvm_vg is not configured\n");
        return 1;
    }

    foreach my $disk_val (@disk_info_for_vg) {
        if ($disk_val =~ "/dev/dsk|/dev/cdisk") {
            my $cmd = "$CMD_PATH/ioscan -m " ;
            if ($disk_val =~ "/dev/dsk") {
                $cmd = $cmd . "dsf " . $disk_val;
            }
            else {
                $cmd = $cmd . "cluster_dsf " . $disk_val;
            }
            my ($ret, @output) = runandCaptureCommand($cmd);
            if ($ret) {
                $sgeasyTool->screen_and_log("ERROR: Unable to execute ioscan command on $disk_val\n");
                return 1;
            } 
            # Third line in the output is always the device name
            # Example of ioscan -m dsf 
            # ioscan -m dsf /dev/dsk/c5t1d0
            # Persistent DSF           Legacy DSF(s)
            # ========================================
            # /dev/disk/disk25         /dev/dsk/c5t1d0
            $output[2] =~ s/\s+/ /g;
            my @pers_dev_info_list = split(" ", $output[2]);
            if (@final_pv_info) {
                push (@final_pv_info, "|");
            }
            if ($disk_val =~ "/dev/dsk") {
                push (@final_pv_info,$pers_dev_info_list[0]); 
            }

            # in case of cluster dsf it is second field
            # ioscan -m cluster_dsf /dev/dsk/c0t13d0
            # Cluster DSF             Persistent DSF           Legacy DSF(s)
            # =================================================================
            # /dev/cdisk/disk20       /dev/disk/disk41         /dev/dsk/c0t13d0

            else {
                push (@final_pv_info,$pers_dev_info_list[1]); 
            }
        }  
        # persistent device was specified (/dev/disk/..)
        else {
            if (@final_pv_info) {
                push (@final_pv_info,"|");
            }
            push (@final_pv_info,$disk_val);
        }
    }
    return 0;
}

sub populate_disk_info_for_dg {
    my ($vxvm_dg, $cmd_output) = @_;
    my $vxddladm_cmd = undef;
    my @list_of_devices_for_dg = ();
    my $tmp_device;
    my @disk_ouput;
    my @dg_output;
    my $is_new_naming_scheme = 0;
    if ($^O eq 'hpux') {
        $vxddladm_cmd = "$CMD_PATH/vxddladm get namingscheme";
    }
    else {
        $vxddladm_cmd = "$VXADM_CMD_PATH/vxddladm get namingscheme";
    }
    my ($ret,@vxadm_output) = runandCaptureCommand($vxddladm_cmd);
    if ($ret) {
        $sgeasyTool->screen_and_log("ERROR: vxddladm command returned error\n");
        return 1;    
    }    
    if ($^O eq 'hpux' && ! grep {/OS Native/} $vxadm_output[2]) {
        $sgeasyTool->screen_and_log("ERROR: Naming scheme for disk is not OS Native " .
                                    "change the naming scheme\nto OS Native before " .
                                    "configuring diskgroups.\n");
        return 1;
    }
    if (grep {/New/} $vxadm_output[2]) {
        $is_new_naming_scheme = 1;
    }
    if ($^O eq 'hpux') { 
        # Check if the machine is HPVM. In case of HPVM the disk if they are exported
        # in some fashion, we will end up in having disk numbers starting with 
        # >512(for ex:/dev/disk/c513t0d1) and so on.
        my $isHpvm = 0;
        my $model_cmd = "/usr/bin/model";
        my @modelcmd_output;
        ($ret, @modelcmd_output) = runandCaptureCommand($model_cmd);
        if ($ret) {
            $sgeasyTool->screen_and_log("ERROR: In running model command\n");
            return 1;
        }
        $isHpvm = 1 if (grep {/Virtual Machine/} @modelcmd_output);
        if (grep {/c513/} @$cmd_output && $isHpvm) {
            $sgeasyTool->screen_and_log("ERROR: Disks are imported in unsupported fashion " . 
                                        " for this script.\n Please run vxddladm to correct ".
                                        "naming scheme before using the script\n");
            return 1;
        }    
    }
    if (!grep {/\b$vxvm_dg\b/} @$cmd_output) {
        $sgeasyTool->screen_and_log("ERROR: $vxvm_dg is not configured.\n");
        return 1;
    }    
    foreach my $line (@$cmd_output) {
        chomp($line);
        @disk_ouput = grep {/^Disk:/} $line;
        if (@disk_ouput) {
            my @disk_output_fields = split (":",$disk_ouput[0]);
            $disk_output_fields[1] =~ s/^\s+//g;
            if ($disk_output_fields[1]) {
                $tmp_device = $disk_output_fields[1];           
            }
        }
        else {
            @dg_output = grep {/^dgname:/} $line;
            if (@dg_output) {
                my @dg_output_fields = split (":",$dg_output[0]);
                if (!$dg_output_fields[1]) {
                    next;
                }
                $dg_output_fields[1] =~ s/^\s+//g;     
                if ($dg_output_fields[1] eq $vxvm_dg) {
                    push (@list_of_devices_for_dg,$tmp_device);
                }
                $tmp_device="";
            } 
        }
    } 
    foreach my $disk_device (@list_of_devices_for_dg) {
        my $cmd = undef;
        if ($^O eq 'hpux') {
            if ($is_new_naming_scheme) {
                $disk_device = "/dev/disk/" . $disk_device;
            }
            else {
                $disk_device = "/dev/dsk/" . $disk_device;
            }
            my $cmd = "$CMD_PATH/ioscan -m dsf " . $disk_device;
            my ($ret, @output) = runandCaptureCommand($cmd);
            if ($ret) {
                $sgeasyTool->screen_and_log("ERROR: Unable to run ioscan command for disk $disk_device.\n");
                return 1;
            }
            # Third line in the output is always the device name
            # Example of ioscan -m dsf 
            # ioscan -m dsf /dev/dsk/c5t1d0
            # Persistent DSF           Legacy DSF(s)
            # ========================================
            # /dev/disk/disk25         /dev/dsk/c5t1d0
            $output[2] =~ s/\s+/ /g;
            my @pers_dev_info_list = split(" ", $output[2]);
            if (@final_pv_info) {
                push (@final_pv_info, "|");
            }
            push (@final_pv_info,$pers_dev_info_list[0]); 
        }
        else {
            my $device_val = "/dev/" . $disk_device;
            if (@final_pv_info) {
                push (@final_pv_info, "|");
            }
            push (@final_pv_info,$device_val);
        }
    }
    return 0;
}
