# IBM(c) 2013-2016 EPL license http://www.eclipse.org/legal/epl-v10.html
#-------------------------------------------------------

=head1

    xCAT plugin to support z/VM (s390x)

=cut

#-------------------------------------------------------
package xCAT_plugin::zvm;
use xCAT::Client;
use xCAT::zvmUtils;
use xCAT::zvmCPUtils;
use xCAT::MsgUtils;
use Sys::Hostname;
use xCAT::Table;
use xCAT::Utils;
use xCAT::TableUtils;
use xCAT::ServiceNodeUtils;
use xCAT::NetworkUtils;
use XML::Simple;
use File::Basename;
use File::Copy;
use File::Path;
use File::Temp;
use Time::HiRes;
use POSIX;
use Getopt::Long;
use strict;
use warnings;
use Cwd;
# builtin should be set to 1 if this is xcat built into z/VM
my $builtin;
$builtin = 1;

# If the following line ("1;")is not included, you get:
# /opt/xcat/lib/perl/xCAT_plugin/zvm.pm did not return a true value
1;

#-------------------------------------------------------

=head3  handled_commands

    Return list of commands handled by this plugin

=cut

#-------------------------------------------------------
sub handled_commands {
    return {
        rpower   => 'nodehm:power,mgt',
        rinv     => 'nodehm:mgt',
        mkvm     => 'nodehm:mgt',
        rmvm     => 'nodehm:mgt',
        lsvm     => 'nodehm:mgt',
        chvm     => 'nodehm:mgt',
        rscan    => 'nodehm:mgt',
        execcmdonvm => 'nodehm:mgt',
        nodeset  => 'noderes:netboot',
        getmacs  => 'nodehm:getmac,mgt',
        rnetboot => 'nodehm:mgt',
        rmigrate => 'nodehm:mgt',
        chhypervisor => ['hypervisor:type', 'nodetype:os=(zvm.*)'],
        revacuate => 'hypervisor:type',
        reventlog => 'nodehm:mgt',
    };
}

#-------------------------------------------------------

=head3  preprocess_request

    Check and setup for hierarchy

=cut

#-------------------------------------------------------
sub preprocess_request {
    my $req      = shift;
    my $callback = shift;

    # Hash array
    my %sn;

    # Scalar variable
    my $sn;

    # Array
    my @requests;

    # If already preprocessed, go straight to request
    if ( $req->{_xcatpreprocessed}->[0] == 1 ) {
        return [$req];
    }
    my $nodes   = $req->{node};
    my $service = "xcat";

    # Find service nodes for requested nodes
    # Build an individual request for each service node
    if ($nodes) {
        $sn = xCAT::ServiceNodeUtils->get_ServiceNode( $nodes, $service, "MN" );

        # Build each request for each service node
        foreach my $snkey ( keys %$sn ) {
            my $n = $sn->{$snkey};
            print "snkey=$snkey, nodes=@$n\n";
            my $reqcopy = {%$req};
            $reqcopy->{node}                   = $sn->{$snkey};
            $reqcopy->{'_xcatdest'}            = $snkey;
            $reqcopy->{_xcatpreprocessed}->[0] = 1;
            push @requests, $reqcopy;
        }

        return \@requests;
    }
    else {

        # Input error
        my %rsp;
        my $rsp;
        $rsp->{data}->[0] = "Input noderange missing. Usage: zvm <noderange> \n";
        xCAT::MsgUtils->message( "I", $rsp, $callback, 0 );
        return 1;
    }
}

#-------------------------------------------------------

=head3  process_request

    Process the command.  This is the main call.

=cut

#-------------------------------------------------------
sub process_request {
    my $request  = shift;
    my $callback = shift;
    my $nodes    = $request->{node};
    my $command  = $request->{command}->[0];
    my $args     = $request->{arg};
    my $envs     = $request->{env};
    $::STDIN     = $request->{stdin}->[0];
    my %rsp;
    my $rsp;
    my @nodes = @$nodes;
    my $host  = hostname();

    # Directory where executables are on zHCP
    $::DIR = "/opt/zhcp/bin";

    # Directory where system config is on zHCP
    $::SYSCONF = "/opt/zhcp/conf";

    # Directory where zFCP disk pools are on zHCP
    $::ZFCPPOOL = "/var/opt/zhcp/zfcp";

    # Use sudo or not
    # This looks in the passwd table for a key = sudoer
    ($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer();

    # Process ID for xfork()
    my $pid;

    # Child process IDs
    my @children = ();

    #*** Power on or off a node ***
    if ( $command eq "rpower" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                powerVM( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach
    }    # End of case

    #*** Hardware and software inventory ***
    elsif ( $command eq "rinv" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                if (xCAT::zvmUtils->isHypervisor($_)) {
                    inventoryHypervisor( $callback, $_, $args );
                } else {
                    inventoryVM( $callback, $_, $args );
                }

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    #*** Migrate a virtual machine ***
    elsif ( $command eq "rmigrate" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                migrateVM( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    #*** Evacuate all virtual machines off a hypervisor ***
    elsif ( $command eq "revacuate" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                evacuate( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    #*** Create a virtual server ***
    elsif ( $command eq "mkvm" ) {

        # Determine if the argument is a node
        my $clone = 0;
        my %cloneInfoHash = (); # create empty hash
        if ( $args->[0] ) {
            $clone = xCAT::zvmUtils->isZvmNode( $args->[0] );
        }

        # Loop through all the arguments looking for "-imagename"
        # if an image name is found and it matches what is in the
        # doclone.txt then this must be a specialcloneVM call.
        my $argsSize = @{$args};
        my $imagename = '';

        for ( my $i = 0 ; $i < $argsSize ; $i++ ) {
            my $parm = $args->[$i];
            #xCAT::zvmUtils->printSyslog("Args[$i] =<$parm>\n");
            if ( index( $parm, "--imagename" ) != -1 ) {
                if ( ($i+1) < $argsSize) {
                    $imagename = $args->[$i+1];
                }

                if ( !length($imagename) ) {
                    xCAT::zvmUtils->printSyslog("(Error) image name value missing\n");
                    xCAT::zvmUtils->printLn( $callback, "$nodes: (Error) image name value missing\n" );
                    return;
                }

                xCAT::zvmUtils->printSyslog("mkvm for (@nodes). Parm --imagename found with value ($imagename). Check if this is special case.\n");

                %cloneInfoHash = xCAT::zvmUtils->getSpecialCloneInfo($imagename);
                if (%cloneInfoHash) {
                    xCAT::zvmUtils->printSyslog("Image found in doclone.txt for creating (@nodes)\n");
                    # call special clonevm processing
                    specialcloneVM( $callback, \@nodes, $args, \%cloneInfoHash);
                    return;
                }
            }
        }

        # looking for --osimage, if an osimage name is found, check the image's
        # comments, if the comments indicate it's an non-xcatconf4z image, update
        # the zvm table for the nodes to set the flag to be xcatconf4z=0 in
        # comments colume
        my $osimage = '';
        for ( my $i = 0 ; $i < $argsSize ; $i++ ) {
            my $parm = $args->[$i];
            if ( index( $parm, "--osimage" ) != -1 ) {
                if ( ($i+1) < $argsSize) {
                    $osimage = $args->[$i+1];
                }

                if ( !length($osimage) ) {
                    xCAT::zvmUtils->printSyslog("(Error) osimage value missing\n");
                    xCAT::zvmUtils->printLn( $callback, "$nodes: (Error) osimage value missing\n" );
                    return;
                }

                xCAT::zvmUtils->printSyslog("mkvm for (@nodes). Parm --osimage found with value ($osimage). Set the node flag to indicate if it will be deployed by using xcatconf4z image or not.\n");

                # Update the zvm table comments colume to indicate the xcatconf4z type image
                my @propNames = ('comments');
                my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $osimage, @propNames );
                if ( $propVals->{'comments'} =~ /xcatconf4z=0/ ) {
                    foreach (@nodes) {
                        @propNames = ( 'status' );
                        $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames );
                        my $status = $propVals->{'status'};
                        if ( !$status  ) {
                            $status = "XCATCONF4Z=0";
                        } else {
                            $status = "$status;XCATCONF4Z=0";
                        }
                        xCAT::zvmUtils->setNodeProp( 'zvm', $_, 'status', $status );
                    }
                }
            }
        }

        #*** Clone virtual server ***
        if ( $clone ) {
            cloneVM( $callback, \@nodes, $args );
        }

        #*** Create user entry ***
        # Create node based on directory entry
        # or create a NOLOG if no entry is provided
        else {
            foreach (@nodes) {
                $pid = xCAT::Utils->xfork();

                # Parent process
                if ($pid) {
                    push( @children, $pid );
                }

                # Child process
                elsif ( $pid == 0 ) {

                    makeVM( $callback, $_, $args );

                    # Exit process
                    exit(0);
                }    # End of elsif
                else {

                    # Ran out of resources
                    die "Error: Could not fork\n";
                }
            }    # End of foreach
        }    # End of else
    }    # End of case

    #*** Remove a virtual server ***
    elsif ( $command eq "rmvm" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                removeVM( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach
    }    # End of case

    #*** Print the user entry ***
    elsif ( $command eq "lsvm" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                listVM( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach
    }    # End of case

    #*** Change the user entry ***
    elsif ( $command eq "chvm" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                changeVM( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach
    }    # End of case

    #*** Collect node information from zHCP ***
    elsif ( $command eq "rscan" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                scanVM( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    #*** Set the boot state for a node ***
    elsif ( $command eq "nodeset" ) {
        foreach (@nodes) {

            # Only one file can be punched to reader at a time
            # Forking this process is not possible
            nodeSet( $callback, $_, $args );

        }    # End of foreach
    }    # End of case

    #*** Get the MAC address of a node ***
    elsif ( $command eq "getmacs" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                getMacs( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    #*** Boot from network ***
    elsif ( $command eq "rnetboot" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                netBoot( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach
    }    # End of case

    #*** Configure the virtualization hosts ***
    elsif ( $command eq "chhypervisor" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                changeHypervisor( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach
    }    # End of case

    #*** Retrieve or clear event logs ***
    elsif ( $command eq "reventlog" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                eventLog( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    #*** Update the node (no longer supported) ***
    elsif ( $command eq "updatenode" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                updateNode( $callback, $_, $args );

                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case


    #*** Execute a command on VM ***
    elsif ( $command eq "execcmdonvm" ) {
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                xCAT::zvmUtils->execcmdonVM($::SUDOER, $_, $args->[0]);
                # Exit process
                exit(0);
            }
            else {

                # Ran out of resources
                die "Error: Could not fork\n";
            }

        }    # End of foreach
    }    # End of case

    # Wait for all processes to end
    foreach (@children) {
        waitpid( $_, 0 );
    }

    return;
}

#-------------------------------------------------------

=head3   removeVM

    Description  : Delete the user from user directory
    Arguments    : Node to remove
                   Upstream instance ID (Optional)
                   Upstream request ID (Optional)
    Returns      : Nothing, errors returned in $callback
    Example      : removeVM($callback, $node);

=cut

#-------------------------------------------------------
sub removeVM {

    # Get inputs
    my ( $callback, $node , $args ) = @_;
    my $rc;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid', 'discovered' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");
    my $out;
    my $outmsg;

    my $requestId = "NoUpstreamRequestID"; # Default is still visible in the log
    my $objectId = "NoUpstreamObjectID"; # Default is still visible in the log
    if ($args) {
        @ARGV = @$args;

        # Parse options
        GetOptions(
            'q|requestid=s' => \$requestId  # Optional
          , 'j|objectid=s' => \$objectId  # Optional
            );
    }

    # If node is a not a discovered node then remove the userid and its related resources.
    my $discovered = $propVals->{'discovered'};
    if ( !$discovered || $discovered == 0 ) {
        # System was not discovered so we can destroy the virtual machine and
        # its resources.  First, get any vswitches in directory.
        xCAT::zvmUtils->printSyslog("Calling getVswitchIdsFromDirectory $::SUDOER, $hcp, $userId");
        my @vswitch = xCAT::zvmUtils->getVswitchIdsFromDirectory( $::SUDOER, $hcp, $userId);
        if (xCAT::zvmUtils->checkOutput( $vswitch[0] ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$vswitch[0]" );
            return;
        }
        my %vswitchhash;
        # For each vswitch revoke the userid vswitch authority
        foreach (@vswitch) {
            if (!(length $_)) {next;}
            # skip revoke if we already did one for this vswitch
            if (exists $vswitchhash{$_}) {
               xCAT::zvmUtils->printSyslog("removeVM. Skipping duplicate vswitch remove grant from: $_");
            }
            else {
                xCAT::zvmUtils->printSyslog("removeVM. Found vswitch to remove grant from: $_");
                $out = xCAT::zvmCPUtils->revokeVSwitch( $callback, $::SUDOER, $hcp, $userId, $_);
                $vswitchhash{$_} = '1';
                #caller logs any errors, so just continue.
            }
        }

        # Power off user ID
        xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId -f IMMED");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId -f IMMED"`;
        $rc = $? >> 8;
        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
            return;
        }
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ($out =~ m/Return Code: 200/i){
            if ($out =~ m/Reason Code: 12/i) {
                xCAT::zvmUtils->printSyslog("$userId already logged off.");
                $rc = 0;
            } elsif ($out =~ m/Reason Code: 16/i) {
                xCAT::zvmUtils->printSyslog("$userId in process of logging off.");
                $rc = 0;
            }
        }
        if ( $rc == -1 ) {
            xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out");
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
                return;
        }

        # Delete user entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Delete_DM -T $userId -e 0"`;
        $rc = $? >> 8;
        if ($rc == 255) { # Adding "Failed" to message will cause zhcp error dialog to be displayed to user
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
            return;
        }
        xCAT::zvmUtils->printSyslog("smcli Image_Delete_DM -T $userId -e 0 $out");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        # Check for errors
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ( $rc == -1 ) {
            return;
        }

        # Go through each pool and free zFCP devices belonging to node
        my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`);
        my $pool;
        my @luns;
        my $update;
        my $expression;
        foreach (@pools) {
            if (!(length $_)) {next;}
            $pool = xCAT::zvmUtils->replaceStr( $_, ".conf", "" );

            $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_"`;
            ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$_\"", $hcp, "removeVM", $out, $node );
            if ($rc != 0) {
                xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                return;
            }
            $out = `echo "$out" | egrep -a -i $node`;
            @luns = split("\n", $out);
            foreach (@luns) {
                if (!(length $_)) {next;}
                # Update entry: status,wwpn,lun,size,range,owner,channel,tag
                my @info = split(',', $_);
                $update = "free,$info[1],$info[2],$info[3],$info[4],,,";
                $expression = "'s#" . $_ . "#" .$update . "#i'";
                $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e $expression $::ZFCPPOOL/$pool.conf"`;
            }

            if (@luns) {
                xCAT::zvmUtils->printLn($callback, "$node: Updating FCP device pool $pool... Done");
            }
        }

        # Check for errors
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ( $rc == -1 ) {
            return;
        }
    } else {
        xCAT::zvmUtils->printLn($callback, "$node: 'discovered' property in zvm table is 1.  Node is removed only from xCAT.  Virtual machine was not deleted.");
    }

    # Remove node from 'zvm', 'nodelist', 'nodetype', 'noderes', 'nodehm', 'ppc', 'switch' tables
    # Save node entry in 'mac' table
    xCAT::zvmUtils->delTabEntry( 'zvm',      'node', $node );
    xCAT::zvmUtils->delTabEntry( 'hosts',    'node', $node );
    xCAT::zvmUtils->delTabEntry( 'nodelist', 'node', $node );
    xCAT::zvmUtils->delTabEntry( 'nodetype', 'node', $node );
    xCAT::zvmUtils->delTabEntry( 'noderes',  'node', $node );
    xCAT::zvmUtils->delTabEntry( 'nodehm',   'node', $node );
    xCAT::zvmUtils->delTabEntry( 'ppc',      'node', $node );
    xCAT::zvmUtils->delTabEntry( 'switch',   'node', $node );

    # Erase old hostname from known_hosts,all hostname are recorded in lower-case.
    my $lowernode = lc($node);
    $out = `ssh-keygen -R $lowernode`;

    # Erase hostname from /etc/hosts
    $out = `sed -i /$node./d /etc/hosts`;

    return;
}

#-------------------------------------------------------

=head3   changeVM

    Description  : Change a virtual machine's configuration
    Arguments    : Node
                   Option
    Returns      : Nothing, errors returned in $callback
    Example      : changeVM($callback, $node, $args);

=cut

#-------------------------------------------------------
sub changeVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid', 'status' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # If the node is being actively cloned then return.
    if ( $propVals->{'status'} =~ /CLONING=1/ and $propVals->{'status'} =~ /CLONE_ONLY=1/ ) {
        return;
    }

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};

    # add page or spool does not need a userid, but flag any others
    if ( $args->[0] ne "--addpagespool" ) {
        if ( !$userId ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
            return;
        }
        # Capitalize user ID
        $userId =~ tr/a-z/A-Z/;
    }

    # Get zHCP user ID
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp);
    $hcpUserId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("changeVM() node:$node userid:$userId subCmd:$args->[0] zHCP:$hcp sudoer:$::SUDOER sudo:$::SUDO");

    # Common subfunction variables
    my $out = "";
    my $outmsg;
    my $device = 0;
    my $newlinkcall = -1;   # -1: not linked, 0: linked using the old way, 1: linked using linkdiskandbringonline script.
    my $rc = 0;
    my $vdev;

    # add3390 [disk pool] [device address] [size] [mode] [read password (optional)] [write password (optional)] [multi password (optional)] [fstype (optional)]
    if ( $args->[0] eq "--add3390" ) {
        my $pool    = $args->[1];
        my $addr    = $args->[2];
        my $cyl     = $args->[3];


        # If the user specifies auto as the device address, then find a free device address
        if ($addr eq "auto") {
            $addr = xCAT::zvmUtils->getFreeAddress($::SUDOER, $node, "smapi");
        }

        my $mode = "MR";
        if ($args->[4]) {
            $mode = $args->[4];
        }

        my $readPw  = "''";
        if ($args->[5]) {
            $readPw = $args->[5];
        }

        my $writePw = "''";
        if ($args->[6]) {
            $writePw = $args->[6];
        }

        my $multiPw = "''";
        if ($args->[7]) {
            $multiPw = $args->[7];
        }

        my $fstype = '';
        if ($args->[8]) {
            $fstype = $args->[8];
        }

        # Convert to cylinders if size is given as M or G
        # Otherwise, assume size is given in cylinders
        # Note this is for a 4096 block size ECKD disk, where 737280 bytes = 1 cylinder
        if ($cyl =~ m/M/i) {
            $cyl =~ s/M//g;
            $cyl = xCAT::zvmUtils->trimStr($cyl);
            $cyl = sprintf("%.4f", $cyl);
            $cyl = ($cyl * 1024 * 1024)/737280;
            $cyl = ceil($cyl);
        } elsif ($cyl =~ m/G/i) {
            $cyl =~ s/G//g;
            $cyl = xCAT::zvmUtils->trimStr($cyl);
            $cyl = sprintf("%.4f", $cyl);
            $cyl = ($cyl * 1024 * 1024 * 1024)/737280;
            $cyl = ceil($cyl);
        } elsif ($cyl =~ m/[a-zA-Z]/) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size can be Megabytes (M), Gigabytes (G), or number of cylinders" );
            return;
        }

        # Add to directory entry
        my $error = 0;
        xCAT::zvmUtils->printSyslog( "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $readPw -W $writePw -M $multiPw\"" );
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
            "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $readPw -W $writePw -M $multiPw\"",
            $hcp, "changeVM", $out, $node );
        if ( $rc != 0 ) {
            xCAT::zvmUtils->printLn( $callback, $outmsg );
            return;
        }

        if ( $fstype && ( $fstype !~ /(ext2|ext3|ext4|xfs|swap)/i ) ) {
            $out = "(Warning) File system type can only be ext2, ext3, ext4 ,swap or xfs" . "\n";
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            $error = 1;
        }

        if ( $fstype && !$error ) { # Format the disk before making it active.
            # link the disk
            # Check if zhcp has the new routine to link and online the disk
            $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/linkdiskandbringonline $userId $addr $mode"`;

            $rc = $? >> 8;
            # Note: We don't use zvmUtils->checkSSH_Rc() in this section because some non-zero RCs are tolerated.
            if ($rc == 255) {
                xCAT::zvmUtils->printSyslog( "$node: changeVM() Unable to communicate with zHCP agent" );
                xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unable to communicate with zHCP agent: $hcp" );
                $error = 1;
            } elsif ( $rc > 0 && $rc != 127 ) {
                xCAT::zvmUtils->printSyslog( "$node: changeVM() Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" );
                xCAT::zvmUtils->printLn( $callback, "$node: changeVM()Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" );
                $error = 1;
            } elsif ( $rc ==0 ) {
                $newlinkcall = 1;
                if ($out =~ m/Success:/i){
                    # sample output=>linkdiskandbringonline maint start time: 2017-03-03-16:20:48.011
                    #                Success: Userid maint vdev 193 linked at ad35 device name dasdh
                    #                linkdiskandbringonline exit time: 2017-03-03-16:20:52.150
                    $out = `echo "$out" | egrep -a -i "Success:"`;
                    my @info = split( ' ', $out );
                    $device = "/dev/" . $info[10];
                } else {
                    xCAT::zvmUtils->printSyslog( "$node: changeVM() Error occurred in call to linkdiskandbringonline: $out" );
                    xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred in call to linkdiskandbringonline: $out" );
                    $error = 1;
                }
            } else {
                xCAT::zvmUtils->printSyslog( "$node: changeVM() Could not find zhcp linkdiskandbringonline, using old code path." );
                my $retry = 3;
                $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' );
                while ( $retry > 0 ) {
                    # wait 2 seconds for disk creation complete
                    sleep(2);
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp LINK TO $userId $addr AS $vdev M 2>&1"`;
                    $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0;
                    # Note: We don't use zvmUtils->checkSSH_Rc() in this section because
                    #       we are only interested in logging the final error.
                    if ($error) {
                        $retry -= 1;
                        $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' );
                    } else {
                        $newlinkcall = 0;
                        last;
                    }
                }

                if ( $error ) {
                    xCAT::zvmUtils->printSyslog( "Error occurred in 'vmcp LINK TO $userId $addr AS $vdev M' $device. Output: $out" );
                    xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred in 'vmcp LINK TO $userId $addr AS $vdev M' " .
                                                        "Output: $out" );
                } else {
                # make the disk online and get it's device name
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r $vdev &> /dev/null"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                        "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/cio_ignore -r $vdev &> /dev/null\"",
                        $hcp, "changeVM", $out, $node );
                    if ( $rc != 0 ) {
                        xCAT::zvmUtils->printLn( $callback, $outmsg );
                        $error = 1;
                    }
                    if ( !$error ) {
                    $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $vdev );
                        if ( $out !~ 'Done' ) {
                            xCAT::zvmUtils->printLn($callback, "$node: $out");
                            $error = 1;
                        }
                    }
                    if ( !$error ) {
                    my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat /proc/dasd/devices"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat /proc/dasd/devices\"", $hcp, "changeVM", $select, $node );
                    if ($rc != 0) {
                        xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                        $error = 1;
                    } else {
                        $select = `echo "$select" | egrep -a -i "0.0.$vdev"`;
                        chomp( $select );
                        # A sample entry:
                        # 0.0.0101(ECKD) at ( 94:     0) is dasda       : active at blocksize: 4096, 600840 blocks, 2347 MB
                        if ( $select ) {
                            my @info = split( ' ', $select );
                            $device = "/dev/" . $info[6];
                        } else {
                                xCAT::zvmUtils->printSyslog( "$node: changeVM() Error, unable to find the device " .
                                                             "on $hcp related to 0.0.$vdev" );
                                xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error, unable to find the device " .
                                                                    "on $hcp related to 0.0.$vdev" );
                                $error = 1;
                            }
                        }
                    }
                }
            }

            # format the disk
            if ( !$error ) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -y -b 4096 -d cdl -f $device 2>&1"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/dasdfmt -y -b 4096 -d cdl -f $device 2>&1\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            }
            if ( !$error ) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdasd -a $device 2>&1"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdasd -a $device 2>&1\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            }
            if ( !$error ) {
                $device .= '1';
                if ( $fstype =~ m/xfs/i ) {
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs.xfs -f $device 2>&1"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                        "ssh $::SUDOER\@$hcp \"$::SUDO mkfs.xfs -f $device 2>&1\"",
                        $hcp, "changeVM", $out, $node );
                    if ( $rc != 0 ) {
                        xCAT::zvmUtils->printLn( $callback, $outmsg );
                        $error = 1;
                    }
                } elsif ( $fstype =~ m/swap/i ) {
                        xCAT::zvmUtils->printSyslog( "the file system is swap, so no format needed");
                } else {
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs -F -t $fstype $device 2>&1"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                        "ssh $::SUDOER\@$hcp \"$::SUDO mkfs -F -t $fstype $device 2>&1\"",
                        $hcp, "changeVM", $out, $node );
                    if ( $rc != 0 ) {
                        xCAT::zvmUtils->printLn( $callback, $outmsg );
                        $error = 1;
                    }
                }
                if ( $error ) {
                    xCAT::zvmUtils->printLn( $callback, "(Warning) Cannot format disk with fstype $fstype" );
                }
            }

            # offline and detach disk using the new call so that zhcp records are updated
            if ( $newlinkcall == 1 ){
                $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/offlinediskanddetach $userId $addr"`;
                ( $rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh -o ConnectTimeout=30 $::SUDOER\@$hcp \"$::SUDO $::DIR/offlinediskanddetach $userId $addr\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            # Use old code to disable and detach
            } elsif ( $newlinkcall == 0 ) {
                $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $vdev );
                if ( $out !~ 'Done' ){
                    # Note: We are only going to log the disable failure because a failure to
                    #       disable the device doesn't mean much because we are going to immmediately
                    #       detach it.  The worst that we expect to happen in this case is that
                    #       we have a dasd enabled that is no longer attached.
                    xCAT::zvmUtils->printSyslog( "$node: $out");
                }

                xCAT::zvmUtils->printSyslog( "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null\"" );
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                xCAT::zvmUtils->printLn( $callback, $outmsg );
                xCAT::zvmUtils->printSyslog( $outmsg );
                    $error = 1;
                }
            }
            # else disk was not linked so we don't have to remove it.
        }

        if ( !$error ) {
            # Add to active configuration
            xCAT::zvmUtils->printLn( $callback, "$node: Adding 3390 disk to $userId\'s directory entry as $addr ... Done" );
            my $power = `/opt/xcat/bin/rpower $node stat`;
            if ($power =~ m/: on/i) {
                xCAT::zvmUtils->printSyslog( "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode\"" );
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            }

            if ( $error != 1 ) {
                xCAT::zvmUtils->printLn( $callback, "$node: Adding disk to $userId\'s active configuration... Done" );
            }
        }
        $out = '';
    }

    # add3390active [device address] [mode]
    elsif ( $args->[0] eq "--add3390active" ) {
        my $addr = $args->[1];
        my $mode = $args->[2];

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # add9336 [disk pool] [virtual device address] [size] [mode] [read password (optional)] [write password (optional)] [multi password (optional)] [fstype (optional)]
    elsif ( $args->[0] eq "--add9336" ) {
        my $pool    = $args->[1];
        my $addr    = $args->[2];
        my $blks    = $args->[3];

        # If the user specifies auto as the device address, then find a free device address
        if ($addr eq "auto") {
            $addr = xCAT::zvmUtils->getFreeAddress($::SUDOER, $node, "smapi");
        }

        my $mode = "MR";
        if ($args->[4]) {
            $mode = $args->[4];
        }

        my $readPw  = "''";
        if ($args->[5]) {
            $readPw = $args->[5];
        }

        my $writePw = "''";
        if ($args->[6]) {
            $writePw = $args->[6];
        }

        my $multiPw = "''";
        if ($args->[7]) {
            $multiPw = $args->[7];
        }

        my $fstype = '';
        if ($args->[8]) {
            $fstype = $args->[8];
        }

        # Convert to blocks if size is given as M or G
        # Otherwise, assume size is given in blocks
        # Note this is for a 4096 block size ECKD disk, where 737280 bytes = 1 cylinder
        if ($blks =~ m/M/i) {
            $blks =~ s/M//g;
            $blks = xCAT::zvmUtils->trimStr($blks);
            $blks = sprintf("%.4f", $blks);
            $blks = ($blks * 1024 * 1024)/512;
            $blks = ceil($blks);
        } elsif ($blks =~ m/G/i) {
            $blks =~ s/G//g;
            $blks = xCAT::zvmUtils->trimStr($blks);
            $blks = sprintf("%.4f", $blks);
            $blks = ($blks * 1024 * 1024 * 1024)/512;
            $blks = ceil($blks);
        } elsif ($blks =~ m/[a-zA-Z]/) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size can be Megabytes (M), Gigabytes (G), or number of blocks" );
            return;
        }

        # Add to directory entry
        my $error = 0;
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 9336 -a AUTOG -r $pool -u 2 -z $blks -m $mode -f 1 -R $readPw -W $writePw -M $multiPw 2>&1"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
            "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 9336 -a AUTOG -r $pool -u 2 -z $blks -m $mode -f 1 -R $readPw -W $writePw -M $multiPw 2>&1\"",
            $hcp, "changeVM", $out, $node );
        if ( $rc != 0 ) {
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            return;
        }

        if ( $fstype && ( $fstype !~ /(ext2|ext3|ext4|xfs)/i ) ) {
            $out = "(Warning) File system type can only be ext2, ext3, ext4 or xfs" . "\n";
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            $error = 1;
        }

        my $device = 0;
        my $newlinkcall = -1;   # -1: not linked, 0: linked using the old way, 1: linked using linkdiskandbringonline script.
        my $rc = 0;
        my $vdev;

        if ( $fstype && !$error ) { # Format the disk before making it active.
            # link the disk
            # Check if zhcp has the new routine to link and online the disk
            $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/linkdiskandbringonline $userId $addr $mode"`;

            $rc = $? >> 8;
            if ($rc == 255) {
                xCAT::zvmUtils->printSyslog( "$node: changeVM() Unable to communicate with zHCP agent" );
                xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unable to communicate with zHCP agent: $hcp" );
                $error = 1;
            } elsif ( $rc > 0 && $rc != 127 ) {
                xCAT::zvmUtils->printSyslog( "$node: changeVM() Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" );
                xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" );
                $error = 1;
            } elsif ( $rc ==0 ) {
                $newlinkcall = 1;
                if ($out =~ m/Success:/i){
                    # sample output=>linkdiskandbringonline maint start time: 2017-03-03-16:20:48.011
                    #                Success: Userid maint vdev 193 linked at ad35 device name dasdh
                    #                linkdiskandbringonline exit time: 2017-03-03-16:20:52.150
                    $out = `echo "$out" | egrep -a -i "Success:"`;
                    my @info = split( ' ', $out );
                    $device = "/dev/" . $info[10];
                } else {
                    xCAT::zvmUtils->printSyslog( "$node: changeVM() Error occurred in call to linkdiskandbringonline: $out" );
                    xCAT::zvmUtils->printLn( $callback, "$node: changeVM()(Error occurred in call to linkdiskandbringonline: $out" );
                    $error = 1;
                }
            } else {
                xCAT::zvmUtils->printSyslog( "$node: changeVM() Could not find zhcp linkdiskandbringonline, using old code path." );
                my $retry = 3;
                $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' );
                while ( $retry > 0 ) {
                    # wait 2 seconds for disk creation complete
                    sleep(2);
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp LINK TO $userId $addr AS $vdev M 2>&1"`;
                    $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0;
                    # Note: We don't use zvmUtils->checkSSH_Rc() in this section because
                    #       we are only interested in logging the final error.
                    if ($error) {
                        $retry -= 1;
                        $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' );
                    } else {
                        $newlinkcall = 0;
                        last;
                    }
                }

                if ( $error ) {
                    xCAT::zvmUtils->printSyslog( "Error occurred in 'vmcp LINK TO $userId $addr AS $vdev M' $device. Output: $out" );
                    xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred in 'vmcp LINK TO $userId $addr AS $vdev M' " .
                                                        "Output: $out" );
                } else {
                # make the disk online and get it's device name
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r $vdev &> /dev/null"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                        "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/cio_ignore -r $vdev &> /dev/null\"",
                        $hcp, "changeVM", $out, $node );
                    if ( $rc != 0 ) {
                        xCAT::zvmUtils->printLn( $callback, $outmsg );
                        $error = 1;
                    }
                    if ( !$error ) {
                    $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $vdev );
                        if ( $out !~ 'Done' ) {
                            xCAT::zvmUtils->printLn($callback, "$node: $out");
                            $error = 1;
                        }
                    }
                    if ( !$error ) {
                    my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat /proc/dasd/devices"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat /proc/dasd/devices\"", $hcp, "changeVM", $select, $node );
                    if ($rc != 0) {
                        xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                        $error = 1;
                    } else {
                            $select = `echo "$select" | egrep -a -i "0.0.$vdev"`;
                        chomp( $select );
                        # A sample entry:
                        # select: 0.0.0001(FBA ) at ( 94:    28) is dasdh       : active at blocksize: 512, 61440 blocks, 30 MB
                        if ( $select ) {
                            my @info = split( ' ', $select );
                            $device = "/dev/" . $info[7];
                        } else {
                                xCAT::zvmUtils->printSyslog( "$node: changeVM() Error, unable to find the device " .
                                                             "on $hcp related to 0.0.$vdev" );
                                xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error, unable to find the device " .
                                                                    "on $hcp related to 0.0.$vdev" );
                                $error = 1;
                            }
                        }
                        }
                }
            }

            #Delete the existing partition in case the disk already has partition on it
            if ( !$error ) {
                $out =
`ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk $device << EOF
d
w
EOF"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdisk $device d w\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
            }
            }

            # Create one partition to use the entire disk space
            if ( !$error ) {
                $out =
`ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk $device << EOF
n
p
1


w
EOF"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdisk $device n p 1 w\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            }
            if ( !$error ) {
                $device .= '1';
                if ( $fstype =~ m/xfs/i ) {
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs.xfs -f $device 2>&1"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                        "ssh $::SUDOER\@$hcp \"$::SUDO mkfs.xfs -f $device 2>&1\"",
                        $hcp, "changeVM", $out, $node );
                    if ( $rc != 0 ) {
                        xCAT::zvmUtils->printLn( $callback, $outmsg );
                        $error = 1;
                    }
                } else {
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs -F -t $fstype $device 2>&1"`;
                    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                        "ssh $::SUDOER\@$hcp \"$::SUDO mkfs -F -t $fstype $device\"",
                        $hcp, "changeVM", $out, $node );
                    if ( $rc != 0 ) {
                        xCAT::zvmUtils->printLn( $callback, $outmsg );
                        $error = 1;
                    }
            }
            if ( $error ) {
                xCAT::zvmUtils->printLn( $callback, "(Warning) Cannot format disk with fstype $fstype" );
            }
            }

            # offline and detach disk using new call so that zhcp records are updated
            if ( $newlinkcall == 1 ){
                $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/offlinediskanddetach $userId $addr"`;
                ( $rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh -o ConnectTimeout=30 $::SUDOER\@$hcp \"$::SUDO $::DIR/offlinediskanddetach $userId $addr\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            # Use old code to disable and detach
            } elsif ( $newlinkcall == 0 ) {
                $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $vdev );
                if ($out !~ 'Done' ){
                    # Note: We are only going to log the disable failure because a failure to
                    #       disable the device doesn't mean much because we are going to immmediately
                    #       detach it.  The worst that we expect to happen in this case is that
                    #       we have a dasd enabled that is no longer attached.
                    xCAT::zvmUtils->printSyslog( "$node: $out");
                }

                xCAT::zvmUtils->printSyslog( "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null\"" );
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                xCAT::zvmUtils->printLn( $callback, $outmsg );
                xCAT::zvmUtils->printSyslog( $outmsg );
                    $error = 1;
                }
            }
            # else disk was not linked so we don't have to remove it.
        }
        if (!$error) {
            # Add to active configuration
            xCAT::zvmUtils->printLn( $callback, "$node: Adding 9336 disk to $userId\'s directory entry as $addr ... Done" );
            my $power = `/opt/xcat/bin/rpower $node stat`;
            if ($power =~ m/: on/i) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?,
                    "ssh $::SUDOER\@$hcp $::SUDO \"$::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode\"",
                    $hcp, "changeVM", $out, $node );
                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printLn( $callback, $outmsg );
                    $error = 1;
                }
            }

            if ( $error != 1 ) {
                xCAT::zvmUtils->printLn( $callback, "$node: Adding disk to $userId\'s active configuration... Done" );
            }
        }
        $out = '';
    }

    # adddisk2pool [function] [region] [volume] [group]
    elsif ( $args->[0] eq "--adddisk2pool" ) {
        # This is no longer supported in chvm. Using chhypervisor instead.
        changeHypervisor( $callback, $node, $args );
    }

    # addzfcp2pool [pool] [status] [wwpn] [lun] [size] [owner (optional)]
    elsif ( $args->[0] eq "--addzfcp2pool" ) {
        # This is no longer supported in chvm. Using chhypervisor instead.
        changeHypervisor( $callback, $node, $args );
    }

    # addnic [address] [type] [device count]
    elsif ( $args->[0] eq "--addnic" ) {
        my $addr     = $args->[1];
        my $type     = $args->[2];
        my $devcount = $args->[3];
        my $allgood = 1;

        # Add to active configuration if possible
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "ping") {
            #$out = `ssh $::SUDOER\@$node "/sbin/vmcp define nic $addr type $type"`;
            my $cmd = "$::SUDO /sbin/vmcp define nic $addr type $type";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            # Check if error.
            if ( $out =~ m/not created/i || $out =~ m/conflicting/i ) {
                $allgood = 0;
                xCAT::zvmUtils->printLn( $callback, "(Error) Failed in: define nic $addr type $type <br>" );
            }
        }
        if ($allgood == 1) {
            # Translate QDIO or Hipersocket into correct type
            if ($type =~m/QDIO/i) {
                $type = 2;
            } elsif ($type =~m/HIPER/i) {
                $type = 1;
            }

            # Add to directory entry
            $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Create_DM -T $userId -v $addr -a $type -n $devcount"`;
            xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Create_DM -T $userId -v $addr -a $type -n $devcount");
            $out = xCAT::zvmUtils->appendHostname( $node, $out );
        }
    }

    # addpagespool [vol_addr] [volume_label] [volume_use] [system_config_name (optional)] [system_config_type (optional)] [parm_disk_owner (optional)] [parm_disk_number (optional)] [parm_disk_password (optional)]
    elsif ( $args->[0] eq "--addpagespool" ) {
        my $argsSize = @{$args};

        my $i;
        my @options = ("", "vol_addr=", "volume_label=", "volume_use=", "system_config_name=", "system_config_type=", "parm_disk_owner=", "parm_disk_number=", "parm_disk_password=");
        my $argStr = "";
        foreach $i ( 1 .. $argsSize ) {
            if ( $args->[$i] ) {
                $argStr .= " -k \"$options[$i]$args->[$i]\"";
            }
        }

        # Add a full volume page or spool disk to the system
        $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Page_or_Spool_Volume_Add -T $hcpUserId $argStr"`;
        xCAT::zvmUtils->printSyslog("smcli Page_or_Spool_Volume_Add -T $hcpUserId $argStr");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # addprocessor [address]
    elsif ( $args->[0] eq "--addprocessor" ) {
        my $addr = $args->[1];

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_CPU_Define_DM -T $userId -v $addr -b 0 -d 1 -y 0"`;
        xCAT::zvmUtils->printSyslog("smcli Image_CPU_Define_DM -T $userId -v $addr -b 0 -d 1 -y 0");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # addprocessoractive [address] [type]
    elsif ( $args->[0] eq "--addprocessoractive" ) {
        my $addr = $args->[1];
        my $type = $args->[2];

        $out = xCAT::zvmCPUtils->defineCpu( $::SUDOER, $node, $addr, $type );
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # addvdisk [device address] [size]
    elsif ( $args->[0] eq "--addvdisk" ) {
        my $addr = $args->[1];
        my $size = $args->[2];
        my $mode = $args->[3];
        my $error = 0;

        xCAT::zvmUtils->printSyslog("$node: smcli Image_Disk_Create_DM -T $userId -v $addr -t FB-512 -a V-DISK -r NONE -u 2 -z $size -m $mode -f 0");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t FB-512 -a V-DISK -r NONE -u 2 -z $size -m $mode -f 0"`;
        $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0;
        if ($error) {
            xCAT::zvmUtils->printSyslog( "$node: Error on Image_Disk_Create_DM. Output: $out" );
            xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred during " .
                                                "'smcli Image_Disk_Create_DM -T $userId -v $addr -t FB-512 -a V-DISK -r NONE " .
                                                "-u 2 -z $size -m $mode -f 0'. Output: $out" );
            return;
        }

        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # addzfcp [pool] [device address (or auto)] [loaddev (0 or 1)] [size] [tag (optional)] [wwpn (optional)] [lun (optional)]
    elsif ( $args->[0] eq "--addzfcp" ) {
        my $argsSize = @{$args};
        if ( ($argsSize != 5) && ($argsSize != 6) && ($argsSize != 8) ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $pool = lc($args->[1]);
        my $device = $args->[2];
        my $loaddev = int($args->[3]);
        if ($loaddev != 0 && $loaddev != 1) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) The loaddev can be 0 or 1" );
            return;
        }
        my $size = $args->[4];
        # Tag specifies what to replace in the autoyast/kickstart template, e.g. $root_device$
        # This argument is optional
        my $tag = $args->[5];

        # Check if WWPN and LUN are given
        # WWPN can be given as a semi-colon separated list
        my $wwpn = "";
        my $lun = "";
        my $useWwpnLun = 0;
        if ($argsSize == 8) {
            $useWwpnLun = 1;
            $wwpn = $args->[6];
            $lun = $args->[7];
            if ($wwpn =~ m/;/) {
                # It's not supported to ipl from a multipath device
                if ($loaddev) {
                    xCAT::zvmUtils->printLn($callback, "$node: (Error) It's not supported to ipl from a multipath device");
                    return;
                }
            }
        }

        my %zFCP; # Store zFCP device's original attributes here
        my %criteria; # Store zFCP device's new attributes here
        my $resultsRef;
        if ($useWwpnLun) {
            # Store current attributes of the SCSI/FCP device in case need to roll back when something goes wrong
            my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun);
            if (xCAT::zvmUtils->checkOutput( $deviceRef ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$deviceRef" );
                return;
            }
            %zFCP = %$deviceRef;

            # Check current status of the FCP device
            if ('used' eq $zFCP{'status'}) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) FCP device 0x$wwpn/0x$lun is in use.");
                return;
            }

            %criteria = (
               'status' => 'used',
               'fcp' => $device,
               'wwpn' => $wwpn,
               'lun' => $lun,
               'size' => $size,
               'owner' => $node,
               'tag' => $tag
            );
            $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        } else {
            # Do not know the WWPN or LUN in this case
            %criteria = (
               'status' => 'used',
               'fcp' => $device,
               'size' => $size,
               'owner' => $node,
               'tag' => $tag
            );
            $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);

            # Store original attributes of the SCSI/FCP device in case need to roll back when something goes wrong
            my %results = %$resultsRef;
            %zFCP = (
                'status' => 'free',
                'wwpn' => $results{'wwpn'},
                'lun' => $results{'lun'},
                'fcp' => '',
                'size' => $size,
                'owner' => '',
                'tag' => ''
            );
        }

        my %results = %$resultsRef;
        if ($results{'rc'} == -1) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to add zFCP device");
            return;
        }

        # Obtain the device assigned by xCAT
        $device = $results{'fcp'};
        $wwpn = $results{'wwpn'};
        $lun = $results{'lun'};

        # Get user directory entry
        my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`;

        # Get source node OS
        my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node);

        my @device_list = split(';', $device);
        foreach (@device_list) {
            # Find DEDICATE statement in the entry (dedicate one if one does not exist)
            my $cur_device = $_;
            if (!$cur_device) { next; }
            my $dedicate = `echo "$userEntry" | egrep -a -i "DEDICATE $cur_device"`;
            if (!$dedicate) {
                # Remove FCP device address from CIO device blacklist
                my $cmd = "$::SUDO /sbin/cio_ignore -r $cur_device";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                # (to-do) Add check for command fail.

                $out = `/opt/xcat/bin/chvm $node --dedicatedevice $cur_device $cur_device 0 2>&1`;
                xCAT::zvmUtils->printLn($callback, "$out");
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    # Roll back. Undedicate FCP device and restore all attributes of the zFCP device
                    foreach (@device_list) {
                        `/opt/xcat/bin/chvm $node --undedicatedevice $_`;
                    }
                    $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%zFCP);
                    # Exit if dedicate failed
                    return;
                }
                if ( $os =~ m/sles/i ) {
                    my $cmd = "$::SUDO /sbin/zfcp_host_configure 0.0.$cur_device 1";
                    $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                        return;
                    }
                } elsif ( $os =~ m/ubuntu/i ) {
                    my $cmd = "$::SUDO /sbin/chzdev zfcp-host $cur_device -e";
                    $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                        return;
                    }
                }
            }
        }

        # Configure native SCSI/FCP inside node (if online)
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "ping") {
            foreach (@device_list) {
                my $cur_device = $_;
                if (!$cur_device) { next; }
                if ($os =~ m/ubuntu/i) { next; }

                # Online device
                $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $node, "-e", "0.0." . $cur_device);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    xCAT::zvmUtils->printLn($callback, "$node: $out");
                    # Roll back. Undedicate FCP device and restore all attributes of the zFCP device
                    foreach (@device_list) {
                        `/opt/xcat/bin/chvm $node --undedicatedevice $_`;
                    }
                    $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%zFCP);
                    return;
                }
            }

            # Set WWPN and LUN in sysfs
            foreach (@device_list) {
                my $cur_device = lc($_);
                if (!$cur_device) { next; }
                $wwpn = lc($wwpn);

                my @wwpn_list = split(";", $wwpn);
                foreach (@wwpn_list) {
                    my $cur_wwpn = $_;
                    if (!$cur_wwpn) { next; }

                    # For versions below RHEL6 or SLES11, they are not supported any more.
                    $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$cur_device/0x$cur_wwpn/unit_add");

                    # Set WWPN and LUN in configuration files
                    my $tmp;
                    if ( $os =~ m/sles1[12]/i ) {
                        #   SLES 11&12: /etc/udev/rules.d/51-zfcp*
                        my $cmd = "$::SUDO /sbin/zfcp_disk_configure 0.0.$cur_device $cur_wwpn $lun 1";
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                            return;
                        }

                        # Check if the config file already exists and contains the zFCP channel
                        $cmd = $::SUDO . ' cat /etc/udev/rules.d/51-zfcp-0.0.' . $cur_device . '.rules';
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                            return;
                        }
                        $out = `echo "$out" | egrep -a -i 'ccw/0.0.' . $cur_device. ']online'`;
                        if (!(length $out)) {
                            # Configure zFCP device to be persistent
                            $cmd = "$::SUDO touch /etc/udev/rules.d/51-zfcp-0.0.$cur_device.rules";
                            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                                return;
                            }

                            # Not configured before, do configuration, will not check for errors here
                            $tmp = 'ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.' . $cur_device . '\", IMPORT{program}=\"collect 0.0.' . $cur_device . ' \%k 0.0.' . $cur_device . ' zfcp\"';
                            $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules';
                            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);

                            $tmp = 'ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.' . $cur_device . ' \%k 0.0.' . $cur_device . 'zfcp\"';
                            $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules';
                            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);

                            $tmp = 'ACTION==\"add\", ENV{COLLECT_0.0.' . $cur_device . '}==\"0\", ATTR{[ccw/0.0.' . $cur_device . ']online}=\"1\"';
                            $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules';
                            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                        }

                        # Will not check for errors here
                        $tmp = 'ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x' . $cur_wwpn . '\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.' . $cur_device . '\", ATTR{[ccw/0.0.' . $cur_device .']0x' . $cur_wwpn . '/unit_add}=\"0x' . $lun . '\"';
                        $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules';
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);

                    } elsif ( $os =~ m/rhel/i ) {
                        #   RHEL: /etc/zfcp.conf
                        $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo \"0.0.$cur_device 0x$cur_wwpn 0x$lun\" >> /etc/zfcp.conf");
                        $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo add > /sys/bus/ccw/devices/0.0.$cur_device/uevent");

                    } elsif ( $os =~ m/ubuntu/i ) {
                        #   Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -e
                        my $cmd = "$::SUDO /sbin/chzdev zfcp-lun 0.0.$cur_device:0x$cur_wwpn:0x$lun -e";
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                            return;
                        }
                    }
                }
            }

            my $cmd = "$::SUDO /sbin/multipath -r";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
            if ($out) {
                # not a fatal error, print a message and continue
                xCAT::zvmUtils->printLn($callback, "$node: $out");
            }

            xCAT::zvmUtils->printLn($callback, "$node: Configuring FCP device to be persistent... Done");
            $out = "";
        }

        # Set loaddev statement in directory entry
        if ($loaddev) {
            $out = `/opt/xcat/bin/chvm $node --setloaddev $wwpn $lun`;
            xCAT::zvmUtils->printLn($callback, "$out");
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to set LOADDEV statement in the directory entry");
                return;
            }
            $out = "";
        }

        xCAT::zvmUtils->printLn($callback, "$node: Adding zFCP device $device/$wwpn/$lun... Done");
    }

    # connectnic2guestlan [address] [lan] [owner]
    elsif ( $args->[0] eq "--connectnic2guestlan" ) {
        my $addr  = $args->[1];
        my $lan   = $args->[2];
        my $owner = $args->[3];

        # Connect to LAN in active configuration
        my $power = `/opt/xcat/bin/rpower $node stat`;
        if ($power =~ m/: on/i) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_LAN -T $userId -v $addr -l $lan -o $owner"`;
            xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_LAN -T $userId -v $addr -l $lan -o $owner");
        }

        $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_LAN_DM -T $userId -v $addr -n $lan -o $owner"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_LAN_DM -T $userId -v $addr -n $lan -o $owner");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # connectnic2vswitch [address] [vSwitch]
    elsif ( $args->[0] eq "--connectnic2vswitch" ) {
        my $addr    = $args->[1];
        my $vswitch = $args->[2];
        my $vswitchPortType = '';
        my $vswitchLanId = '';
        my $argsSize = @{$args};
        if ($argsSize > 3 ) {
            $vswitchPortType = $args->[3];
            $vswitchLanId = $args->[4];
        }

        # Grant access to VSWITCH for Linux user
        $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $vswitch, $vswitchPortType, $vswitchLanId );
        xCAT::zvmUtils->printLn( $callback, "$node: Granting VSwitch ($vswitch $vswitchPortType $vswitchLanId) access for $userId... $out" );

        # Connect to VSwitch in directory entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_Vswitch_DM -T $userId -v $addr -n $vswitch"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_Vswitch_DM -T $userId -v $addr -n $vswitch");

        # Connect to VSwitch in active configuration
        my $power = `/opt/xcat/bin/rpower $node stat`;
        if ($power =~ m/: on/i) {
            $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_Vswitch -T $userId -v $addr -n $vswitch"`;
            xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_Vswitch -T $userId -v $addr -n $vswitch");
        }

        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # copydisk [target address] [source node] [source address]
    elsif ( $args->[0] eq "--copydisk" ) {
        my $tgtNode   = $node;
        my $tgtUserId = $userId;
        my $tgtAddr   = $args->[1];
        my $srcNode   = $args->[2];
        my $srcAddr   = $args->[3];

        # Get source userID
        @propNames = ( 'hcp', 'userid' );
        $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $srcNode, @propNames );
        my $sourceId = $propVals->{'userid'};

        # Assume flashcopy is supported (via SMAPI)
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying $sourceId disk ($srcAddr) to $tgtUserId disk ($srcAddr) using FLASHCOPY" );
        if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) {
             $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $srcAddr, $tgtUserId, $srcAddr);
             xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

             # Exit if flashcopy completed successfully
             # Otherwise try CP FLASHCOPY
             if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) {
                 return;
             }
        }

        #*** Link and copy disk ***
        my $rc;
        my $try;
        my $srcDevNode;
        my $tgtDevNode;

        # Link source disk to HCP
        my $srcLinkAddr;
        $try = 5;
        while ( $try > 0 ) {
            # New disk address
            $srcLinkAddr = $srcAddr + 1000;

            # Check if new disk address is used (source)
            $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $srcLinkAddr );

            # If disk address is used (source)
            while ( $rc == 0 ) {

                # Generate a new disk address
                # Sleep 5 seconds to let existing disk appear
                sleep(5);
                $srcLinkAddr = $srcLinkAddr + 1;
                $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $srcLinkAddr );
            }

            # Link source disk
            # Because the zHCP has LNKNOPAS, no disk password is required
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking source disk ($srcAddr) as ($srcLinkAddr)" );
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $sourceId $srcAddr $srcLinkAddr RR"`;

            # If link fails
            if ( $out =~ m/not linked/i ) {

                # Wait before trying again
                sleep(5);

                $try = $try - 1;
            } else {
                last;
            }
        }    # End of while ( $try > 0 )

        # If source disk is not linked
        if ( $out =~ m/not linked/i ) {
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link source disk ($srcAddr)" );
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" );

            # Exit
            return;
        }

        # Link target disk to HCP
        my $tgtLinkAddr;
        $try = 5;
        while ( $try > 0 ) {

            # New disk address
            $tgtLinkAddr = $tgtAddr + 2000;

            # Check if new disk address is used (target)
            $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr );

            # If disk address is used (target)
            while ( $rc == 0 ) {

                # Generate a new disk address
                # Sleep 5 seconds to let existing disk appear
                sleep(5);
                $tgtLinkAddr = $tgtLinkAddr + 1;
                $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr );
            }

            # Link target disk
            # Because the zHCP has LNKNOPAS, no disk password is required
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" );
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $tgtAddr $tgtLinkAddr MR"`;

            # If link fails
            if ( $out =~ m/not linked/i ) {

                # Wait before trying again
                sleep(5);

                $try = $try - 1;
            } else {
                last;
            }
        }    # End of while ( $try > 0 )

        # If target disk is not linked
        if ( $out =~ m/not linked/i ) {
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)" );
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" );

            # Detatch disks from HCP
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`;

            # Exit
            return;
        }

        #*** Use flashcopy ***
        # Flashcopy only supports ECKD volumes
        # Assume flashcopy is supported and use Linux DD on failure
        my $ddCopy = 0;

        # Check for CP flashcopy lock
        my $wait = 0;
        while ( `ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"` && $wait < 90 ) {

            # Wait until the lock dissappears
            # 90 seconds wait limit
            sleep(2);
            $wait = $wait + 2;
        }

        # If flashcopy locks still exists
        if (`ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"`) {

            # Detatch disks from HCP
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`;

            xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Flashcopy lock is enabled" );
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Remove lock by deleting /tmp/.flashcopy_lock on the zHCP. Use caution!" );
            return;
        } else {

            # Enable lock
            $out = `ssh $::SUDOER\@$hcp "$::SUDO touch /tmp/.flashcopy_lock"`;

            # Flashcopy source disk
            $out = xCAT::zvmCPUtils->flashCopy( $::SUDOER, $hcp, $srcLinkAddr, $tgtLinkAddr );
            $rc = xCAT::zvmUtils->checkOutput( $out );
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                # Try using Linux DD
                $ddCopy = 1;

                # Remove lock
                $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/.flashcopy_lock"`;
            }

            # Remove lock
            $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/.flashcopy_lock"`;
        }

        # Flashcopy not supported, use Linux dd
        if ($ddCopy) {
            #*** Use Linux dd to copy ***
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not working. Using Linux DD" );

            # Enable disks
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtLinkAddr );
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $srcLinkAddr );

            # Determine source device node
            $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcLinkAddr);

            # Determine target device node
            $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtLinkAddr);

            # Format target disk
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`;

            # Check for errors
            $rc = xCAT::zvmUtils->checkOutput( $out );
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                # Detatch disks from HCP
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`;

                return;
            }

            # Sleep 2 seconds to let the system settle
            sleep(2);

            # Automatically create a partition using the entire disk
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating a partition using the entire disk ($tgtDevNode)" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdasd -a /dev/$tgtDevNode"`;

            # Copy source disk to target disk (4096 block size)
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcDevNode) to target disk ($tgtDevNode)" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync"`;

            # Disable disks
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtLinkAddr );
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $srcLinkAddr );

            # Check for error
            $rc = xCAT::zvmUtils->checkOutput( $out );
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                # Detatch disks from HCP
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`;

                return;
            }

            # Sleep 2 seconds to let the system settle
            sleep(2);
        }

        # Detatch disks from HCP
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" );
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching source disk ($srcLinkAddr)" );
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`;

        $out = "$tgtNode: Done";
    }

    # createfilesysnode [source file] [target file]
    elsif ( $args->[0] eq "--createfilesysnode" ) {
        my $srcFile = $args->[1];
        my $tgtFile = $args->[2];

        my $argsSize = @{$args};
        if ($argsSize != 3) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters");
            return;
        }

        # Obtain corresponding WWPN and LUN of source file.
        # A sample url is '/dev/disk/by-path/ccw-0.0.1fb3-zfcp-0x5005076801102991:0x0021000000000000'.
        # (On Ubuntu it's '/dev/disk/by-path/ccw-0.0.1fb3-fc-0x5005076801102991-lun-33')
        # If it's multipath, it could be '0x5005076801102991;5005076801102992;5005076801102993' instead of '0x5005076801102991'.
        # Please note the url is not an actual device path. It's a parameter used to handle devices and the parameter
        # appears like a device path.
        # For the pattern '-zfcp-([\w;]+):(\w+)', '-zfcp-' is used to locate, ([\w;]+) will capture wwpn and store it into
        # variable $1, ':' is the delimiter and (\w+) will capture lun and store it into variable $2. The pattern for wwpn
        # and lun is different because wwpn may appear in multipath format but lun could only be one number.

        # Get source node OS
        my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node);
        if (xCAT::zvmUtils->checkOutput($os) == -1) {
            return;
        }

        my ($devices, $wwpn, $lun);
        if ( $os =~ m/ubuntu/i ) {
            $srcFile =~ m/ccw-0.0.([\w;]+)-fc-([\w;]+)-lun-(\w+)/;
            ($devices, $wwpn, $lun) = ($1, $2, $3);
        } else {
            $srcFile =~ m/ccw-0.0.([\w;]+)-zfcp-([\w;]+):(\w+)/;
            ($devices, $wwpn, $lun) = ($1, $2, $3);
        }

        my $multipath = 0;
        if ($wwpn =~ m/;/) { $multipath = 1; }
        my @deviceList = split(";", $devices);

        my $cmd = "$::SUDO /usr/bin/stat --printf=%n $tgtFile";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
        if ($out eq $tgtFile) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) $tgtFile already exists");
            return;
        }

        # Use udev tools to create mountpoint. If it's signal path device, its devices path
        # is enough for udev. If it's multipath device, multipath device path are involved,
        # so we have to figure out its WWID and use the WWID to create mountpoint.
        my $wwid = '';
        if ($multipath) {
            # Find the name of the multipath device by arbitrary one path in the set
            my @wwpnList = split(";", $wwpn);
            my $curWwpn = '';
            foreach (@wwpnList) {
                if ($_ =~ m/^0x/i) {
                    $curWwpn = $_;
                } else {
                    $curWwpn = "0x$_";
                }
                # Try to get WWID by current WWPN.
                foreach (@deviceList) {
                    my $cur_device = $_;
                    if ( $os =~ m/ubuntu/i ) {
                        $srcFile =~ s/ccw-0.0.[0-9a-f;]+-fc-0x[0-9a-f;]+-lun/ccw-0.0.$cur_device-fc-$curWwpn-lun/i;
                    } else {
                        $srcFile =~ s/ccw-0.0.[0-9a-f;]+-zfcp-0x[0-9a-f;]+:/ccw-0.0.$cur_device-zfcp-$curWwpn:/i;
                    }
                    my $cmd = "$::SUDO /usr/bin/stat --printf=%n $srcFile";
                    $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                    if ($out ne $srcFile) {
                        xCAT::zvmUtils->printLn($callback, "$node: (Warning) $srcFile does not exist");
                        next;
                    }

                    $cmd = $::SUDO . ' /sbin/udevadm info --query=all --name=' . $srcFile;
                    $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                        return;
                    }
                    $out = `echo "$out" | egrep -a -i "ID_SERIAL="`;
                    $out =~ m/ID_SERIAL=(\w+)\s*$/;
                    $wwid = $1;
                    if ($wwid) { last; }
                }
                if ($wwid) { last; }
            }
            if (!$wwid) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) zfcp device $wwpn:$lun not found in OS");
                return;
            }
        } else {
            my $isFound = 0;
            foreach (@deviceList) {
                my $cur_device = $_;
                if ( $os =~ m/ubuntu/i ) {
                    $srcFile =~ s/ccw-0.0.[0-9a-f;]+-fc/ccw-0.0.$cur_device-fc/i;
                } else {
                    $srcFile =~ s/ccw-0.0.[0-9a-f;]+-zfcp/ccw-0.0.$cur_device-zfcp/i;
                }

                my $cmd = "$::SUDO /usr/bin/stat --printf=%n $srcFile";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                if ($out ne $srcFile) {
                    xCAT::zvmUtils->printLn($callback, "$node: (warning) $srcFile does not exist");
                } else {
                    $isFound = 1;
                }
            }
            if (!$isFound) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) zfcp device $wwpn:$lun not found in OS");
                return;
            }
        }

        # Create udev config file if not exist
        my $configFile = '/etc/udev/rules.d/56-zfcp.rules';
        $cmd = "$::SUDO test -e $configFile && echo Exists";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
        if (!($out)) {
            $cmd = "$::SUDO touch $configFile";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            if ( $os =~ m/rhel/i ) {
                # will not check for errors here
                my $zfcp_rule = 'KERNEL==\"zfcp\", RUN+=\"/sbin/zfcpconf.sh\"';
                my $multipath_rule = 'KERNEL==\"zfcp\", RUN+=\"/sbin/multipath -r\"';
                $cmd = $::SUDO . ' echo '. $zfcp_rule . ' >> ' . $configFile;
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
                $cmd = $::SUDO . ' echo ' . $multipath_rule . ' >> ' . $configFile;
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
            }
        }

        # Add the entry into udev config file
        my ( $create_symlink_cmd, $reload_cmd, $update_rule_cmd );
        my $tgtFileName = $tgtFile;
        $tgtFileName =~ s/\/dev\///;
        if ($multipath) {
            my $linkItem = 'KERNEL==\"dm-*\", ENV{DM_UUID}==\"mpath-' . $wwid . '\", SYMLINK+=\"' . $tgtFileName . '\"';
            $update_rule_cmd = $::SUDO . ' echo '. $linkItem . '>>' . $configFile;
            $reload_cmd = "$::SUDO udevadm control --reload";
            $create_symlink_cmd = "$::SUDO udevadm trigger --sysname-match=dm-*";
        } else {
            my $linkItem = 'KERNEL==\"sd*\", ATTRS{wwpn}==\"' . $wwpn . '\", ATTRS{fcp_lun}==\"' . $lun . '\", SYMLINK+=\"' . $tgtFileName . '\%n\"';
            $update_rule_cmd = $::SUDO . ' echo '. $linkItem . '>>' . $configFile;
            $reload_cmd = "$::SUDO udevadm control --reload";
            $create_symlink_cmd = "$::SUDO udevadm trigger --sysname-match=sd*";
        }

        $cmd = "$update_rule_cmd ; $reload_cmd ; $create_symlink_cmd";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
            return;
        }

        xCAT::zvmUtils->printLn($callback, "$node: Creating file system node $tgtFile... Done");
    }

    # dedicatedevice [virtual device] [real device] [mode (1 or 0)]
    elsif ( $args->[0] eq "--dedicatedevice" ) {
        my $vaddr = $args->[1];
        my $raddr = $args->[2];
        my $mode  = $args->[3];

        my $argsSize = @{$args};
        if ($argsSize != 4) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters");
            return;
        }
        my $doActive = 1;

        # Dedicate device to directory entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Dedicate_DM -T $userId -v $vaddr -r $raddr -R $mode"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate_DM -T $userId -v $vaddr -r $raddr -R $mode");
        if (($out =~ m/Return Code: 404/i) and ($out =~ m/Reason Code: 4/i)) {
            $out = "Dedicating device $raddr to $userId" . "'s directory entry... Done";
            $doActive = 0; # Have already been defined before, no need to make active in this case.
        }
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        # Dedicate device to active configuration
        my $power = `/opt/xcat/bin/rpower $node stat`;
        if (($power =~ m/: on/i) and $doActive) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Dedicate -T $userId -v $vaddr -r $raddr -R $mode"`;
            xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate -T $userId -v $vaddr -r $raddr -R $mode");
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        }

        $out = "";
    }

    # deleteipl
    elsif ( $args->[0] eq "--deleteipl" ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_IPL_Delete_DM -T $userId"`;
        xCAT::zvmUtils->printSyslog("smcli Image_IPL_Delete_DM -T $userId");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # formatdisk [address] [multi password]
    elsif ( $args->[0] eq "--formatdisk" ) {
        my $tgtNode   = $node;
        my $tgtUserId = $userId;
        my $tgtAddr   = $args->[1];

        #*** Link and format disk ***
        my $rc;
        my $try;
        my $tgtDevNode;

        # Link target disk to zHCP
        my $tgtLinkAddr;
        $try = 5;
        while ( $try > 0 ) {

            # New disk address
            $tgtLinkAddr = $tgtAddr + 1000;

            # Check if new disk address is used (target)
            $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr );

            # If disk address is used (target)
            while ( $rc == 0 ) {

                # Generate a new disk address
                # Sleep 5 seconds to let existing disk appear
                sleep(5);
                $tgtLinkAddr = $tgtLinkAddr + 1;
                $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr );
            }

            # Link target disk
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" );
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $tgtAddr $tgtLinkAddr MR"`;

            # If link fails
            if ( $out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i ) {
                # Detatch link because only linked as R/O
                `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;

                # Wait before trying again
                sleep(5);

                $try = $try - 1;
            } else {
                last;
            }
        }    # End of while ( $try > 0 )

        # If target disk is not linked
        if ( $out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i ) {
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)" );
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" );

            # Detatch link because only linked as R/O
            `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;

            # Exit
            return;
        }

        #*** Format disk ***
        my @words;
        if ( $rc == -1 ) {

            # Enable disk
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtLinkAddr );

            # Determine target device node
            $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtLinkAddr);

            # Format target disk
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`;

            # Check for errors
            $rc = xCAT::zvmUtils->checkOutput( $out );
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );
                return;
            }
        }

        # Disable disk
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtLinkAddr );

        # Detatch disk from HCP
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" );
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`;

        $out = "$tgtNode: Done";
    }

    # grantvswitch [VSwitch]
    elsif ( $args->[0] eq "--grantvswitch" ) {
        my $vsw = $args->[1];
        my $vswitchPortType = '';
        my $vswitchLanId = '';

        my $argsSize = @{$args};
        if ($argsSize > 2 ) {
            $vswitchPortType = $args->[2];
            $vswitchLanId = $args->[3];
        }

        $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $vsw, $vswitchPortType, $vswitchLanId );
        $out = xCAT::zvmUtils->appendHostname( $node, "Granting VSwitch ($vsw) access for $userId... $out" );
    }

    # disconnectnic [address]
    elsif ( $args->[0] eq "--disconnectnic" ) {
        my $addr = $args->[1];

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Disconnect_DM -T $userId -v $addr"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Disconnect_DM -T $userId -v $addr");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # punchfile [file path] [class (optional)] [remote host (optional)]
    elsif ( $args->[0] eq "--punchfile" ) {
        # Punch a file to a the node reader
        my $argsSize = @{$args};
        if (($argsSize < 2) || ($argsSize > 4)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $filePath = $args->[1];
        my $class = "A";  # Default spool class should be A
        my $remoteHost;
        if ($argsSize > 2) {
            $class = $args->[2];
        }
        if ($argsSize > 3) {
            $remoteHost = $args->[3];  # Must be specified as user@host
        }
        # Obtain file name
        my $fileName = basename($filePath);
        my $trunkFile = "/tmp/$node-$fileName";

        # Validate class
        if ($class !~ /^[a-zA-Z0-9]$/) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid spool class: $class. It should be 1-character alphanumeric" );
            return;
        }

        # If a remote host is specified, obtain the file from the remote host
        # The xCAT public SSH key must have been already setup if this is to work
        my $rc;
        if (defined $remoteHost) {
            $rc = `/usr/bin/scp $remoteHost:$filePath $trunkFile 2>/dev/null; echo $?`;
        } else {
            $rc = `/bin/cp $filePath $trunkFile 2>/dev/null; echo $?`;
        }

        if ($rc != '0') {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source file" );
            return;
        }

        # Check the node flag, if the node has xcatconf4z flag set,  punch it directly.
        # Otherwise, put files into a temp directory for later use.
        my $nodeFlag = '';
        my $cfgTrunkDir = "/tmp/configdrive/$node/";
        my @propNames = ('status');
        my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames );
        $nodeFlag = $propVals->{'status'};
        if ($nodeFlag =~ /XCATCONF4Z=0/) {
            if (!-d $cfgTrunkDir) {
                mkpath($cfgTrunkDir);
            }
            $rc = `/bin/cp $trunkFile $cfgTrunkDir/$fileName 2>/dev/null; echo $?`;
            `rm -rf $trunkFile`;
            if ($rc != '0') {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source file $trunkFile to directory $cfgTrunkDir, please check if xCAT is running out of space" );
            return;
            }

        } else {
            # Set up punch device
            $rc = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r d"`;
            xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d");

            # Send over file to zHCP and punch it to the node's reader
            xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $trunkFile, $trunkFile);
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $trunkFile, $fileName, "", $class );

            # No extra steps are needed if the punch succeeded or failed, just output the results
            xCAT::zvmUtils->printLn( $callback, "$node: Punching $fileName to reader... $out" );

            # Remove temporary file
            `rm -rf $trunkFile`;
            `ssh $::SUDOER\@$hcp "$::SUDO rm -f $trunkFile"`;
            $out = "";
        }
    }

    # purgerdr
    elsif ( $args->[0] eq "--purgerdr" ) {
        # Purge the reader of node
        $out = xCAT::zvmCPUtils->purgeReader($::SUDOER, $hcp, $userId);
        $out = xCAT::zvmUtils->appendHostname( $node, "$out" );
    }

    # removediskfrompool [function] [region] [group]
    elsif ( $args->[0] eq "--removediskfrompool" ) {
        # This is no longer supported in chvm. Using chhypervisor instead.
        changeHypervisor( $callback, $node, $args );
    }

    # removezfcpfrompool [pool] [lun]
    elsif ( $args->[0] eq "--removezfcpfrompool" ) {
        # This is no longer supported in chvm. Using chhypervisor instead.
        changeHypervisor( $callback, $node, $args );
    }

    # removedisk [virtual address]
    elsif ( $args->[0] eq "--removedisk" ) {
        my $addr = $args->[1];

        # Remove from active configuration
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "ping") {
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $node, "-d", $addr );
            $out = `ssh $node "/sbin/vmcp det $addr"`;
        }

        # Remove from user directory entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Delete_DM -T $userId -v $addr -e 0"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Disk_Delete_DM -T $userId -v $addr -e 0");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # removefilesysnode [target file]
    elsif ( $args->[0] eq "--removefilesysnode" ) {
        my $tgtFile = $args->[1];

        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # Unmount this disk and remove this disk from udev config file, but ignore the output
        my $configFile = '/etc/udev/rules.d/56-zfcp.rules';
        my $tgtFileName = $tgtFile;
        $tgtFileName =~ s/\/dev\///;
        my $update_rule_cmd_sd = $::SUDO . ' sed -i -e /SYMLINK+=\"' . $tgtFileName . '\%n\"/d ' . $configFile; # For single device
        my $update_rule_cmd_dm = $::SUDO . ' sed -i -e /SYMLINK+=\"' . $tgtFileName . '\"/d ' . $configFile; # For multipath

        my $reload_cmd = "$::SUDO udevadm control --reload";
        my $create_symlink_cmd_sd = "$::SUDO udevadm trigger --sysname-match=sd*"; # For single device
        my $create_symlink_cmd_dm = "$::SUDO udevadm trigger --sysname-match=dm-*"; # For multipath

        my $cmd = "$update_rule_cmd_sd ; $update_rule_cmd_dm ; $reload_cmd ; $create_symlink_cmd_sd ; $create_symlink_cmd_dm";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
            return;
        }
        xCAT::zvmUtils->printLn($callback, "$node: Removing file system node $tgtFile... Done");
    }

    # removenic [address]
    elsif ( $args->[0] eq "--removenic" ) {
        my $addr = $args->[1];

        # Remove from active configuration
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "ping") {
            $out = `ssh $node "/sbin/vmcp det nic $addr"`;
        }

        # Remove from user directory entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Delete_DM -T $userId -v $addr"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Delete_DM -T $userId -v $addr");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # removeprocessor [address]
    elsif ( $args->[0] eq "--removeprocessor" ) {
        my $addr = $args->[1];

        # Remove from user directory entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_CPU_Delete_DM -T $userId -v $addr"`;
        xCAT::zvmUtils->printSyslog("smcli Image_CPU_Delete_DM -T $userId -v $addr");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # removeloaddev [wwpn] [lun]
    elsif ( $args->[0] eq "--removeloaddev" ) {
        my $wwpn = $args->[1];
        my $lun = $args->[2];

        xCAT::zvmUtils->printLn($callback, "$node: Removing LOADDEV directory statements");

        # Make sure WWPN and LUN do not have 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr( $wwpn, "0x", "" );
        $lun = xCAT::zvmUtils->replaceStr( $lun, "0x", "" );

        # Get user directory entry
        my $updateEntry = 0;
        my $userEntryFile = "/tmp/$node.txt";
        my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`;
        xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'");
        chomp($userEntry);
        if (!$wwpn && !$lun) {
            # If no WWPN or LUN is provided, delete all LOADDEV statements
            `echo "$userEntry" | grep -a -v "LOADDEV" > $userEntryFile`;
            $updateEntry = 1;
        } else {

            # Delete old directory entry file
            `rm -rf $userEntryFile`;

            # Remove LOADDEV PORTNAME and LUN statements in directory entry
            my @lines = split( '\n', $userEntry );
            foreach (@lines) {
                if (!(length $_)) {next;}
                # Check if LOADDEV PORTNAME and LUN statements are in the directory entry
                if ($_ =~ m/LOADDEV PORTNAME $wwpn/i) {
                    $updateEntry = 1;
                    next;
                } elsif ($_ =~ m/LOADDEV LUN $lun/i) {
                    $updateEntry = 1;
                    next;
                } else {
                    # Write directory entry to file
                    `echo "$_" >> $userEntryFile`;
                }
            }
        }

        # Replace user directory entry (if necessary)
        if ($updateEntry) {
            $out = `/opt/xcat/bin/chvm $node --replacevs $userEntryFile 2>&1`;
            xCAT::zvmUtils->printLn($callback, "$out");

            # Delete directory entry file
            `rm -rf $userEntryFile`;
        } else {
            xCAT::zvmUtils->printLn($callback, "$node: No changes required in the directory entry");
        }

        $out = "";
    }

    # removezfcp [device address] [wwpn] [lun] [persist (0 or 1) (optional)]
    elsif ( $args->[0] eq "--removezfcp" ) {
        my $device = $args->[1];
        my $wwpn = $args->[2];
        my $lun = $args->[3];
        my $persist = "0";  # Optional

        # Delete 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
        $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

        my $argsSize = @{$args};
        if ($argsSize != 4 && $argsSize != 5) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters");
            return;
        }

        if ($argsSize == 5) {
            $persist = $args->[4];
        }

        # Check the value of persist
        if ($persist !~ /^[01]$/) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Persist can only be 0 or 1");
            return;
        }
        $persist = int($persist);

        # Find the pool that contains the SCSI/FCP device
        my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun);
        if (xCAT::zvmUtils->checkOutput( $pool ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$pool" );
            return;
        }
        if (!$pool) {
            # Continue to try and remove the SCSI/FCP device even when it is not found in a storage pool
            xCAT::zvmUtils->printLn( $callback, "$node: Could not find FCP device in any FCP storage pool" );
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: Found FCP device in $pool" );

            my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf"`;
            ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$pool.conf\" ", $hcp, "changeVM", $select, $node );
            if ($rc != 0) {
                xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                return;
            }
            $select = `echo "$select" | egrep -a -i "$wwpn,$lun"`;
            chomp($select);
            my @info = split(',', $select);

            # A node can only remove a zFCP device that belongs to itself
            if ( ('used' eq $info[0]) && ($node ne $info[5]) ) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) The zFCP device 0x$wwpn/0x$lun does not belong to the node $node.");
                return;
            }
            # If the device is not known, try to find it in the storage pool
            if ($device && $device !~ /^[0-9a-f;]/i) {
                $device = $info[6];
            }

            my $status = "free";
            my $owner = "";
            if ($persist) {
                # Keep the device reserved if persist = 1
                $status = "reserved";
            }

            my %criteria = (
               'status' => $status,
               'wwpn' => $wwpn,
               'lun' => $lun,
               'owner' => $owner,
            );
            my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
            my %results = %$resultsRef;

            if ($results{'rc'} == -1) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find zFCP device");
                return;
            }

            # Obtain the device assigned by xCAT
            $wwpn = $results{'wwpn'};
            $lun = $results{'lun'};
        }

        # De-configure SCSI over FCP inside node (if online)
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "ping") {
            # Delete WWPN and LUN from sysfs
            $device = lc($device);
            $wwpn = lc($wwpn);

            my @device_list = split(';', $device);
            foreach (@device_list) {
                my $cur_device = $_;
                if (!$cur_device) { next; }
                my @wwpnList = split(";", $wwpn);
                foreach (@wwpnList) {
                    my $cur_wwpn = $_;
                    if (!$cur_wwpn) { next; }

                    # unit_remove does not exist on SLES 10!
                    $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$cur_device/0x$cur_wwpn/unit_remove");

                    # Get source node OS
                    my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node);

                    # Delete WWPN and LUN from configuration files
                    my $expression = "";
                    if ( $os =~ m/sles1[12]/i ) {
                        #   SLES 11&12: /etc/udev/rules.d/51-zfcp*
                        $expression = "/$lun/d";
                        my $cmd = "$::SUDO sed -i -e $expression /etc/udev/rules.d/51-zfcp-0.0.$cur_device.rules";
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                            return;
                        }

                    } elsif ( $os =~ m/rhel/i ) {
                        #   RHEL: /etc/zfcp.conf
                        $expression = "/$lun/d";
                        my $cmd = "$::SUDO sed -i -e $expression /etc/zfcp.conf";
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                            return;
                        }

                    } elsif ( $os =~ m/ubuntu/i ) {
                        #   Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -d
                        my $cmd = "$::SUDO /sbin/chzdev zfcp-lun 0.0.$cur_device:0x$cur_wwpn:0x$lun -d";
                        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                            return;
                        }
                    }
                }
            }
            # will not check for errors here
            my $cmd = "$::SUDO /sbin/multipath -W";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
            $cmd = "$::SUDO /sbin/multipath -r";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);

            xCAT::zvmUtils->printLn($callback, "$node: De-configuring FCP device on host... Done");
        }

        $out = "";
    }

    # replacevs [file]
    elsif ( $args->[0] eq "--replacevs" ) {
        my $argsSize = @{$args};
        my $file;
        if ($argsSize == 2) {
            $file = $args->[1];
        }

        if ($file) {
            if (-e $file) {
                # Target system (zHCP), e.g. root@gpok2.endicott.ibm.com
                my $target = "$::SUDOER@";
                $target .= $hcp;

                # SCP file over to zHCP
                $out = `scp $file $target:$file`;

                # Lock image
                `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Lock_DM -T $userId"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Lock_DM -T $userId");

                # Replace user directory entry
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Replace_DM -T $userId -f $file"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Replace_DM -T $userId -f $file");

                # Unlock image
                `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Unlock_DM -T $userId");

                # Delete file on zHCP
                `ssh $::SUDOER\@$hcp "rm -rf $file"`;
            } else {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) File does not exist" );
                return;
            }
        } elsif ($::STDIN) {
            # Create a temporary file to contain directory on zHCP
            $file = "/tmp/" . $node . ".direct";
            my @lines = split("\n", $::STDIN);

            # Delete existing file on zHCP (if any)
            `ssh $::SUDOER\@$hcp "rm -rf $file"`;

            # Write directory entry into temporary file
            # because directory entry cannot be remotely echoed into stdin
            foreach (@lines) {
                if (!(length $_)) {next;}
                if ($_) {
                    $_ = "'" . $_ . "'";
                    `ssh $::SUDOER\@$hcp "echo $_ >> $file"`;
                }
            }

            # Lock image
            `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Lock_DM -T $userId"`;
            xCAT::zvmUtils->printSyslog("smcli Image_Lock_DM -T $userId");

            # Replace user directory entry
            $out = `ssh $::SUDOER\@$hcp "cat $file | $::SUDO $::DIR/smcli Image_Replace_DM -T $userId -s"`;
            xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp cat $file | $::SUDO $::DIR/smcli Image_Replace_DM -T $userId -s");

            # Unlock image
            `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`;
            xCAT::zvmUtils->printSyslog("smcli Image_Unlock_DM -T $userId");

            # Delete created file on zHCP
            `ssh $::SUDOER\@$hcp "rm -rf $file"`;
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) No directory entry file specified" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify a text file containing the updated directory entry" );
            return;
        }

        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # resetsmapi
    elsif ( $args->[0] eq "--resetsmapi" ) {
        # This is no longer supported in chvm. Using chhypervisor instead.
        changeHypervisor( $callback, $node, $args );
    }

    # setipl [ipl target] [load parms] [parms]
    elsif ( $args->[0] eq "--setipl" ) {
        my $trgt = $args->[1];

        my $loadparms = "''";
        if ($args->[2]) {
            $loadparms = $args->[2];
        }

        my $parms = "''";
        if ($args->[3]) {
            $parms = $args->[3];
        }

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_IPL_Set_DM -T $userId -s $trgt -l $loadparms -p $parms"`;
        xCAT::zvmUtils->printSyslog("smcli Image_IPL_Set_DM -T $userId -s $trgt -l $loadparms -p $parms");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # setpassword [password]
    elsif ( $args->[0] eq "--setpassword" ) {
        my $pw = $args->[1];

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Password_Set_DM -T $userId -p $pw"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Password_Set_DM -T $userId -p $pw");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # setloaddev [wwpn] [lun] [isHex (0 or 1, optional)] [scpdata (optional)]
    elsif ( $args->[0] eq "--setloaddev" ) {
        my $argsSize = @{$args};
        if (($argsSize != 3) && ($argsSize != 5)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $updateScpdata = 0;
        if ($argsSize > 3) {
            $updateScpdata = 1;
        }

        my $wwpn = $args->[1];
        my $lun = $args->[2];
        # isHex and scpdata must appear or disappear concurrently.
        my $isHex = 0;
        my $scpdata = '';
        my $scptype = '';
        if ($updateScpdata) {
            $isHex = $args->[3];
            $scpdata = $args->[4];
            if ($isHex){
                $scptype = '3';
            } else {
                $scptype = '2';
                # If the scpdata is not in HEX form, it may contain white space.
                # So wrap the parameter with quote marks.
                $scpdata = "'".$scpdata."'";
            }
        }

        # Make sure WWPN and LUN do not have 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr( $wwpn, "0x", "" );
        $lun = xCAT::zvmUtils->replaceStr( $lun, "0x", "" );

        xCAT::zvmUtils->printLn($callback, "$node: Setting LOADDEV directory statements");

        # Change SCSI definitions
        `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_SCSI_Characteristics_Define_DM  -T $userId -b '' -k '' -l $lun -p $wwpn -s $scptype -d $scpdata"`;
        xCAT::zvmUtils->printSyslog("smcli Image_SCSI_Characteristics_Define_DM  -T $userId -b '' -k '' -l $lun -p $wwpn -s $scptype -d $scpdata");

        $out = "";
    }

    # smcli [smcli command]
    #     'smcli command' is the smcli command less the 'smcli' command name.  The command will
    #         normally have a %userid% substring within it which indicates the location in the
    #         command that should be replaced with the z/VM userid for the target node.  This can
    #         occur multiple times within the command string.
    #         e.g. chvm gpok168 --smcli 'Virtual_Network_Adapter_Query_Extended -T "%userid%" -k image_device_number=*'
    #              chvm gpok168 --smcli 'Image_Definition_Update_DM -h'
    # The output from the smcli invocation is returned.  If we cannot SSH into the
    # zhcp server then an error message indicating this failure is returned.
    elsif ( $args->[0] eq "--smcli" ) {
        my @smcliCmd;
        my $useridKeyword = '\%userid\%';

        @smcliCmd = @{$args};
        shift @smcliCmd;
        if ( !@smcliCmd ) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters");
            return;
        }

        my $smcliCmdStr = join( ' ', @smcliCmd );
        if ( $smcliCmdStr =~ /$useridKeyword/ ) {
            # Replace userid keyword with the userid for the node.
            $smcliCmdStr =~ s/$useridKeyword/$userId/g;
        }

        $out = `ssh $::SUDOER\@$hcp "$::DIR/smcli $smcliCmdStr"`;
        my $rc = $? >> 8;

        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" );
            return;
        }
    }

    # undedicatedevice [virtual device]
    elsif ( $args->[0] eq "--undedicatedevice" ) {
        my $vaddr = $args->[1];

        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters");
            return;
        }

        # Undedicate device in directory entry
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Undedicate_DM -T $userId -v $vaddr"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Device_Undedicate_DM -T $userId -v $vaddr");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        # Undedicate device in active configuration
        my $power = `/opt/xcat/bin/rpower $node stat`;
        if ($power =~ m/: on/i) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Undedicate -T $userId -v $vaddr"`;
            xCAT::zvmUtils->printSyslog("smcli Image_Device_Undedicate -T $userId -v $vaddr");
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        }

        $out = "";
    }

    # sharevolume [vol_addr] [share_enable (YES or NO)]
    elsif ( $args->[0] eq "--sharevolume" ) {
        my $volAddr = $args->[1];
        my $share = $args->[2];

        # Add disk to running system
        $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Share -T $userId -k img_vol_addr=$volAddr -k share_enable=$share"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Volume_Share -T $userId -k img_vol_addr=$volAddr -k share_enable=$share");
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
    }

    # setprocessor [count]
    elsif($args->[0] eq "--setprocessor") {
        my $cpuCount = $args->[1];
        my @allCpu;
        my $count = 0;
        my $newAddr;
        my $cpu;
        my @allValidAddr = ('00','01','02','03','04','05','06','07','09','09','0A','0B','0C','0D','0E','0F',
                            '10','11','12','13','14','15','16','17','19','19','1A','1B','1C','1D','1E','1F',
                            '20','21','22','23','24','25','26','27','29','29','2A','2B','2C','2D','2E','2F',
                            '30','31','32','33','34','35','36','37','39','39','3A','3B','3C','3D','3E','3F');

        # Get current CPU count and address
        xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $userId -k CPU");
        my $proc = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $userId -k CPU"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $userId -k CPU\"", $hcp, "changeVM", $proc, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $proc = `echo "$proc" | egrep -a -i CPU=`;
        while ( index( $proc, "CPUADDR" ) != -1) {
            my $position = index($proc, "CPUADDR");
            my $address = substr($proc, $position + 8, 2);
            push( @allCpu, $address );
            $proc = substr( $proc, $position + 10 );
        }

        # Find free valid CPU address
        my %allCpu = map { $_=>1 } @allCpu;
        my @addrLeft = grep( !defined $allCpu{$_}, @allValidAddr );

        # Add new CPUs
        if ( $cpuCount > @allCpu ) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k CPU_MAXIMUM=COUNT=$cpuCount -k TYPE=ESA"`;
            xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k CPU_MAXIMUM=COUNT=$cpuCount -k TYPE=ESA");
            while ( $count < $cpuCount - @allCpu ) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k CPU=CPUADDR=$addrLeft[$count]"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k CPU=CPUADDR=$addrLeft[$count]");
                $count++;
            }
        # Remove CPUs
        } else {
            while ( $count <= @allCpu - $cpuCount ) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_CPU_Delete_DM -T $userId -v $allCpu[@allCpu-$count]"`;
                xCAT::zvmUtils->printSyslog("smcli Image_CPU_Delete_DM -T $userId -v $allCpu[@allCpu-$count]");
                $count++;
            }
        }

        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        $out = "";
    }

    # setmemory [size]
    elsif ($args->[0] eq "--setmemory") {
        # Memory hotplug not supported, just change memory size in user directory
        my $size = $args->[1];

        if (!($size =~ m/G/i || $size =~ m/M/i)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size can be Megabytes (M) or Gigabytes (G)" );
            return;
        }

        # Set initial memory to 1M first, make this function able to increase/descrease the storage
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k STORAGE_INITIAL=1M"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k STORAGE_INITIAL=1M");

        # Set both initial memory and maximum memory to be the same
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k STORAGE_INITIAL=$size -k STORAGE_MAXIMUM=$size"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k STORAGE_INITIAL=$size -k STORAGE_MAXIMUM=$size");

        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        $out = "";
    }

    # setconsole [start|stop]
    elsif ($args->[0] eq "--setconsole") {
        # Start or stop spool console
        my $action = $args->[1];
        my $consoleCmd;

        # Start console spooling
        if ( $action eq 'start' ) {
            $consoleCmd = "spool console start";
            $out = xCAT::zvmCPUtils->sendCPCmd( $::SUDOER, $hcp, $userId, $consoleCmd );
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        }

        # Stop console
        elsif ( $action eq 'stop' ) {
            $consoleCmd = "spool console stop";
            $out = xCAT::zvmCPUtils->sendCPCmd( $::SUDOER, $hcp, $userId, $consoleCmd );
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        }

        else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" );
        }
    }

    # aemod [function] [parm1 (optional)] [parm2 (optional)]..
    elsif ($args->[0] eq "--aemod") {
        my $parm = "";
        my $conf = "";
        my $i = 0;
        my $invokescript = "invokeScript.sh";
        my $workerscript = $args->[1];
        my $argsSize = @{$args};
        my $trunkFile = "aemod.doscript";
        my $class = "X";
        my $tempDir = `/bin/mktemp -d /tmp/aemod.XXXXXXXX`;
        chomp($tempDir);

        $conf .= sprintf("%s\n", "#!/bin/bash");
        $parm = "/bin/bash $workerscript";
        for ( $i = 2 ; $i < $argsSize ; $i++ ) {
            $parm .= ' ';
            $parm .= $args->[$i];
        }
        $conf .= sprintf("%s\n", $parm);

        open(FILE, ">$tempDir/$invokescript");
        print FILE ("$conf");
        close(FILE);

        if (-e "/opt/xcat/share/xcat/scripts/$workerscript") {
            # Generate the tar package for punch
            my $oldpath=cwd();
            system("cp /opt/xcat/share/xcat/scripts/$workerscript $tempDir");
            chdir($tempDir);
            system("tar cvf $trunkFile $invokescript $workerscript");

            # Check the node, if node status contains XCATCONF4Z=0, store it in a tmp directory for later use.
            # Otherwise, punch it directly.
            my $nodeFlag = '';
            my $cfgTrunkDir = "/tmp/configdrive/$node/";
            my @propNames = ('status');
            my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames );
            $nodeFlag = $propVals->{'status'};
            if ($nodeFlag =~ /XCATCONF4Z=0/) {
                if (!-d $cfgTrunkDir) {
                    mkpath($cfgTrunkDir, 0, 0750);
                }
                my $rc = `/bin/cp -r $tempDir $cfgTrunkDir/ 2>/dev/null; echo $?`;
                `rm -rf $tempDir`;
                if ($rc != '0') {
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source directory $tempDir to directory $cfgTrunkDir, please check if xCAT is running out of space" );
                    rmtree "$cfgTrunkDir";
                    return;
                }
            } else {
                # Online zHCP's punch device
                $out = xCAT::zvmUtils->onlineZhcpPunch($::SUDOER, $hcp);
                if ( $out =~ m/Failed/i ) {
                    xCAT::zvmUtils->printLn( $callback, "$node: Online zHCP's punch device... $out" );
                    `rm -rf $tempDir`;
                    return;
                }

                # Punch file to reader
                xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $trunkFile, $trunkFile);
                $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $trunkFile, $trunkFile, "", $class );
                chdir($oldpath);
                if ( $out =~ m/Failed/i ) {
                    xCAT::zvmUtils->printLn( $callback, "$node: Punching file to reader... $out" );
                    `rm -rf $tempDir`;
                    return;
                }
            }
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) The worker script $workerscript does not exist on xCAT server" );
            rmdir($tempDir);
            return;
        }
    }

    # Otherwise, print out error
    else {
        $out = "$node: (Error) Option '$args->[0]' not supported on chvm";
        xCAT::zvmUtils->printLn( $callback, "$out" );
    }
    return;
}

#-------------------------------------------------------

=head3   powerVM

    Description  : Power on or off a given node
    Arguments    :   Node
                     Option [on|off|reboot|reset|stat|isreachable]
    Returns      : Nothing
    Example      : powerVM($callback, $node, $args);

=cut

#-------------------------------------------------------
sub powerVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid', 'status' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );
    my $status = $propVals->{'status'};

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        # This may be a stat query to a zVM host hypervisor, if so return on/off for the zhcp node name
        # Look in the hosts table for this zhcp to get the node name then look back in zvm table
        # to get the userid to check the power on.
        if ( $args->[0] eq 'stat') {
           my @propNames2 = ( 'node' );
           my $propVals2 = xCAT::zvmUtils->getTabPropsByKey('hosts', 'hostnames', $hcp, @propNames2);
           if ( !$propVals2->{'node'} ) {
              xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to look up zhcp node for $hcp" );
              return;
           }
           # Now we have a node name for this zhcp, get the userid so we can check the power.
           my $node2 = $propVals2->{'node'};
           $propVals2 = xCAT::zvmUtils->getNodeProps( 'zvm', $node2, @propNames );
           $userId = $propVals2->{'userid'};
           if ( !$userId ) {
              xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID on zhcp node $node2" );
              return;
           }
        } else {
           xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
           return;
        }
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("powerVM() node:$node userid:$userId zHCP:$hcp sudoer:$::SUDOER sudo:$::SUDO");

    # Output string
    my $out;

    # Power on virtual server
    if ( $args->[0] eq 'on' ) {
        # Check the node flag, if it contain XCATCONF4Z=0, it indicate that this node will be deployed by using non-xcatconf4z type
        # image, it will call the reconstructor to generate a final punched file, and punched to reader. otherwise, power on the vm directly.
        my $nodeFlag = '';
        my $cfgTrunkDir = "/tmp/configdrive/$node";
        my @propNames = ('status');
        my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames );
        my $cfgdrive = '';
        my $destCfgdrive = '';
        my $class = "X";
        $nodeFlag = $propVals->{'status'};
        # When start the non-xcatconf4z cloud image at first time, we need to modify cfgdrive.tgz to append xCAT key, so adding SSH and IUCV
        # check to ensure it is the first start, since after vm is started SSH and IUCV flag is set.
        if ( $nodeFlag =~ /XCATCONF4Z=0/ && $nodeFlag !~ /SSH=1/ && $nodeFlag != /IUCV=1/ ) {
            # Call constructor to generate a final configdrive for target vm
            $cfgdrive = xCAT::zvmUtils->genCfgdrive($cfgTrunkDir);
            if ( -e $cfgdrive ) {
                # Purge reader
                $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId );
                xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" );

                # Online zHCP's punch device
                $out = xCAT::zvmUtils->onlineZhcpPunch($::SUDOER, $hcp);
                if ( $out =~ m/Failed/i ) {
                    `rm -rf $cfgTrunkDir`;
                    xCAT::zvmUtils->printLn( $callback, "$node: Online zHCP's punch device... $out" );
                    return;
                }

                $destCfgdrive = "/tmp/$node-" . basename($cfgdrive);
                # Punch file to reader
                xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $cfgdrive, $destCfgdrive);
                `/bin/rm -rf $cfgTrunkDir`;

                $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $destCfgdrive, basename($cfgdrive), "", $class );
                `ssh $::SUDOER\@$hcp "$::SUDO /bin/rm $destCfgdrive"`;
                if ( $out =~ m/Failed/i ) {
                    xCAT::zvmUtils->printLn( $callback, "$node: Punching final config drive to reader... $out" );
                    return;
                }
            } else {
                xCAT::zvmUtils->printLn( $callback, "$node: Failed to generate the final cfgdrive for target vm" );
                return;
            }
        }

        xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $userId");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $userId"`;
        my $rc = $? >> 8;
        $out = xCAT::zvmUtils->trimStr( $out );
        if ( $rc == 255 ) {
            xCAT::zvmUtils->printSyslog( "$node: (Error) Failed to communicate with the zhcp system: $hcp output:$out" );
            return;
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        }

        my $delscriptpath = "/var/lib/sspmod/portdelete";
        # If user doesn't use NIC which is record in switch table. When power on the instance, delete
        # the port at the same time. Use file "portdelete" to delete the ports, it exists on CMA appliance.
        # If the script of "portdelete" doesn't exist, the function is skipped, ports will not be deleted.
        if ( -e $delscriptpath and $status =~ /CLONING=1/ and $status =~ /CLONE_ONLY=1/ ) {
            xCAT::zvmUtils->printSyslog( "$node: Delete the port that is NOT used for Alternate Deploy Provisioning.");
            # Get the list from switch table.
            my $switchTab = xCAT::Table->new('switch');
            if ( !$switchTab ) {
                xCAT::zvmUtils->printSyslog( "$node: (Error) Could not open table: switch.");
                return;
            }
            my @portData = $switchTab->getAllAttribsWhere( "node='".lc($userId)."'", 'port' );
            $switchTab->close;
            my $ports = '';
            foreach ( @portData ) {
                $ports = $ports.' '.$_->{'port'};
            }
            my $out = `$delscriptpath $ports`;
            $rc = $?;
            $out = xCAT::zvmUtils->trimStr( $out );
            if ( $rc !=0 ) {
                xCAT::zvmUtils->printSyslog( "$node:(Error) Failed to delete port output:$out" );
                return;
            }else {
                xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            }
        }

        # If we were cloning the server then turn off cloning flag.
        if ( $status =~ /CLONING=1/ ) {
            $status =~ s/CLONING=1/CLONING=0/g;
        }

        if ( $status =~ /CLONE_ONLY=1/ ) {
            # Indicate node is being powered up so that we will confirm the IP address on the nodestat.
            if ( $status =~ /POWER_UP=/ ) {
                $status =~ s/POWER_UP=0/POWER_UP=1/g;
            } else {
                $status = "$status;POWER_UP=1";
            }
            xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status );
        }
    }

    # Power off virtual server
    elsif ( $args->[0] eq 'off' ) {
        xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId -f IMMED");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId -f IMMED"`;
        my $rc = $? >> 8;
        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
            return;
        }
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ($out =~ m/Return Code: 200/i){
            if ($out =~ m/Reason Code: 12/i) {
                xCAT::zvmUtils->printSyslog("$userId already logged off.");
                $out = "$userId already logged off.";
                $rc = 0;
            } elsif ($out =~ m/Reason Code: 16/i) {
                xCAT::zvmUtils->printSyslog("$userId in process of logging off.");
                $out = "$userId in process of logging off.";
                $rc = 0;
            }
        }
        if ( $rc == -1 ) {
            xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out");
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            return;
        }
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
    }

    # Power off virtual server (gracefully)
    elsif ( $args->[0] eq 'softoff' ) {
        my $ping = xCAT::zvmUtils->pingNode($node);
        my $sleepseconds = 15;
        if ($ping eq "ping") {
            #$out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -h now"`;
            my $cmd = "$::SUDO shutdown -h now";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
            sleep($sleepseconds);    # Wait 15 seconds before logging user off
        }

        xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`;
        my $rc = $? >> 8;
        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
            return;
        }
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ($out =~ m/Return Code: 200/i){
            if ($out =~ m/Reason Code: 12/i) {
                xCAT::zvmUtils->printSyslog("$userId already logged off.");
                $out = "$userId already logged off.";
                $rc = 0;
            } elsif ($out =~ m/Reason Code: 16/i) {
                xCAT::zvmUtils->printSyslog("$userId in process of logging off.");
                $out = "$userId in process of logging off.";
                $rc = 0;
            }
        }
        if ( $rc == -1 ) {
            xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out");
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            return;
        }
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
    }

    # Get the status (on|off)
    elsif ( $args->[0] eq 'stat' ) {
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/HCPCQU361E.*/off/' | sed 's/$userId.*/on/'`;
        my $rc = $? >> 8;
        if ($rc != 0) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $out, rc is $rc" );
        }
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
    }

    # Reset a virtual server
    elsif ( $args->[0] eq 'reset' ) {

        xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`;
        my $rc = $? >> 8;
        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
            return;
        }
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ($out =~ m/Return Code: 200/i){
            if ($out =~ m/Reason Code: 12/i) {
                xCAT::zvmUtils->printSyslog("$userId already logged off.");
                $out = "$userId already logged off.";
                $rc = 0;
            } elsif ($out =~ m/Reason Code: 16/i) {
                xCAT::zvmUtils->printSyslog("$userId in process of logging off.");
                $out = "$userId in process of logging off.";
                $rc = 0;
            }
        }
        if ( $rc == -1 ) {
                xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out" );
                xCAT::zvmUtils->printLn( $callback, "$node: $out" );
                return;
        }
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        # Wait for output
        while ( `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/Done/'` !~ "Done" ) {
            sleep(5);
        }

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $userId"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $userId");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        if ( $status =~ /CLONE_ONLY=1/ ) {
            # Indicate node is being powered up so that we will confirm the IP address on the nodestat.
            if ( $status =~ /POWER_UP=/ ) {
                $status =~ s/POWER_UP=0/POWER_UP=1/g;
            } else {
                $status = "$status;POWER_UP=1";
            }
            xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status );
        }
    }

    # Reboot a virtual server
    elsif ( $args->[0] eq 'reboot' ) {
        my $timeout = 0;
        #$out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -r now &>/dev/null"`;
        my $cmd = "$::SUDO shutdown -r now &>/dev/null";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);

        # Wait until node is down or 180 seconds
        while ($timeout < 180) {
            my $ping = xCAT::zvmUtils->pingNode($node);
            if ($ping eq "ping") {
                sleep(1);
                $timeout++;
            } else {
                last;
            }
        }
        if ($timeout >= 180) {
            xCAT::zvmUtils->printLn( $callback, "$node: Shutting down $userId... Failed\n" );
            return;
        }

        xCAT::zvmUtils->printLn( $callback, "$node: Shutting down $userId... Done\n" );

        # Wait until node is up or 180 seconds
        $timeout = 0;
        while ($timeout < 180) {
            my $ping = xCAT::zvmUtils->pingNode($node);
            if ($ping eq "noping") {
                sleep(1);
                $timeout++;
            } else {
                last;
            }
        }
        if ($timeout >= 180) {
            xCAT::zvmUtils->printLn( $callback, "$node: Rebooting $userId... Failed\n" );
            return;
        }

        if ( $status =~ /CLONE_ONLY=1/ ) {
            # Indicate node is being powered up so that we will confirm the IP address on the nodestat.
            if ( $status =~ /POWER_UP=/ ) {
                $status =~ s/POWER_UP=0/POWER_UP=1/g;
            } else {
                $status = "$status;POWER_UP=1";
            }
            xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status );
        }

        xCAT::zvmUtils->printLn( $callback, "$node: Rebooting $userId... Done\n" );
    }

    # Pause a virtual server
    elsif ( $args->[0] eq 'pause' ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Pause -T $userId -k PAUSE=YES"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Pause -T $userId -k PAUSE=YES");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
    }

    # Unpause a virtual server
    elsif ( $args->[0] eq 'unpause' ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Pause -T $userId -k PAUSE=NO"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Pause -T $userId -k PAUSE=NO");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
    }

    #Check VM reachable status
    elsif ( $args->[0] eq 'isreachable' ) {
        if ($status =~ /CLONE_ONLY=1/ and $status =~ /POWER_UP=1/){
            # Special test and handling for 's390x' architecture nodes which are in the nodetype
            # and zvm table and are marked as being powered up.
            my %generalArgs;
            $generalArgs{'verbose'} = 0;
            my $nodes = [$node];
            xCAT::zvmUtils->handlePowerUp( $callback, $nodes, \%generalArgs );
        }
        # Check vm's status
        xCAT::zvmUtils->printSyslog("check $node isreachable");
        my $cmd = "$::SUDO date";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd);
        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$node: unreachable");
            return;
        }
        # Create output string
        if ($out) {
            xCAT::zvmUtils->printLn( $callback, "$node: reachable");
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: unreachable");
        }
    }

    else {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" );
    }
    return;
}

#-------------------------------------------------------

=head3   scanVM

    Description : Get node information from zHCP
    Arguments   : zHCP
    Returns     : Nothing
    Example     : scanVM($callback, $node, $args);

=cut

#-------------------------------------------------------
sub scanVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;
    my $write2db = '';
    if ($args) {
        @ARGV = @$args;

        # Parse options
        GetOptions( 'w' => \$write2db );
    }

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    # Exit if node is not a HCP
    if ( !( $hcp =~ m/$node/i ) ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) $node is not a hardware control point" );
        return;
    }

    # Print output string
    # [Node name]:
    #   objtype=node
    #   id=[userID]
    #   arch=[Architecture]
    #   hcp=[HCP node name]
    #   groups=[Group]
    #   mgt=zvm
    #
    # gpok123:
    #   objtype=node
    #   id=LINUX123
    #   arch=s390x
    #   hcp=gpok456.endicott.ibm.com
    #   groups=all
    #   mgt=zvm

    # Output string
    my $str = "";

    # Get nodes managed by this zHCP
    # Look in 'zvm' table
    my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 );
    my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $hcp . "%'", 'node', 'userid' );

    my $out;
    my $node2;
    my $id;
    my $os;
    my $arch;
    my $groups;

    # Get node hierarchy from /proc/sysinfo
    my $hierarchy;
    my $host = xCAT::zvmCPUtils->getHost($::SUDOER, $hcp);
    my $sysinfo = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO cat /proc/sysinfo"`;

    # Get node CEC
    my $cec = `echo "$sysinfo" | grep -a "Sequence Code"`;
    my @args = split( ':', $cec );
    # Remove leading spaces and zeros
    $args[1] =~ s/^\s*0*//;
    $cec = xCAT::zvmUtils->trimStr($args[1]);

    # Get node LPAR
    my $lpar = `echo "$sysinfo" | grep -a "LPAR Name"`;
    @args = split( ':', $lpar );
    $lpar = xCAT::zvmUtils->trimStr($args[1]);

    # Save CEC, LPAR, and zVM to 'zvm' table
    my %propHash;
    if ($write2db) {
        # Save CEC to 'zvm' table
        %propHash = (
            'nodetype'  =>     'cec',
            'parent'    =>     ''
        );
        xCAT::zvmUtils->setNodeProps( 'zvm', $cec, \%propHash );

        # Save LPAR to 'zvm' table
        %propHash = (
            'nodetype'  =>     'lpar',
            'parent'    =>     $cec
        );
        xCAT::zvmUtils->setNodeProps( 'zvm', $lpar, \%propHash );

        # Save zVM to 'zvm' table
        %propHash = (
            'nodetype'  =>     'zvm',
            'parent'    =>     $lpar
        );
        xCAT::zvmUtils->setNodeProps( 'zvm', lc($host), \%propHash );
    }

    # Search for s managed by given zHCP
    # Get 'node' and 'userid' properties
    %propHash = ();
    foreach (@entries) {
        $node2 = $_->{'node'};

        # Get groups
        @propNames = ('groups');
        $propVals  = xCAT::zvmUtils->getNodeProps( 'nodelist', $node2, @propNames );
        $groups    = $propVals->{'groups'};

        # Load VMCP module
        xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node2);

        # Get user ID
        @propNames = ('userid');
        $propVals  = xCAT::zvmUtils->getNodeProps( 'zvm', $node2, @propNames );
        $id = $propVals->{'userid'};
        if (!$id) {
            $id = xCAT::zvmCPUtils->getUserId($::SUDOER, $node2);
        }

        # Get architecture
        #$arch = `ssh -o ConnectTimeout=2 $::SUDOER\@$node2 "uname -p"`;
        my $cmd = "$::SUDO uname -p";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node2, $cmd, $callback);
        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
            return;
        }

        $arch = xCAT::zvmUtils->trimStr($out);
        if (!$out) {
            # Assume arch is s390x
            $arch = 's390x';
        }

        # Get OS
        $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node2);

        # Save node attributes
        if ($write2db) {

            # Do not save if node = host
            if (!(lc($host) eq lc($node2))) {
                # Save to 'zvm' table
                %propHash = (
                    'hcp'       => $hcp,
                    'userid'    => $id,
                    'nodetype'  => 'vm',
                    'parent'    => lc($host)
                );
                xCAT::zvmUtils->setNodeProps( 'zvm', $node2, \%propHash );

                # Save to 'nodetype' table
                %propHash = (
                    'arch'  => $arch,
                    'os'    => $os
                );
                xCAT::zvmUtils->setNodeProps( 'nodetype', $node2, \%propHash );
            }
        }

        # Create output string
        $str .= "$node2:\n";
        $str .= "  objtype=node\n";
        $str .= "  arch=$arch\n";
        $str .= "  os=$os\n";
        $str .= "  hcp=$hcp\n";
        $str .= "  userid=$id\n";
        $str .= "  nodetype=vm\n";
        $str .= "  parent=$host\n";
        $str .= "  groups=$groups\n";
        $str .= "  mgt=zvm\n\n";
    }

    xCAT::zvmUtils->printLn( $callback, "$str" );
    return;
}

#-------------------------------------------------------

=head3   inventoryVM

    Description : Get hardware and software inventory of a given node
    Arguments   :   Node
                    Type of inventory (all|config|console [logsize]|cpumem|cpumempowerstat|--freerepospace)
    Returns     : Nothing, errors returned in $callback
    Example     : inventoryVM($callback, $node, $args);

=cut

#-------------------------------------------------------
sub inventoryVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Output string
    my $str = "";

    my $outmsg;
    my $rc;

    # Check if node is pingable
    if (($args->[0] ne '--consoleoutput') and ($args->[0] ne 'cpumempowerstat')){
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "noping") {
            $str = "$node: (Error) Host is unreachable";
            xCAT::zvmUtils->printLn( $callback, "$str" );
        return;
        }
    }

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    # Get zvm system node name. nodetype should be zvm.
    my $tab2 = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 );
    my @results2 = $tab2->getAllAttribsWhere( "nodetype='zvm'", 'hcp', 'node' );
    my $hypervisornode = "unknown";
    foreach (@results2) {
        if ( $_->{'hcp'} eq $hcp ) {
            $hypervisornode = $_->{'node'};
        }
    }

    # Load VMCP module
    xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node);

    # Get configuration
    if ( $args->[0] eq 'config' ) {

        # Get z/VM host for specified node
        my $host = xCAT::zvmCPUtils->getHost($::SUDOER, $node);

        # Get architecture
        my $arch = xCAT::zvmUtils->getArch($::SUDOER, $node);

        # Get operating system
        my $os = xCAT::zvmUtils->getOs($::SUDOER, $node);

        # Get privileges
        my $priv = xCAT::zvmCPUtils->getPrivileges($::SUDOER, $node);

        # Get memory configuration
        my $memory = xCAT::zvmCPUtils->getMemory($::SUDOER, $node);

        # Get max memory
        my $maxMem = xCAT::zvmUtils->getMaxMemory($::SUDOER, $hcp , $node);

        # Get processors configuration
        my $proc = xCAT::zvmCPUtils->getCpu($::SUDOER, $node);

        $str .= "z/VM UserID: $userId\n";
        $str .= "z/VM Host: $host\n";
        $str .= "Operating System: $os\n";
        $str .= "Architecture: $arch\n";
        $str .= "HCP: $hcp\n";
        $str .= "Privileges: \n$priv\n";
        $str .= "Total Memory: $memory\n";
        $str .= "Max Memory: $maxMem\n";
        $str .= "Processors: \n$proc\n";
        $str .= "xCAT Hypervisor Node: $hypervisornode\n"; # new field for GUI

    } elsif ( $args->[0] eq 'cpumem' ) {

        # Get memory configuration
        my $memory = xCAT::zvmCPUtils->getMemory($::SUDOER, $node);

        # Get processors configuration
        my $proc = xCAT::zvmCPUtils->getCpu($::SUDOER, $node);

        # Get instance CPU used time
        my $cputime = xCAT::zvmUtils->getUsedCpuTime($::SUDOER, $hcp , $node);
        if (xCAT::zvmUtils->checkOutput( $cputime ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$cputime" );
            return;
        }

        $str .= "Total Memory: $memory\n";
        $str .= "Processors: \n$proc\n";
        $str .= "CPU Used Time: $cputime\n";

    } elsif ( $args->[0] eq 'cpumempowerstat' ) {
        # This option will check power stat then based on the power stat, use
        # SMAPI to query the cpu, mem and uptime. so all info is done in one
        # SMAPI call will help the performance enhancement.
        my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null"`;
        xCAT::zvmUtils->printSyslog( "$node: power stat query return: $out");
        if ( $out =~ 'HCPCQU045E' or $out =~ 'HCPCQU361E' ) {
            $out = 'off';
        } elsif ( $out =~ $userId) {
            $out = 'on';
        } else {
            # should not be here
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) power stat query return not parsable, the result is $out");
            return
        }

        if ( $out eq 'off') {
            # upper layer should check power off state first
            $str .= "Power state: off\n";
            $str .= "Total Memory: 0M\n";
            $str .= "Processors: 0\n";
            $str .= "CPU Used Time: 0 sec\n";
        } else {
            # This is 'on' branch, we should be able to query info
            xCAT::zvmUtils->printSyslog( "$node: calling smcli Image_Performance_Query -T $userId -c 1" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Performance_Query -T $userId -c 1"`;
            my $rc = $? >> 8;

            if ($rc == 255) {
                xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" );
                return;
            }
            if ( $rc ) {
                xCAT::zvmUtils->printSyslog( "$node: (Error) calling smcli Image_Performance_Query -T $userId" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) calling smcli Image_Performance_Query -T $userId" );
                return;
            }
            # In order to save SMAPI call effort, we didn't set them into separated function
            # just get SMAPI output and parse the output
            my $time = `echo -e "$out" | egrep -a -i "Used CPU time:"`;
            $time =~ s/^Used CPU time:(.*)/$1/;
            my @timearray = split(' ', $time);
            # Get value is us , need make it seconds
            my $usedtime = $timearray[0]/1000000;

            my $cpus = `echo -e "$out" | egrep -a -i "Guest CPUs:"`;
            $cpus =~ s/^Guest CPUs:(.*)/$1/;
            my @cpuarray = split(' ', $cpus);
            my $totalcpu = $cpuarray[0];

            # This is the used memory, not max mem defined in user dirct, it's in KB
            my $mem = `echo -e "$out" | egrep -a -i "Max memory:"`;
            $mem =~ s/^Max memory:(.*)/$1/;
            my @memarry = split(' ', $mem);
            my $totalmem = $memarry[0]/1024;

            $str .= "Power state: on\n";
            $str .= "Total Memory: $totalmem"."M\n";
            $str .= "Processors: $totalcpu\n";
            $str .= "CPU Used Time: $usedtime"." sec\n";
        }
    } elsif ( $args->[0] eq 'all' ) {

        # Get z/VM host for specified node
        my $host = xCAT::zvmCPUtils->getHost($::SUDOER, $node);

        # Get architecture
        my $arch = xCAT::zvmUtils->getArch($::SUDOER, $node);

        # Get operating system
        my $os = xCAT::zvmUtils->getOs($::SUDOER, $node);

        # Get privileges
        my $priv = xCAT::zvmCPUtils->getPrivileges($::SUDOER, $node);

        # Get memory configuration
        my $memory = xCAT::zvmCPUtils->getMemory($::SUDOER, $node);

         # Get max memory
        my $maxMem = xCAT::zvmUtils->getMaxMemory($::SUDOER, $hcp , $node);

        # Get processors configuration
        my $proc = xCAT::zvmCPUtils->getCpu($::SUDOER, $node);

        # Get disks configuration
        my $storage = xCAT::zvmCPUtils->getDisks($::SUDOER, $node);

        # Get NICs configuration
        my $nic = xCAT::zvmCPUtils->getNic($::SUDOER, $node);

        # Get zFCP device info
        my $zfcp = xCAT::zvmUtils->getZfcpInfo($::SUDOER, $node);

        # Get OS system up time
        my $uptime = xCAT::zvmUtils->getUpTime($::SUDOER, $node);

        # Get instance CPU used time
        my $cputime = xCAT::zvmUtils->getUsedCpuTime($::SUDOER, $hcp , $node);
        if (xCAT::zvmUtils->checkOutput( $cputime ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$cputime" );
            return;
        }

        # Create output string
        $str .= "z/VM UserID: $userId\n";
        $str .= "z/VM Host: $host\n";
        $str .= "Operating System: $os\n";
        $str .= "Architecture: $arch\n";
        $str .= "HCP: $hcp\n";
        $str .= "Uptime: $uptime\n";
        $str .= "CPU Used Time: $cputime\n";
        $str .= "Privileges: \n$priv\n";
        $str .= "Total Memory: $memory\n";
        $str .= "Max Memory: $maxMem\n";
        $str .= "Processors: \n$proc\n";
        $str .= "Disks: \n$storage\n";
        if ($zfcp) {
            $str .= "zFCP: \n$zfcp\n";
        }
        $str .= "NICs: \n$nic\n";
        $str .= "xCAT Hypervisor Node: $hypervisornode\n"; # new field for GUI

    } elsif ( $args->[0] eq '--freerepospace' ) {

        # Get /install available disk size
        my $freespace = xCAT::zvmUtils->getFreeRepoSpace($::SUDOER, $node);

        # Create output string
        if ($freespace) {
            $str .= "Free Image Repository: $freespace\n";
        } else {
            return;
        }

    # Get console output
    } elsif ( $args->[0] eq '--consoleoutput' ) {

        my $argsSize = @{$args};

        # Let SMAPI execution on ZHCP to punch the console log to the caller
        my $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Console_Get -T $userId"`;
        xCAT::zvmUtils->printSyslog("$::SUDOER\@$hcp $::SUDO $::DIR/smcli Image_Console_Get -T $userId");

        my $out;
        chomp( $out = `ssh $::SUDOER\@$hcp "$::SUDO cat /sys/bus/ccw/drivers/vmur/0.0.000c/online"` );
        if ($out != 1) {
            chomp( $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r 000c; /sbin/chccwdev -e 000c"`);
            if ( !( $out =~ m/Done$/i ) ) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to online the zHCP's reader, cmd output: $out.");
                xCAT::zvmUtils->printSyslog( "inventoryVM() Failed to online the zHCP's reader, cmd output: $out." );
                return;
            }
            $out = `ssh $::SUDOER\@$hcp "$::SUDO which udevadm &> /dev/null && udevadm settle || udevsettle"`;
        }
        # we need set class otherwise we will get error like:
        # vmur: Reader device class does not match spool file class.
        $out = `ssh $::SUDOER\@$hcp "$::SUDO vmcp spool c class \\*"`;
        xCAT::zvmUtils->printSyslog( "vmcp spool c class return: $out" );

        # Get console output from zhcp
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/sbin/vmur list"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO /usr/sbin/vmur list\"", $hcp, "inventoryVM", $out, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $out = `echo "$out" | egrep -a -i "$userId "`;
        my @spoolFiles = sort(split('\n', $out));
        $str = "";
        foreach (@spoolFiles){
            if (!(length $_)) {next;}
            my @fileProperty = split(' ', $_);
            my $spoolFileId = $fileProperty[1];
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/sbin/vmur re -t -O $spoolFileId"`;
            $str .= $out
        }

        # Prepare to output
        my $str_length = length($str);
        if (!$str_length) {
            $str = "(Error) No console log avaiable";
            # Append hostname (e.g. gpok3) in front
            $str = xCAT::zvmUtils->appendHostname( $node, $str );

            xCAT::zvmUtils->printLn( $callback, "$str" );
            return;
        } elsif ($argsSize eq 2) {
            my $logsize = $args->[1];
            # only output last $logsize bytes of console log
            if (($logsize > 0) and ($logsize < $str_length)) {
                $str = substr($str, -$logsize);
                my $truncatd = $str_length - $logsize;
                $str = "Truncated console log, $truncatd bytes ignored\n".$str
            }
        }
        # Append hostname (e.g. gpok3) in front
        $str = xCAT::zvmUtils->appendHostname( $node, $str );
        xCAT::zvmUtils->printInfo( $callback, "$str" );
        return;

    } else {
        $str = "$node: (Error) Option not supported";
        xCAT::zvmUtils->printLn( $callback, "$str" );
        return;
    }

    # Append hostname (e.g. gpok3) in front
    $str = xCAT::zvmUtils->appendHostname( $node, $str );

    xCAT::zvmUtils->printLn( $callback, "$str" );
    return;
}

#-------------------------------------------------------

=head3   listVM

    Description : Show the info for a given node
    Arguments   : Node
                  Option
    Returns     : Nothing
    Example     : listVM($callback, $node);

=cut

#-------------------------------------------------------
sub listVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Set cache directory
    my $cache = '/var/opt/zhcp/cache';

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid', 'status' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    my $out;
    my $rc;

    # Get disk pool configuration
    if ( $args->[0] eq "--diskpool" ) {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Get disk pool names
    elsif ( $args->[0] eq "--diskpoolnames" ) {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Get network names
    elsif ( $args->[0] eq "--getnetworknames" ) {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Get network
    elsif ( $args->[0] eq "--getnetwork" ) {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Get the status of all DASDs accessible to a virtual image
    elsif ( $args->[0] eq "--querydisk" ) {
        my $vdasd = $args->[1];

        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Query -T $userId -k $vdasd"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Disk_Query -T $userId -k $vdasd");
    }

    # Get the status of all DASDs accessible to a the system
    elsif ( $args->[0] eq "--queryalldisks" ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Disk_Query -T MAINT -k dev_num=ALL -k disk_size=YES"`;
        xCAT::zvmUtils->printSyslog(" ssh zhcp smcli System_Disk_Query -T MAINT -k dev_num=ALL -k disk_size=YES");
    }

    # Get list of PAGE volumes
    elsif ( $args->[0] eq "--querypagevolumes" ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Page_Utilization_Query -T MAINT "`;
        xCAT::zvmUtils->printSyslog(" ssh zhcp smcli System_Page_Utilization_Query -T MAINT");
    }

    # Get list of SPOOL volumes
    elsif ( $args->[0] eq "--queryspoolvolumes" ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Spool_Utilization_Query -T MAINT "`;
        xCAT::zvmUtils->printSyslog(" ssh zhcp smcli System_Spool_Utilization_Query -T MAINT");
    }

    # Get user profile names
    elsif ( $args->[0] eq "--userprofilenames" ) {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Get zFCP disk pool configuration
    elsif ( $args->[0] eq "--zfcppool" ) {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Get zFCP disk pool names
    elsif ( $args->[0] eq "--zfcppoolnames") {
        # This is no longer supported in lsvm. Using inventoryHypervisor instead.
        inventoryHypervisor( $callback, $node, $args );
    }

    # Check whether instance has given NIC
    elsif ( $args->[0] eq "--checknics") {
        if ($propVals->{'status'} =~ /CLONE_ONLY=1/) {
            xCAT::zvmUtils->printSyslog("$node: cloned flag detected, no further check");
        } else {
            # Get the directory data without the *DVHOPT line
            xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'");
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`;
            $rc = $? >> 8;

            if ($rc == 255) {
                xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" );
                return;
            }
            if ( $rc ) {
                xCAT::zvmUtils->printSyslog( "$node: (Error) calling smcli Image_Query_DM -T $userId" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) calling smcli Image_Query_DM -T $userId" );
                return;
            }
            $out =~ s/\*DVHOPT(.*)//s; # remove last line with *DVHOPT and newline after it

            my $argsSize = @{$args};
            if ($argsSize != 2) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Nodename and only one NIC address must be input");
                return;
            }
            # For each NIC given, check whether it exists in the user direct of user
            # In case this is a cloned instance, will not check whether it exist or not
            # directly return True.
            my $i;
            my $dev;

            $dev = $args->[1];
            if ($dev =~ m/^[0-9a-fA-F]{1,4}/) {
                # add '0' to $dev, e.g 0100 or 100 are both valid, the length check already done above
                if ($out =~ m/.*NICDEF [0]*$dev TYPE QDIO LAN SYSTEM .*/i ) {
                    xCAT::zvmUtils->printSyslog("$node: succeed in find $dev in user direct");
                } else {
                    # Not return $out to upper layer as it might contain password
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) not able to find $dev in user direct");

                    xCAT::zvmUtils->printSyslog("$node: (Error) not able to find $dev in user direct: $out");
                    return;
                }
            } else {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) input NIC param $dev invalid");
                return;
            }
        }
        # ok, NIC we planned to check are found or this is a cloned node.
        $out = '';
    }

    # Get user entry
    elsif ( !$args->[0] ) {

        # Get the directory data without the *DVHOPT line
        xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`;
        $rc = $? >> 8;

        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" );
            return;
        }
        if ( $rc ) {
            xCAT::zvmUtils->printSyslog( "$node: (Error) calling smcli Image_Query_DM -T $userId" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) calling smcli Image_Query_DM -T $userId" );
            return;
        }
        $out =~ s/\*DVHOPT(.*)//s; # remove last line with *DVHOPT and newline after it
    } else {
        $out = "$node: (Error) Option not supported";
    }

    # Append hostname (e.g. gpok3) in front
    $out = xCAT::zvmUtils->appendHostname( $node, $out );
    xCAT::zvmUtils->printLn( $callback, "$out" );

    return;
}

#-------------------------------------------------------

=head3   makeVM

    Description : Create a virtual machine
                   * A unique MAC address will be assigned
    Arguments   :  Node
                   Directory entry text file (optional)
                   Upstream instance ID (optional)
                   Upstream request ID (optional)
    Returns     : Nothing
    Example     : makeVM($callback, $node, $args);

=cut

#-------------------------------------------------------
sub makeVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );
    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("makeVM for node:$node on zhcp:$hcp");

    # Find the number of arguments
    my $argsSize = @{$args};

    # Create a new user in zVM without user directory entry file
    my $out;
    my $outmsg;
    my $rc;
    my $stdin;
    my $password = "";
    my $memorySize = "";
    my $privilege = "";
    my $profileName = "";
    my $cpuCount = 1;
    my $diskPool = "";
    my $diskSize = "";
    my $diskVdev = "";
    my $ipl = "";
    my $logonby = "";
    my $requestId = "NoUpstreamRequestID"; # Default is still visible in the log
    my $objectId = "NoUpstreamObjectID"; # Default is still visible in the log
    if ($args) {
        @ARGV = @$args;

        # Parse options
        GetOptions(
            's|stdin' => \$stdin,  # Directory entry contained in stdin
            'p|profile=s' => \$profileName,
            'w|password=s' => \$password,
            'c|cpus=i' => \$cpuCount,  # Optional
            'm|mem=s' => \$memorySize,
            'd|diskpool=s' => \$diskPool,
            'z|size=s' => \$diskSize,
            'v|diskvdev=s' => \$diskVdev,  # Optional
            'r|privilege=s' => \$privilege, # Optional
            'q|requestid=s' => \$requestId,  # Optional
            'j|objectid=s' => \$objectId,  # Optional
            'i|ipl=s' => \$ipl, # Optional
            'l|logonby=s' => \$logonby);  # Optional
    }
    # If one of the options above are given, create the user without a directory entry file
    if ($profileName || $password || $memorySize) {
        if (!$profileName || !$password || !$memorySize) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more required parameter(s)" );
            return;
        }

        # Default privilege to G if none is given
        if (!$privilege) {
            $privilege = 'G';

        }

        # validate the logonby userid
        my @userids = split(' ', $logonby);
        if (scalar(@userids) > 8) {
            xCAT::zvmUtils->printSyslog("logonby statement contains more than 8 users which is not allowed, the value is: $logonby");
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) logonby statement contains more than 8 users which is not allowed, the value is: $logonby");
            return;
        }
        for ( my $i = 0 ; $i < scalar(@userids) ; $i++ ) {
            if (length($userids[$i]) > 8) {
                xCAT::zvmUtils->printSyslog("logonby userid $userids[$i] contains more than 8 chars");
                xCAT::zvmUtils->printLn( $callback, "$node: logonby userid $userids[$i] contains more than 8 chars");
                return;
            }
        }
        # Generate temporary user directory entry file
        my $userEntryFile = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount, $ipl, $logonby);
        if ( $userEntryFile == -1 ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to generate user directory entry file" );
            return;
        }
        # Create a new user in z/VM without disks
        $out = `/opt/xcat/bin/mkvm $node $userEntryFile 2>&1`;
        xCAT::zvmUtils->printLn( $callback, "$out");
        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
            # The error would have already been printed under mkvm
            `rm -rf $userEntryFile`;
            return;
        }
        # If one of the disk operations are given, add disk(s) to this new user
        if ($diskPool || $diskSize) {
            if (!$diskPool || !$diskSize) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more required parameter(s) for adding disk" );
                `rm -rf $userEntryFile`;
                return;
            }

            # Default disk virtual device to 0100 if none is given
            if (!$diskVdev) {
                $diskVdev = "0100";
            }

            $out = `/opt/xcat/bin/chvm $node --add3390 $diskPool $diskVdev $diskSize 2>&1`;
            xCAT::zvmUtils->printLn( $callback, "$out");
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                # The error would have already been printed under chvm
                `rm -rf $userEntryFile`;
                return;
            }
        }

        # Remove the temporary file
        $out = `rm -f $userEntryFile`;
        return;
    }

    # Get user entry file (if any)
    my $userEntry;
    if (!$stdin) {
        $userEntry = $args->[0];
    }

    # Get MAC address in 'mac' table
    my $macId;
    my $generateNew = 1;
    @propNames = ('mac');
    $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames );
    # If MAC address exists
    my @lines;
    my @words;
    if ( $propVals->{'mac'} ) {

        # Get MAC suffix (MACID)
        $macId = $propVals->{'mac'};
        $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" );
        $macId = substr( $macId, 6 );
    } else {
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`;

        # Get USER Prefix
        my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q vmlan\"", $hcp, "makeVM", $out, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        my $prefix = `echo "$out" | egrep -a -i "USER Prefix:"`;
        $prefix =~ s/(.*?)USER Prefix:(.*)/$2/;
        $prefix =~ s/^\s+//;
        $prefix =~ s/\s+$//;

        # Get MACADDR Prefix instead if USER Prefix is not defined
        if (!$prefix) {
            $prefix = `echo "$out" | egrep -a -i "MACADDR Prefix:"`;
            $prefix =~ s/(.*?)MACADDR Prefix:(.*)/$2/;
            $prefix =~ s/^\s+//;
            $prefix =~ s/\s+$//;

            if (!$prefix) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not find the MACADDR/USER prefix of the z/VM system" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the node's zHCP($hcp) is correct, the node is online, and the SSH keys are setup for the zHCP" );
                return;
            }
        }
        # Generate MAC address
        my $mac;
        while ($generateNew) {

            # If no MACID is found, get one
            $macId = xCAT::zvmUtils->getMacID($::SUDOER, $hcp);
            if ( !$macId ) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not generate MACID" );
                return;
            }

            # Create MAC address
            $mac = $prefix . $macId;

            # If length is less than 12, append a zero
            if ( length($mac) != 12 ) {
                $mac = "0" . $mac;
            }

            # Format MAC address
            $mac =
                substr( $mac, 0, 2 ) . ":"
              . substr( $mac, 2,  2 ) . ":"
              . substr( $mac, 4,  2 ) . ":"
              . substr( $mac, 6,  2 ) . ":"
              . substr( $mac, 8,  2 ) . ":"
              . substr( $mac, 10, 2 );

            # Check 'mac' table for MAC address
            my $tab = xCAT::Table->new( 'mac', -create => 1, -autocommit => 0 );
            my @entries = $tab->getAllAttribsWhere( "mac = '" . $mac . "'", 'node' );

            # If MAC address exists
            if (@entries) {
                # Generate new MACID
                $out = xCAT::zvmUtils->generateMacId($::SUDOER, $hcp);
                $generateNew = 1;
            } else {
                $generateNew = 0;

                # Save MAC address in 'mac' table
                xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac );

                # Generate new MACID
                $out = xCAT::zvmUtils->generateMacId($::SUDOER, $hcp);
            }
        } # End of while ($generateNew)
    }

    # Create virtual server
    my $line;
    my @hcpNets;
    my $netName = '';
    my $oldNicDef;
    my $nicDef;
    my $id;
    my @vswId;
    my $target = "$::SUDOER\@$hcp";
    if ($userEntry) {
        # Copy user entry
        $out = `cp $userEntry /tmp/$node.txt`;
        $userEntry = "/tmp/$node.txt";

        # If the directory entry contains a NICDEF statement, append MACID to the end
        # User must select the right one (layer) based on template chosen
        $out = `cat $userEntry | egrep -a -i "NICDEF"`;
        if ($out) {

            # Get the networks used by the zHCP
            @hcpNets = xCAT::zvmCPUtils->getNetworkNamesArray($::SUDOER, $hcp);

            # Search user entry for network name
            foreach (@hcpNets) {
                if ( $out =~ m/ $_/i ) {
                    $netName = $_;
                    last;
                }
            }

            # Find NICDEF statement
            $oldNicDef = `cat $userEntry | egrep -a -i "NICDEF" | egrep -a -i "$netName"`;
            if ($oldNicDef) {
                $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef);
                $nicDef = xCAT::zvmUtils->replaceStr($oldNicDef, $netName, "$netName MACID $macId");

                # Append MACID at the end
                $out = `sed -i -e "s,$oldNicDef,$nicDef,i" $userEntry`;
            }
        }
        # Open user entry
        $out = `cat $userEntry`;
        @lines = split( '\n', $out );

        # Get the userID in user entry
        $line = xCAT::zvmUtils->trimStr( $lines[0] );
        @words = split( ' ', $line );
        $id = $words[1];

        # Change userID in user entry to match userID defined in xCAT
        $out = `sed -i -e "s,$id,$userId,i" $userEntry`;

        # SCP file over to zHCP
        $out = `scp $userEntry $target:$userEntry`;
        # Create virtual server
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Create_DM -T $userId -f $userEntry"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Create_DM -T $userId -f $userEntry");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        # Check output
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ( $rc == 0 ) {

            # Get VSwitch of zHCP (if any)
            @vswId = xCAT::zvmCPUtils->getVswitchId($::SUDOER, $hcp);

            # Is there an internal vswitch for xcat and zhcp? If so do not grant to that.
            my $internalVswitch = '-';
            if (open my $input_fh, "</opt/xcat/internalVswitch") {
               $internalVswitch = <$input_fh>; # read first line, should be just one token
               close $input_fh;
               chomp($internalVswitch);
            }

            # Grant access to VSwitch for Linux user
            # GuestLan do not need permissions
            # skip any duplicates
            my %vswitchhash;
            foreach (@vswId) {
               if (!(length $_)) {next;}
               # skip grant if we already did one for this vswitch
               if (exists $vswitchhash{$_}) {
                  xCAT::zvmUtils->printSyslog("makeVM. Skipping duplicate vswitch grant from: $_");
               }
               else {
                  if ($_ ne $internalVswitch) {
                     xCAT::zvmUtils->printSyslog("makeVM. Found vswitch to grant: $_");
                     $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $_ ,'', ''); # Don't have porttype or vlan
                     xCAT::zvmUtils->printLn( $callback, "$node: Granting VSwitch ($_) access for $userId... $out" );
                     $vswitchhash{$_} = '1';
                  } else {
                     xCAT::zvmUtils->printSyslog("makeVM. Skipping grant for internal vswitch: $_");
                  }
               }
            }

            # Remove user entry file (on zHCP)
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $userEntry"`;
        }

        # Remove user entry on xCAT
        $out = `rm -rf $userEntry`;
    } elsif ($stdin) {
        # Take directory entry from stdin
        $stdin = $::STDIN;

        # If the directory entry contains a NICDEF statement, append MACID to the end
        # User must select the right one (layer) based on template chosen
        $out = `echo -e "$stdin" | egrep -a -i "NICDEF"`;
        if ($out) {
            # Get the networks used by the zHCP
            @hcpNets = xCAT::zvmCPUtils->getNetworkNamesArray($::SUDOER, $hcp);

            # Search user entry for network name
            $netName = '';
            foreach (@hcpNets) {
                if ( $out =~ m/ $_/i ) {
                    $netName = $_;
                    last;
                }
            }

            # Find NICDEF statement
            $oldNicDef = `echo -e "$stdin" | egrep -a -i "NICDEF" | egrep -a -i "$netName"`;
            if ($oldNicDef) {
                $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef);

                # Append MACID at the end
                $nicDef = xCAT::zvmUtils->replaceStr( $oldNicDef, $netName, "$netName MACID $macId" );
                # Update stdin
                $stdin =~ s/$oldNicDef/$nicDef/g;
            }
        }

        # Create a temporary file to contain directory on zHCP
        my $file = "/tmp/" . $node . ".direct";
        @lines = split("\n", $stdin);

        # Delete existing file on zHCP (if any)
        `ssh $::SUDOER\@$hcp "rm -rf $file"`;

        # Write directory entry into temporary file
        # because directory entry cannot be remotely echoed into stdin
        foreach (@lines) {
            if (!(length $_)) {next;}
            if ($_) {
                $_ = "'" . $_ . "'";
                `ssh $::SUDOER\@$hcp "echo $_ >> $file"`;
            }
        }

        # Create virtual server
        $out = `ssh $::SUDOER\@$hcp "cat $file | $::SUDO $::DIR/smcli Image_Create_DM -T $userId -s"`;
        xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp cat $file | $::SUDO $::DIR/smcli Image_Create_DM -T $userId -s");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );

        # Check output
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ( $rc == 0 ) {

            # Get VSwitch of zHCP (if any)
            @vswId = xCAT::zvmCPUtils->getVswitchId($::SUDOER, $hcp);

            # Grant access to VSwitch for Linux user
            # GuestLan do not need permissions
            foreach (@vswId) {
                $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $_, '', '');
                xCAT::zvmUtils->printLn( $callback, "$node: Granting VSwitch ($_) access for $userId... $out" );
            }

            # Delete created file on zHCP
            `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "rm -rf $file"`;
        }
    } else {

        # Create NOLOG virtual server
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/createvs $userId"`;
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
    }

    return;
}

#-------------------------------------------------------

=head3   cloneVM

    Description : Clone a virtual server
    Arguments   :   Node
                    Disk pool
                    Disk password
                    clone info hash, can be empty
    Returns     : Nothing
    Example     : cloneVM($callback, $targetNode, $args);

=cut

#-------------------------------------------------------
sub cloneVM {

    # Get inputs
    my ( $callback, $nodes, $args ) = @_;

    # Get nodes
    my @nodes = @$nodes;

    # Return code for each command
    my $rc;
    my $out;

    # Child process IDs
    my @children;

    # Process ID for xfork()
    my $pid;

    # Get source node
    my $sourceNode = $args->[0];
    my @propNames  = ( 'hcp', 'userid' );
    my $propVals   = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames );

    # Get zHCP
    my $srcHcp = $propVals->{'hcp'};

    # Get node user ID
    my $sourceId = $propVals->{'userid'};
    # Capitalize user ID
    $sourceId =~ tr/a-z/A-Z/;

    # Get operating system, e.g. sles11sp2 or rhel6.2
    @propNames = ( 'os' );
    $propVals = xCAT::zvmUtils->getNodeProps( 'nodetype', $sourceNode, @propNames );
    my $srcOs = $propVals->{'os'};

    # Set IP address
    my $sourceIp = xCAT::zvmUtils->getIp($sourceNode);

    my @dedicates = xCAT::zvmUtils->getDedicates( $callback, $::SUDOER, $sourceNode );
    if (xCAT::zvmUtils->checkOutput( $dedicates[0] ) == -1) {
        xCAT::zvmUtils->printLn( $callback, "$dedicates[0]" );
        return;
    }
    if (scalar(@dedicates)) {
        xCAT::zvmUtils->printLn( $callback, "$sourceNode: (Error) Dedicate statements found in source directory." );
        return;
    }

    # Get networks in 'networks' table
    my $netEntries = xCAT::zvmUtils->getAllTabEntries('networks');
    my $srcNetwork = "";
    my $srcMask;
    foreach (@$netEntries) {
        # Get source network and mask
        $srcNetwork = $_->{'net'};
        $srcMask = $_->{'mask'};

        # If the host IP address is in this subnet, return
        if (xCAT::NetworkUtils->ishostinsubnet($sourceIp, $srcMask, $srcNetwork)) {

            # Exit loop
            last;
        } else {
            $srcNetwork = "";
        }
    }

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$srcHcp sudo:$::SUDO");
    xCAT::zvmUtils->printSyslog("srcHcp:$srcHcp sourceId:$sourceId srcOs:$srcOs srcNetwork:$srcNetwork srcMask:$srcMask");
    xCAT::zvmUtils->printSyslog("nodes:@nodes");

    foreach (@nodes) {
        xCAT::zvmUtils->printLn( $callback, "$_: Cloning $sourceNode" );

        # Exit if missing source node
        if ( !$sourceNode ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node" );
            return;
        }

        # Exit if missing source HCP
        if ( !$srcHcp ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node HCP" );
            return;
        }

        # Exit if missing source user ID
        if ( !$sourceId ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source user ID" );
            return;
        }

        # Exit if missing source operating system
        if ( !$srcOs ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source operating system" );
            return;
        }

        # Exit if missing source operating system
        if ( !$sourceIp || !$srcNetwork || !$srcMask ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source IP, network, or mask" );
            return;
        }

        # Get target node
        @propNames = ( 'hcp', 'userid' );
        $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames );

        # Get target HCP
        my $tgtHcp = $propVals->{'hcp'};

        # Get node userID
        my $tgtId = $propVals->{'userid'};
        # Capitalize userID
        $tgtId =~ tr/a-z/A-Z/;

        # Exit if missing target zHCP
        if ( !$tgtHcp ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing target node HCP" );
            return;
        }

        # Exit if missing target user ID
        if ( !$tgtId ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing target user ID" );
            return;
        }

        # Exit if source and target zHCP are not equal
        if ( $srcHcp ne $tgtHcp ) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) Source and target HCP are not equal" );
            xCAT::zvmUtils->printLn( $callback, "$_: (Solution) Set the source and target HCP appropriately in the zvm table" );
            return;
        }

        #*** Get MAC address ***
        my $targetMac;
        my $macId;
        my $generateNew = 0;    # Flag to generate new MACID
        @propNames = ('mac');
        $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $_, @propNames );
        if ( !$propVals->{'mac'} ) {

            # If no MACID is found, get one
            $macId = xCAT::zvmUtils->getMacID($::SUDOER, $tgtHcp);
            if ( !$macId ) {
                xCAT::zvmUtils->printLn( $callback, "$_: (Error) Could not generate MACID" );
                return;
            }

            # Create MAC address (target)
            $targetMac = xCAT::zvmUtils->createMacAddr( $::SUDOER, $_, $macId );
            if (xCAT::zvmUtils->checkOutput( $targetMac ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$targetMac" );
                return;
            }

            # Save MAC address in 'mac' table
            xCAT::zvmUtils->setNodeProp( 'mac', $_, 'mac', $targetMac );

            # Generate new MACID
            $out = xCAT::zvmUtils->generateMacId($::SUDOER, $tgtHcp);
        }

        xCAT::zvmUtils->printSyslog("tgtHcp:$tgtHcp tgtId:$tgtId targetMac:$targetMac macId:$macId");
    }

    #*** Link source disks ***
    # Get MDisk statements of source node
    my @words;
    my $addr;
    my $type;
    my $linkAddr;
    my $i;

    # Hash table of source disk addresses
    # $srcLinkAddr[$addr] = $linkAddr
    my %srcLinkAddr;
    my %srcDiskSize;

    # Hash table of source disk type
    # $srcLinkAddr[$addr] = $type
    my %srcDiskType;
    my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $sourceNode );
    if (xCAT::zvmUtils->checkOutput( $srcDisks[0] ) == -1) {
        xCAT::zvmUtils->printLn( $callback, "$srcDisks[0]" );
        return;
    }

    # Get details about source disks
    # Output is similar to:
    #   MDISK=VDEV=0100 DEVTYPE=3390 START=0001 COUNT=10016 VOLID=EMC2C4 MODE=MR
    $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $sourceId -k MDISK"`;
    xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $sourceId -k MDISK");
    xCAT::zvmUtils->printSyslog("$out");
    xCAT::zvmUtils->printSyslog("srcDisks:@srcDisks");
    my $srcDiskDet = xCAT::zvmUtils->trimStr($out);
    foreach (@srcDisks) {

        # Get disk address
        @words      = split( ' ', $_ );
        $addr       = $words[1];
        $type       = $words[2];

        # Add 0 in front if address length is less than 4
        while (length($addr) < 4) {
            $addr = '0' . $addr;
        }

        # Get disk type
        $srcDiskType{$addr} = $type;

        # Get disk size (cylinders or blocks)
        # ECKD or FBA disk
        if ( $type eq '3390' || $type eq '9336' ) {
            my @lines = split( '\n', $srcDiskDet );

            # Loop through each line
            for ( $i = 0 ; $i < @lines ; $i++ ) {
                # remove the MDISK= from the line
                $lines[$i] =~ s/MDISK=//g;

                # Extract vdev address
                # search for = signs, capture what is after = but not whitespace
                @words = ($lines[$i] =~ m/=(\S+)/g);
                my $srcDiskAddr = $words[0];
                if ($srcDiskAddr eq $addr) {
                   $srcDiskSize{$srcDiskAddr} = $words[3];
                   xCAT::zvmUtils->printSyslog("addr:$addr type:$type srcDiskAddr:$srcDiskAddr srcDiskSize:$words[3]");
                }
            }
        }

        # If source disk is not linked
        my $try = 5;
        while ( $try > 0 ) {

            # New disk address
            $linkAddr = $addr + 1000;

            # Check if new disk address is used (source)
            $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $srcHcp, $linkAddr );

            # If disk address is used (source)
            while ( $rc == 0 ) {

                # Generate a new disk address
                # Sleep 5 seconds to let existing disk appear
                sleep(5);
                $linkAddr = $linkAddr + 1;
                $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $srcHcp, $linkAddr );
            }

            $srcLinkAddr{$addr} = $linkAddr;

            # Link source disk to HCP
            foreach (@nodes) {
                xCAT::zvmUtils->printLn( $callback, "$_: Linking source disk ($addr) as ($linkAddr)" );
            }
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$srcHcp "$::SUDO /sbin/vmcp link $sourceId $addr $linkAddr RR"`;

            if ( $out =~ m/not linked/i ) {
                # Do nothing
            } else {
                last;
            }

            $try = $try - 1;

            # Wait before next try
            sleep(5);
        } # End of while ( $try > 0 )

        # If source disk is not linked
        if ( $out =~ m/not linked/i ) {
            foreach (@nodes) {
                xCAT::zvmUtils->printLn( $callback, "$_: Failed" );
            }

            # Exit
            return;
        }

        # Enable source disk
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $srcHcp, "-e", $linkAddr );
    } # End of foreach (@srcDisks)

    # Get the networks the HCP is on
    my @hcpNets = xCAT::zvmCPUtils->getNetworkNamesArray($::SUDOER, $srcHcp);

    # Get the NICDEF address of the network on the source node
    my @tmp;
    my $srcNicAddr = '';
    my $hcpNetName = '';

    # Find the NIC address
    xCAT::zvmCPUtils->loadVmcp($::SUDOER, $sourceNode);
    $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $sourceId -k NICDEF"`;
    xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $sourceId -k NICDEF");
    xCAT::zvmUtils->printSyslog("$out");
    # Output is similar to:
    #   NICDEF_PROFILE=VDEV=0800 TYPE=QDIO LAN=SYSTEM SWITCHNAME=VSW2
    #   NICDEF=VDEV=0900 TYPE=QDIO DEVICES=3 LAN=SYSTEM SWITCHNAME=GLAN1
    #   NICDEF=VDEV=0A00 TYPE=QDIO DEVICES=3 LAN=SYSTEM SWITCHNAME=VSW2

    my @lines = split( '\n', $out );

    # Loop through each line
    my $line;
    for ( $i = 0 ; $i < @lines ; $i++ ) {
        # Loop through each network name
        foreach (@hcpNets) {
            # If the network is found
            if ( $lines[$i] =~ m/SWITCHNAME=$_/i ) {
                # Save network name
                $hcpNetName = $_;

                $lines[$i] =~ s/NICDEF_PROFILE=//g;
                $lines[$i] =~ s/NICDEF=//g;

                # Extract NIC address
                @words = ($lines[$i] =~ m/=(\S+)/g);
                $srcNicAddr = $words[0];
                xCAT::zvmUtils->printSyslog("hcpNetName:$hcpNetName srcNicAddr:$srcNicAddr");

                # Grab only the 1st match
                last;
            }
        }
    }

    # If no network name is found, exit
    if (!$hcpNetName || !$srcNicAddr) {
        #*** Detatch source disks ***
        for $addr ( keys %srcLinkAddr ) {
            $linkAddr = $srcLinkAddr{$addr};

            # Disable and detatch source disk
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $srcHcp, "-d", $linkAddr );
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$srcHcp "$::SUDO /sbin/vmcp det $linkAddr"`;
            foreach (@nodes) {
                xCAT::zvmUtils->printLn( $callback, "$_: Detatching source disk ($addr) at ($linkAddr)" );
            }
        }

        foreach (@nodes) {
            xCAT::zvmUtils->printLn( $callback, "$_: (Error) No suitable network device found in user directory entry" );
            xCAT::zvmUtils->printLn( $callback, "$_: (Solution) Verify that the node has one of the following network devices: @hcpNets" );
        }

        return;
    }

    # Get vSwitch of source node (if any)
    my @srcVswitch = xCAT::zvmCPUtils->getVswitchId($::SUDOER, $srcHcp);

    # Get source MAC address in 'mac' table
    my $srcMac;
    @propNames = ('mac');
    $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $sourceNode, @propNames );
    if ( $propVals->{'mac'} ) {

        # Get MAC address
        $srcMac = $propVals->{'mac'};
    }

    # Get user entry of source node without any mdisk statements
    my $srcUserEntry = "/tmp/$sourceNode.txt";
    $out = `rm $srcUserEntry`;
    $out = xCAT::zvmUtils->getUserEntryWODisk( $callback, $::SUDOER, $sourceNode, $srcUserEntry );
    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
        xCAT::zvmUtils->printLn( $callback, "$out" );
        return;
    }

    # Check if user entry is valid
    $out = `cat $srcUserEntry`;

    # If output contains USER LINUX123, then user entry is good
    if ( $out =~ m/USER $sourceId/i ) {

        # Turn off source node
        my $ping = xCAT::zvmUtils->pingNode($sourceNode);
        if ($ping eq "ping") {
            $out = `ssh -o ConnectTimeout=10 $sourceNode "shutdown -h now"`;
            sleep(90);    # Wait 1.5 minutes before logging user off

            foreach (@nodes) {
                xCAT::zvmUtils->printLn( $callback, "$_: Shutting down $sourceNode" );
            }
        }

        xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $sourceId");
        $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Deactivate -T $sourceId"`;
        $rc = $? >> 8;
        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $srcHcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $srcHcp" );
            return;
        }
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ($out =~ m/Return Code: 200/i){
            if ($out =~ m/Reason Code: 12/i) {
                $out = "$sourceId already logged off.";
                $rc = 0;
            } elsif ($out =~ m/Reason Code: 16/i) {
                $out = "$sourceId in process of logging off.";
                $rc = 0;
            }
        }
        if ( $rc == -1 ) {
                xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $sourceId output: $out" );
                xCAT::zvmUtils->printLn( $callback, "$out");
                return;
        }
        xCAT::zvmUtils->printSyslog("$out");

        #*** Clone source node ***
        # Remove flashcopy lock (if any)
        $out = `ssh $::SUDOER\@$srcHcp "$::SUDO rm -f /tmp/.flashcopy_lock"`;
        foreach (@nodes) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process
            elsif ( $pid == 0 ) {
                clone(
                    $callback, $_, $args, \@srcDisks, \%srcLinkAddr, \%srcDiskSize, \%srcDiskType,
                    $srcNicAddr, $hcpNetName, \@srcVswitch, $srcOs, $srcMac, $netEntries, $sourceIp, $srcNetwork, $srcMask
                );

                # Exit process
                exit(0);
            }

            # End of elsif
            else {
                # Ran out of resources
                die "Error: Could not fork\n";
            }

            # Clone 4 nodes at a time
            # If you handle more than this, some nodes will not be cloned
            # You will get errors because SMAPI cannot handle many nodes
            if ( !( @children % 4 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }    # End of foreach

        # Handle the remaining nodes
        # Wait for all processes to end
        foreach (@children) {
            waitpid( $_, 0 );
        }

        # Remove source user entry
        $out = `rm $srcUserEntry`;
    }    # End of if

    #*** Detatch source disks ***
    for $addr ( keys %srcLinkAddr ) {
        $linkAddr = $srcLinkAddr{$addr};

        # Disable and detatch source disk
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $srcHcp, "-d", $linkAddr );
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$srcHcp "$::SUDO /sbin/vmcp det $linkAddr"`;

        foreach (@nodes) {
            xCAT::zvmUtils->printLn( $callback, "$_: Detatching source disk ($addr) at ($linkAddr)" );
        }
    }

    #*** Done ***
    foreach (@nodes) {
        xCAT::zvmUtils->printLn( $callback, "$_: Done" );
    }

    return;
}

#-------------------------------------------------------

=head3   clone

    Description : Clone a virtual server
    Arguments   :   Target node
                    Disk pool
                    Disk password (optional)
                    Source disks
                    Source disk link addresses
                    Source disk sizes
                    NIC address
                    Network name
                    VSwitch names (if any)
                    Operating system
                    MAC address
                    Root parition device address
                    Path to network configuration file
                    Path to hardware configuration file (SUSE only)
    Returns     : Nothing, errors returned in $callback
    Example     : clone($callback, $_, $args, \@srcDisks, \%srcLinkAddr, \%srcDiskSize,
                    $srcNicAddr, $hcpNetName, \@srcVswitch, $srcOs, $srcMac, $netEntries,
                    $sourceIp, $srcNetwork, $srcMask);

=cut

#-------------------------------------------------------
sub clone {

    # Get inputs
    my (
        $callback, $tgtNode, $args, $srcDisksRef, $srcLinkAddrRef, $srcDiskSizeRef, $srcDiskTypeRef,
        $srcNicAddr, $hcpNetName, $srcVswitchRef, $srcOs, $srcMac, $netEntries, $sourceIp, $srcNetwork, $srcMask
      )
      = @_;

    # Get source node properties from 'zvm' table
    my $sourceNode = $args->[0];
    my @propNames  = ( 'hcp', 'userid' );
    my $propVals   = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames );

    # Get zHCP
    my $srcHcp = $propVals->{'hcp'};

    # Get node user ID
    my $sourceId = $propVals->{'userid'};
    # Capitalize user ID
    $sourceId =~ tr/a-z/A-Z/;

    # Get source disks
    my @srcDisks    = @$srcDisksRef;
    my %srcLinkAddr = %$srcLinkAddrRef;
    my %srcDiskSize = %$srcDiskSizeRef;
    my %srcDiskType = %$srcDiskTypeRef;
    my @srcVswitch  = @$srcVswitchRef;

    # Return code for each command
    my $rc;

    # Get node properties from 'zvm' table
    @propNames = ( 'hcp', 'userid' );
    $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node HCP" );
        return;
    }

    # Get zHCP user ID
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp);
    if ( !$hcpUserId ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing zHCP user ID" );
        return;
    }
    # Capitalize user ID
    $hcpUserId =~ tr/a-z/A-Z/;

    # Get node user ID
    my $tgtUserId = $propVals->{'userid'};
    if ( !$tgtUserId ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $tgtUserId =~ tr/a-z/A-Z/;

    # Exit if source node HCP is not the same as target node HCP
    if ( !( $srcHcp eq $hcp ) ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Source node HCP ($srcHcp) is not the same as target node HCP ($hcp)" );
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Set the source and target HCP appropriately in the zvm table" );
        return;
    }

    # Get target IP from /etc/hosts
    `makehosts`;
    sleep(5);
    my $targetIp = xCAT::zvmUtils->getIp($tgtNode);
    if ( !$targetIp ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing IP for $tgtNode in /etc/hosts" );
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify that the node's IP address is specified in the hosts table and then run makehosts" );
        return;
    }
    xCAT::zvmUtils->printSyslog("hcp:$hcp tgtUserId:$tgtUserId targetIp:$targetIp");

    my $out;
    my $outmsg;
    my @lines;
    my @words;

    # Get disk pool and multi password
    my $i;
    my %inputs;
    foreach $i ( 1 .. 2 ) {
        if ( $args->[$i] ) {

            # Split parameters by '='
            @words = split( "=", $args->[$i] );

            # Create hash array
            $inputs{ $words[0] } = $words[1];
        }
    }

    # Get disk pool
    my $pool = $inputs{"pool"};
    if ( !$pool ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing disk pool. Please specify one." );
        return;
    }
    xCAT::zvmUtils->printSyslog("pool:$pool");

    # Get multi password
    # It is Ok not have a password
    my $tgtPw = "''";
    if ($inputs{"pw"}) {
        $tgtPw = $inputs{"pw"};
    }

    # Save user directory entry as /tmp/hostname.txt, e.g. /tmp/gpok3.txt
    # The source user entry is retrieved in cloneVM()
    my $userEntry    = "/tmp/$tgtNode.txt";
    my $srcUserEntry = "/tmp/$sourceNode.txt";

    # Remove existing user entry if any
    $out = `rm $userEntry`;
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $userEntry"`;

    # Copy user entry of source node
    $out = `cp $srcUserEntry $userEntry`;

    # Replace source userID with target userID
    $out = `sed -i -e "s,$sourceId,$tgtUserId,i" $userEntry`;

    # Get target MAC address in 'mac' table
    my $targetMac;
    my $macId;
    my $generateNew = 0;    # Flag to generate new MACID
    @propNames = ('mac');
    $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $tgtNode, @propNames );
    if ($propVals) {

        # Get MACID
        $targetMac = $propVals->{'mac'};
        $macId     = $propVals->{'mac'};
        $macId     = xCAT::zvmUtils->replaceStr( $macId, ":", "" );
        $macId     = substr( $macId, 6 );
    } else {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target MAC address" );
        return;
    }

    # If the user entry contains a NICDEF statement
    $out = `cat $userEntry | egrep -a -i "NICDEF"`;
    if ($out) {

        # Get the networks used by the zHCP
        my @hcpNets = xCAT::zvmCPUtils->getNetworkNamesArray($::SUDOER, $hcp);

        # Search user entry for network name
        my $hcpNetName = '';
        foreach (@hcpNets) {
            if ( $out =~ m/ $_/i ) {
                $hcpNetName = $_;
                last;
            }
        }

        # If the user entry contains a MACID
        $out = `cat $userEntry | egrep -a -i "MACID"`;
        if ($out) {
            my $pos = rindex( $out, "MACID" );
            my $oldMacId = substr( $out, $pos + 6, 12 );
            $oldMacId = xCAT::zvmUtils->trimStr($oldMacId);

            # Replace old MACID
            $out = `sed -i -e "s,$oldMacId,$macId,i" $userEntry`;
        } else {

            # Find NICDEF statement
            my $oldNicDef = `cat $userEntry | egrep -a -i "NICDEF" | egrep -a -i "$hcpNetName"`;
            $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef);
            my $nicDef = xCAT::zvmUtils->replaceStr( $oldNicDef, $hcpNetName, "$hcpNetName MACID $macId" );

            # Append MACID at the end
            $out = `sed -i -e "s,$oldNicDef,$nicDef,i" $userEntry`;
        }
    }

    # SCP user entry file over to HCP
    xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $userEntry, $userEntry );

    #*** Create new virtual server ***
    my $try = 5;
    while ( $try > 0 ) {
        if ( $try > 4 ) {
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" );
        } else {
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to create user directory entry" );
        }
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Create_DM -T $tgtUserId -f $userEntry"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Create_DM -T $tgtUserId -f $userEntry");
        xCAT::zvmUtils->printSyslog("$out");

        # Check if user entry is created
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId" | sed '\$d'`;
        xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId | sed '\$d'");
        xCAT::zvmUtils->printSyslog("$out");
        $rc  = xCAT::zvmUtils->checkOutput( $out );

        if ( $rc == -1 ) {

            # Wait before trying again
            sleep(5);

            $try = $try - 1;
        } else {
            last;
        }
    }

    # Remove user entry
    $out = `rm $userEntry`;
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $userEntry"`;

    # Exit on bad output
    if ( $rc == -1 ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry" );
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify that the node's zHCP and its zVM's SMAPI are both online" );
        return;
    }

    # Load VMCP module on HCP and source node
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`;

    # Grant access to VSwitch for Linux user
    # GuestLan do not need permissions
    my %vswitchhash;
    foreach (@srcVswitch) {
       # skip grant if we already did one for this vswitch
       if (exists $vswitchhash{$_}) {
          xCAT::zvmUtils->printSyslog("clone. Skipping duplicate vswitch grant from: $_");
       }
       else {
          xCAT::zvmUtils->printLn( $callback, "$tgtNode: Granting VSwitch ($_) access for $tgtUserId" );
          # If this is one of our recent provisions the directory of the source should contain any vlan id grants also
          $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $tgtUserId, $_, '', '' );

          # Check for errors
          $rc = xCAT::zvmUtils->checkOutput( $out );
          if ( $rc == -1 ) {

              # Exit on bad output
              xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );
              return;
          }
          $vswitchhash{$_} = '1';
       }
    }    # End of foreach (@vswitchId)

    #*** Add MDisk to target user entry ***
    my $addr;
    my @tgtDisks;
    my $type;
    my $mode;
    my $cyl;
    foreach (@srcDisks) {

        # Get disk address
        @words = split( ' ', $_ );
        $addr = $words[1];
        push( @tgtDisks, $addr );
        $type       = $words[2];
        $mode       = $words[6];
        if (!$mode) {
            $mode = "MR";
        }

        # Add 0 in front if address length is less than 4
        while (length($addr) < 4) {
            $addr = '0' . $addr;
        }

        # Add ECKD disk
        if ( $type eq '3390' ) {

            # Get disk size (cylinders)
            $cyl = $srcDiskSize{$addr};

            $try = 5;
            while ( $try > 0 ) {

                # Add ECKD disk
                if ( $try > 4 ) {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" );
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" );
                }
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw");
                xCAT::zvmUtils->printSyslog("$out");
                xCAT::zvmUtils->printLn( $callback, "$out" );

                # Check output
                $rc = xCAT::zvmUtils->checkOutput( $out );
                if ( $rc == -1 ) {

                    # Wait before trying again
                    sleep(5);

                    # One less try
                    $try = $try - 1;
                } else {

                    # If output is good, exit loop
                    last;
                }
            }    # End of while ( $try > 0 )

            # Exit on bad output
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" );
                return;
            }
        }    # End of if ( $type eq '3390' )

        # Add FBA disk
        elsif ( $type eq '9336' ) {

            # Get disk size (blocks)
            my $blkSize = '512';
            my $blks    = $srcDiskSize{$addr};

            $try = 10;
            while ( $try > 0 ) {

                # Add FBA disk
                if ( $try > 9 ) {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" );
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" );
                }
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $pool -u 1 -z $blks -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $pool -u 1 -z $blks -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw");
                xCAT::zvmUtils->printSyslog("$out");
                xCAT::zvmUtils->printLn( $callback, "$out" );

                # Check output
                $rc = xCAT::zvmUtils->checkOutput( $out );
                if ( $rc == -1 ) {

                    # Wait before trying again
                    sleep(5);

                    # One less try
                    $try = $try - 1;
                } else {

                    # If output is good, exit loop
                    last;
                }
            }    # End of while ( $try > 0 )

            # Exit on bad output
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" );
                return;
            }
        }    # End of elsif ( $type eq '9336' )
    }

    # Check if the number of disks in target user entry
    # is equal to the number of disks added
    my @disks;
    $try = 10;
    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks added (@tgtDisks). Checking directory for those disks..." );
    while ( $try > 0 ) {

        # Get disks within user entry
        xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId\"", $hcp, "clone", $out, $tgtNode );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $out = `echo "$out" | sed '\$d' | grep -a -i "MDISK"`;
        xCAT::zvmUtils->printSyslog("$out");
        @disks = split( '\n', $out );

        if ( @disks != @tgtDisks ) {
            $try = $try - 1;

            # Wait before trying again
            sleep(5);
        } else {
            last;
        }
    }

    # Exit if all disks are not present
    if ( @disks != @tgtDisks ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." );
        xCAT::zvmUtils->printSyslog( "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." );

        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks found in $sourceId source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" );
        xCAT::zvmUtils->printSyslog( "$tgtNode: Disks found in $sourceId + source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" );

        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify disk pool($pool) has free disks and that directory updates are working" );
        return;
    }

    #**  * Link, format, and copy source disks ***
    my $srcAddr;
    my $directoryMdiskAddr;
    my $tgtAddr;
    my $srcDevNode;
    my $tgtDevNode;
    my $tgtDiskType;

    foreach (@tgtDisks) {

        # Get disk type (3390 or 9336)
        $tgtDiskType = $srcDiskType{$_};

        #*** Try to use SMAPI flashcopy first if ECKD  ***
        # Otherwise link the target disks and if ECKD, try CP Flashcopy. If
        # CP flashcopy does not work or not ECKD; use Linux DD
        my $ddCopy = 0;
        my $cpFlashcopy = 1;
        my $smapiFlashCopyDone = 0;
        $directoryMdiskAddr = $_;

        if ($tgtDiskType eq '3390') {
            # Try SMAPI FLASHCOPY
            if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($directoryMdiskAddr) to target disk ($directoryMdiskAddr) using FLASHCOPY" );
                xCAT::zvmUtils->printSyslog( "$tgtNode: Doing SMAPI flashcopy source disk ($sourceId $directoryMdiskAddr) to target disk ($tgtUserId $directoryMdiskAddr) using FLASHCOPY" );
                $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $directoryMdiskAddr, $tgtUserId, $directoryMdiskAddr);

                # Exit if flashcopy completed successfully
                # Otherwise, if not built in xCAT, try CP FLASHCOPY
                if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) {
                    xCAT::zvmUtils->printSyslog( "$tgtNode: SMAPI flashcopy done. output:<$out>" );
                    $cpFlashcopy = 0;
                    $smapiFlashCopyDone = 1;

                    # now link the disk in zhcp for further tailoring
                    $try = 10;
                    while ( $try > 0 ) {

                        # Add 0 in front if address length is less than 4
                        while (length($directoryMdiskAddr) < 4) {
                            $directoryMdiskAddr = '0' . $directoryMdiskAddr;
                        }
                        # New disk address
                        $tgtAddr = $directoryMdiskAddr + 2000;

                        # Check if new disk address is used (target)
                        $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr );

                        # If disk address is used (target)
                        while ( $rc == 0 ) {

                            # Generate a new disk address
                            # Sleep 5 seconds to let existing disk appear
                            sleep(5);
                            $tgtAddr = $tgtAddr + 1;
                            $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr );
                        }

                        # Link target disk
                        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($directoryMdiskAddr) as ($tgtAddr)" );
                        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $directoryMdiskAddr $tgtAddr MR $tgtPw"`;

                        # If link fails
                        if ( $out =~ m/not linked/i || $out =~ m/not write-enabled/i ) {

                            # Wait before trying again
                            sleep(5);

                            $try = $try - 1;
                        } else {
                            last;
                        }
                    }    # End of while ( $try > 0 )

                    # If target disk is not linked
                    if ( $out =~ m/not linked/i ) {
                        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($directoryMdiskAddr)" );
                        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" );

                        # Exit
                        return;
                    }

                } else {
                    xCAT::zvmUtils->printLn( $callback, "$out" );
                    xCAT::zvmUtils->printSyslog("$tgtNode: SMAPI Flashcopy error, trying CP Flashcopy. SMAPI output: $out" );
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: SMAPI Flashcopy error, trying CP Flashcopy." );
                }
            }
        }

        # If SMAPI flashcopy did not work or this is not an ECKD, then link the target disks in write mode
        if ( !$smapiFlashCopyDone ) {

           #*** Link target disk ***
           $try = 10;
           while ( $try > 0 ) {

               # Add 0 in front if address length is less than 4
               while (length($_) < 4) {
                   $_ = '0' . $_;
               }
               # New disk address
               $srcAddr = $srcLinkAddr{$_};
               $tgtAddr = $_ + 2000;

               # Check if new disk address is used (target)
               $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr );

               # If disk address is used (target)
               while ( $rc == 0 ) {

                   # Generate a new disk address
                   # Sleep 5 seconds to let existing disk appear
                   sleep(5);
                   $tgtAddr = $tgtAddr + 1;
                   $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr );
               }

               # Link target disk
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($_) as ($tgtAddr)" );
               $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $_ $tgtAddr MR $tgtPw"`;

               # If link fails
               if ( $out =~ m/not linked/i || $out =~ m/not write-enabled/i ) {

                   # Wait before trying again
                   sleep(5);

                   $try = $try - 1;
               } else {
                   last;
               }
           }    # End of while ( $try > 0 )

           # If target disk is not linked
           if ( $out =~ m/not linked/i ) {
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($_)" );
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" );

               # Exit
               return;
           }
           if ($tgtDiskType eq '3390') {


               # Use CP FLASHCOPY
               if ($cpFlashcopy)  {
                    # Check for CP flashcopy lock
                   my $wait = 0;
                   while ( `ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"` && $wait < 90 ) {

                       # Wait until the lock dissappears
                       # 90 seconds wait limit
                       sleep(2);
                       $wait = $wait + 2;
                   }

                   # If flashcopy locks still exists
                   if (`ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"`) {

                       # Detatch disks from HCP
                       $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;
                       xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Flashcopy lock is enabled" );
                       xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Remove lock by deleting /tmp/.flashcopy_lock on the zHCP. Use caution!" );
                       return;
                   } else {

                       # Enable lock
                       $out = `ssh $::SUDOER\@$hcp "$::SUDO touch /tmp/.flashcopy_lock"`;

                       # Flashcopy source disk
                       $out = xCAT::zvmCPUtils->flashCopy( $::SUDOER, $hcp, $srcAddr, $tgtAddr );
                       xCAT::zvmUtils->printSyslog("flashCopy: $out");
                       $rc = xCAT::zvmUtils->checkOutput( $out );
                       if ( $rc == -1 ) {
                           xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );
                           xCAT::zvmUtils->printSyslog("$tgtNode: CP Flashcopy error,  trying Linux DD next." );

                           # Try Linux dd
                           $ddCopy = 1;
                       }

                       # Wait a while for flashcopy to completely finish
                       sleep(10);

                       # Remove lock
                       $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/.flashcopy_lock"`;
                   }
               }
           } else {
               $ddCopy = 1;
           }

           # Flashcopy not supported, use Linux dd
           if ($ddCopy) {

               #*** Use Linux dd to copy ***
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not working. Using Linux DD" );

               # Enable target disk
               $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr );

               # Determine source device node
               $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcAddr);

               # Determine target device node
               $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr);

               # Format target disk
               # Only ECKD disks need to be formated
               if ($tgtDiskType eq '3390') {
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" );
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`;
                   xCAT::zvmUtils->printSyslog("dasdfmt -b 4096 -y -f /dev/$tgtDevNode");

                   # Check for errors
                   $rc = xCAT::zvmUtils->checkOutput( $out );
                   if ( $rc == -1 ) {
                       xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                       # Detatch disks from HCP
                       $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;

                       return;
                   }

                   # Sleep 2 seconds to let the system settle
                   sleep(2);

                   # Copy source disk to target disk
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" );
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync && $::SUDO echo $?"`;
                   $out = xCAT::zvmUtils->trimStr($out);
                   if (int($out) != 0) {
                       # If $? is not 0 then there was an error during Linux dd
                       $out = "(Error) Failed to copy /dev/$srcDevNode";
                   }

                   xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync");
                   xCAT::zvmUtils->printSyslog("$out");
               } else {
                   # Copy source disk to target disk
                   # Block size = 512
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" );
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync && $::SUDO echo $?"`;
                   $out = xCAT::zvmUtils->trimStr($out);
                   if (int($out) != 0) {
                       # If $? is not 0 then there was an error during Linux dd
                       $out = "(Error) Failed to copy /dev/$srcDevNode";
                   }

                   xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync");
                   xCAT::zvmUtils->printSyslog("$out");

                   # Force Linux to re-read partition table
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Forcing Linux to re-read partition table" );
                   $out =
   `ssh $::SUDOER\@$hcp "$::SUDO cat<<EOM | fdisk /dev/$tgtDevNode
   p
   w
   EOM"`;
               }

               # Check for error
               $rc = xCAT::zvmUtils->checkOutput( $out );
               if ( $rc == -1 ) {
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                   # Disable disks
                   $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr );

                   # Detatch disks from zHCP
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;

                   return;
               }

               # Sleep 2 seconds to let the system settle
               sleep(2);
           } # end if ddcopy
        } # end if SMAPI flashcopy did not complete


        # Disable and enable target disk
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr );
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr );

        # Determine target device node (it might have changed)
        $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr);

        # Mount device and check if it is the root partition
        # If it is, then modify the network configuration

        # Mount target disk
        my $cloneMntPt = "/mnt/$tgtUserId/$tgtDevNode";

        # Disk can contain more than 1 partition. Find the right one (not swap)
        # Check if /usr/bin/file is available
        if (`ssh $::SUDOER\@$hcp "$::SUDO test -f /usr/bin/file && echo Exists"`) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*"`;
            xCAT::zvmUtils->printSyslog("file -s /dev/$tgtDevNode*");
        } else {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*"`;
            xCAT::zvmUtils->printSyslog("fdisk -l /dev/$tgtDevNode*");
        }
        xCAT::zvmUtils->printSyslog("$out");

        $out = "";
        $try = 5;
        while (!$out && $try > 0) {
            # Check if /usr/bin/file is available
            if (`ssh $::SUDOER\@$hcp "$::SUDO test -f /usr/bin/file && echo Exists"`) {
                xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp \"$::SUDO /usr/bin/file -s /dev/$tgtDevNode*\"");
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO /usr/bin/file -s /dev/$tgtDevNode*\"", $hcp, "clone", $out, $tgtDevNode );
                if ($rc != 0) {
                    xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                    return;
                }
            } else {
                xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*\"");
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*\"", $hcp, "clone", $out, $tgtDevNode );
                if ($rc != 0) {
                    xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                    return;
                }
            }
            $out = `echo "$out" | egrep -a -i -v swap | egrep -a -o "$tgtDevNode\[1-9\]"`;
            $out = xCAT::zvmUtils->trimStr($out);
            xCAT::zvmUtils->printSyslog("$out");

            # Wait before trying again
            sleep(5);
            $try = $try - 1;
        }

        my @tgtDevNodes = split( "\n", $out );
        my $iTgtDevNode = 0;
        $tgtDevNode = xCAT::zvmUtils->trimStr($tgtDevNodes[$iTgtDevNode]);

        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Mounting /dev/$tgtDevNode to $cloneMntPt" );
        xCAT::zvmUtils->printSyslog("tgtDevNodes:@tgtDevNodes");

        # Check the disk is mounted
        $try = 5;
        while ( !(`ssh $::SUDOER\@$hcp "$::SUDO ls $cloneMntPt"`) && $try > 0 ) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $cloneMntPt"`;
            $out = `ssh $::SUDOER\@$hcp "$::SUDO mount /dev/$tgtDevNode $cloneMntPt"`;
            xCAT::zvmUtils->printSyslog("mount /dev/$tgtDevNode $cloneMntPt");

            # If more than 1 partition, try other partitions
            if (@tgtDevNodes > 1 && $iTgtDevNode < @tgtDevNodes) {
                $iTgtDevNode++;
                $tgtDevNode = xCAT::zvmUtils->trimStr($tgtDevNodes[$iTgtDevNode]);
            }

            # Wait before trying again
            sleep(10);
            $try = $try - 1;
        }

        if (!(`ssh $::SUDOER\@$hcp "$::SUDO ls $cloneMntPt"`)) {
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed to mount /dev/$tgtDevNode. Skipping device." );
        }

        # Is this the partition containing /etc?
        if (`ssh $::SUDOER\@$hcp "$::SUDO test -d $cloneMntPt/etc && echo Exists"`) {
            #*** Set network configuration ***
            # Set hostname
            xCAT::zvmUtils->printLn( $callback, "$tgtNode: Setting network configuration" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/HOSTNAME"`;
            $rc = $? >> 8;
            if ($rc == 255) { # Adding "Failed" to message will cause zhcp error dialog to be displayed to user
                xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp rc:$?" );
                xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp rc:$?" );
                return;
            }
            xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/HOSTNAME output:$out");

            # If Red Hat - Set hostname in /etc/sysconfig/network
            if ( $srcOs =~ m/rhel/i ) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/sysconfig/network"`;
                xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/sysconfig/network");
            }

            # Get network layer
            my $layer = xCAT::zvmCPUtils->getNetworkLayer( $::SUDOER, $hcp, $hcpNetName );
            xCAT::zvmUtils->printSyslog("hcp:$hcp hcpNetName:$hcpNetName layer:$layer");

            # Get network configuration file
            # Location of this file depends on the OS
            my $srcIfcfg = '';

            # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts
            my @files;
            if ( $srcOs =~ m/rhel/i ) {
                xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts");
                $out   = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts\"", $hcp, "clone", $out, $tgtDevNode );
                if ($rc != 0) {
                    xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                    return;
                }
                $out = `echo "$out" | egrep -a -i 'ifcfg-eth'`;
                xCAT::zvmUtils->printSyslog("$out");
                @files = split('\n', $out);
                @words = split( ':', $files[0] );
                $srcIfcfg = $words[0];
            }

            # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network
            elsif ( $srcOs =~ m/sles10/i ) {
                $out   = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"`;
                xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*");
                xCAT::zvmUtils->printSyslog("$out");
                @files = split('\n', $out);
                @words = split( ':', $files[0] );
                $srcIfcfg = $words[0];
            }

            # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network
            elsif ( $srcOs =~ m/sles11/i ) {
                $out   = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"`;
                xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*");
                xCAT::zvmUtils->printSyslog("$out");
                @files = split('\n', $out);
                @words = split( ':', $files[0] );
                $srcIfcfg = $words[0];
            }

            my $ifcfgPath = $srcIfcfg;

            # Change IP, network, and mask
            # Go through each network
            my $tgtNetwork = "";
            my $tgtMask;
            foreach (@$netEntries) {

                # Get network and mask
                $tgtNetwork = $_->{'net'};
                $tgtMask = $_->{'mask'};

                # If the host IP address is in this subnet, return
                if (xCAT::NetworkUtils->ishostinsubnet($targetIp, $tgtMask, $tgtNetwork)) {

                    # Exit loop
                    last;
                } else {
                    $tgtNetwork = "";
                }
            }
            # Make sure we change all host name occurrances: long and short. Change old IP to new IP
            # add in a duplicate change in case they put in two space delimited tokens
            $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \'s/$sourceIp/$targetIp/i\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode\\\./ $tgtNode\\\./gi\' \ -e \'s/ $sourceNode\\\$/ $tgtNode/gi\' \ $cloneMntPt/etc/hosts"`;
            $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceIp/$targetIp/i\" \ -e \"s/$sourceNode/$tgtNode/i\" $ifcfgPath"`;
            xCAT::zvmUtils->printSyslog("sed -i -e \'s/$sourceIp/$targetIp/i\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode\\\./ $tgtNode\\\./gi\' \ -e \'s/ $sourceNode\\\$/ $tgtNode/gi\' \ $cloneMntPt/etc/hosts");
            xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceIp/$targetIp/i \ -e s/$sourceNode/$tgtNode/i $ifcfgPath");

            if ($tgtNetwork && $tgtMask) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$srcNetwork/$tgtNetwork/i\" \ -e \"s/$srcMask/$tgtMask/i\" $ifcfgPath"`;
                xCAT::zvmUtils->printSyslog("sed -i -e s/$srcNetwork/$tgtNetwork/i \ -e s/$srcMask/$tgtMask/i $ifcfgPath");
            }

            # Set MAC address
            my $networkFile = $tgtNode . "NetworkConfig";
            my $config;

            $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath"`;
            ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $ifcfgPath\"", $hcp, "clone", $config, $tgtDevNode );
            if ($rc != 0) {
                xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                return;
            }
            if ( $srcOs =~ m/rhel/i ) {
                # Red Hat only
                $config = `echo "$config" | egrep -a -i -v "MACADDR"`;
                $config .= "MACADDR='" . $targetMac . "'\n";
            } else {
                # SUSE only
                $config = `echo "$config" | egrep -a -i -v "LLADDR" | egrep -a -i -v "UNIQUE"`;

                # Set to MAC address (only for layer 2)
                if ( $layer == 2 ) {
                    $config .= "LLADDR='" . $targetMac . "'\n";
                    $config .= "UNIQUE=''\n";
                }
            }
            xCAT::zvmUtils->printSyslog("$config");

            # Write network configuration
            # You cannot SCP file over to mount point as sudo, so you have to copy file to zHCP
            # and move it to mount point
            $out = `echo -e "$config" > /tmp/$networkFile`;
            $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -rf $ifcfgPath"`;
            $out = `cat /tmp/$networkFile | ssh $::SUDOER\@$hcp "$::SUDO cat > /tmp/$networkFile"`;
            $out = `ssh $::SUDOER\@$hcp "$::SUDO mv /tmp/$networkFile $ifcfgPath"`;
            $out = `rm -rf /tmp/$networkFile`;

            # Set to hardware configuration (only for layer 2)
            if ( $layer == 2 ) {
                if ( $srcOs =~ m/rhel/i && $srcMac ) {
                    #*** Red Hat Linux ***

                    # Set MAC address
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$srcMac/$targetMac/i\" $ifcfgPath"`;
                    xCAT::zvmUtils->printSyslog("sed -i -e s/$srcMac/$targetMac/i $ifcfgPath");
                } else {
                    #*** SuSE Linux ***

                    # Get hardware configuration
                    # hwcfg-qeth file is in /etc/sysconfig/hardware
                    my $hwcfgPath = $cloneMntPt . "/etc/sysconfig/hardware/hwcfg-qeth-bus-ccw-0.0.$srcNicAddr";
                    xCAT::zvmUtils->printSyslog("hwcfgPath=$hwcfgPath");
                    my $hardwareFile = $tgtNode . "HardwareConfig";
                    $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $hwcfgPath" | grep -a -v "QETH_LAYER2_SUPPORT" > /tmp/$hardwareFile`;
                    $out = `echo "QETH_LAYER2_SUPPORT='1'" >> /tmp/$hardwareFile`;
                    xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, "/tmp/$hardwareFile", $hwcfgPath );

                    # Remove hardware file from /tmp
                    $out = `rm /tmp/$hardwareFile`;
                }
            }    # End of if ( $layer == 2 )

            # Remove old SSH keys
            $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -f $cloneMntPt/etc/ssh/ssh_host_*"`;
        }

        # Flush disk
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/sync"`;

        # Unmount disk
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/umount $cloneMntPt"`;

        # Remove mount point
        $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -rf $cloneMntPt"`;

        # Disable disks
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr );

        # Detatch disks from HCP
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;

        sleep(5);
    }    # End of foreach (@tgtDisks)

    # Update DHCP (only if it is running)
    $out = `service dhcpd status`;
    if (!($out =~ m/unused/i || $out =~ m/stopped/i)) {
        $out = `/opt/xcat/bin/makedhcp -a`;
    }

    # Power on target virtual server
    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Powering on" );
    $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $tgtUserId"`;
    xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $tgtUserId");

    # Check for error
    $rc = xCAT::zvmUtils->checkOutput( $out );
    if ( $rc == -1 ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );
        return;
    }
}

#-------------------------------------------------------

=head3   nodeSet

    Description : Set the boot state for a node
                    * Punch initrd, kernel, and parmfile to node reader
                    * Layer 2 and 3 VSwitch/Lan supported
    Arguments   : Node
    Returns     : Nothing, errors returned in $callback
    Example     : nodeSet($callback, $node, $args);

=cut

#-------------------------------------------------------
sub nodeSet {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid', 'status' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # If the node is being actively cloned then return.
    if ( $propVals->{'status'} =~ /CLONING=1/ and $propVals->{'status'} =~ /CLONE_ONLY=1/ ) {
        return;
    }

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if (!$hcp) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if (!$userId) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    # Parse the possible operands
    my $osImg;
    my $remoteHost;
    my $transport;
    my $device;
    my $action;
    my $rc;

    foreach my $arg ( @$args ) {
        if ($arg =~ m/^osimage=/i) {
            $osImg = $arg;
            $osImg =~ s/^osimage=//;
        } elsif ($arg =~ m/^device=/i) {
            $device = $arg;
            $device =~ s/device=//;
        } elsif ($arg =~ m/^remotehost=/i) {
            $remoteHost = $arg;
            $remoteHost =~ s/remotehost=//;
        } elsif ($arg =~ m/^transport=/i) {
            $transport = $arg;
            $transport =~ s/transport=//;
        } else {
            # If not a recognized operand with a value then it must be an action
            $action = $arg;
        }
    }

    # Handle case where osimage is specified
    my $os;
    my $arch;
    my $profile;
    my $provMethod;
    if (defined $osImg) {
        $osImg =~ s/osimage=//;
        $osImg =~ s/^\s+//;
        $osImg =~ s/\s+$//;

        @propNames = ('profile', 'provmethod', 'osvers', 'osarch');
        $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $osImg, @propNames );

        # Update nodetype table with os, arch, and profile based on osimage
        if ( !$propVals->{'profile'} || !$propVals->{'provmethod'} || !$propVals->{'osvers'} || !$propVals->{'osarch'} ) {
            # Exit
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile, provmethod, osvers, or osarch for osimage" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Provide profile, provmethod, osvers, and osarch in the osimage definition" );
            return;
        }

        # Update nodetype table with osimage attributes for node
        my %propHash = (
            'os'      => $propVals->{'osvers'},
            'arch'    => $propVals->{'osarch'},
            'profile' => $propVals->{'profile'},
            'provmethod' => $propVals->{'provmethod'}
        );
        xCAT::zvmUtils->setNodeProps( 'nodetype', $node, \%propHash );
        $action = $propVals->{'provmethod'};
    }

    # Get install directory and domain from site table
    my @entries = xCAT::TableUtils->get_site_attribute("installdir");
    my $installDir = $entries[0];
    @entries = xCAT::TableUtils->get_site_attribute("domain");
    my $domain = $entries[0];
    @entries = xCAT::TableUtils->get_site_attribute("master");
    my $master = $entries[0];
    @entries = xCAT::TableUtils->get_site_attribute("xcatdport");
    my $xcatdPort = $entries[0];

    # Get node OS, arch, and profile from 'nodetype' table
    @propNames = ( 'os', 'arch', 'profile' );
    $propVals = xCAT::zvmUtils->getNodeProps( 'nodetype', $node, @propNames );

    $os      = $propVals->{'os'};
    $arch    = $propVals->{'arch'};
    $profile = $propVals->{'profile'};

    # If no OS, arch, or profile is found
    if (!$os || !$arch || !$profile) {

        # Exit
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node OS, arch, and profile in nodetype table" );
        return;
    }

    # Get action
    my $out;
    my $outmsg;
    if ( $action eq "install" ) {

        # Get node root password
        @propNames = ('password');
        $propVals = xCAT::zvmUtils->getTabPropsByKey( 'passwd', 'key', 'system', @propNames );
        my $passwd = $propVals->{'password'};
        if ( !$passwd ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing root password for this node" );
            return;
        }

        # Get node OS base
        my @tmp;
        if ( $os =~ m/sp/i ) {
            @tmp = split( /sp/, $os );
        } else {
            @tmp = split( /\./, $os );
        }
        my $osBase = $tmp[0];

        # Get node distro
        my $distro = "";
        if ( $os =~ m/sles/i ) {
            $distro = "sles";
        } elsif ( $os =~ m/rhel/i ) {
            $distro = "rh";
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to determine node Linux distribution" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify the node Linux distribution is either sles* or rh*" );
            return;
        }

        # Get autoyast/kickstart template
        my $tmpl;

        # Check for $profile.$os.$arch.tmpl
        if ( -e "$installDir/custom/install/$distro/$profile.$os.$arch.tmpl" ) {
            $tmpl = "$profile.$os.$arch.tmpl";
        }
        # Check for $profile.$osBase.$arch.tmpl
        elsif ( -e "$installDir/custom/install/$distro/$profile.$osBase.$arch.tmpl" ) {
            $tmpl = "$profile.$osBase.$arch.tmpl";
        }
        # Check for $profile.$arch.tmpl
        elsif ( -e "$installDir/custom/install/$distro/$profile.$arch.tmpl" ) {
            $tmpl = "$profile.$arch.tmpl";
        }
        # Check for $profile.tmpl second
        elsif ( -e "$installDir/custom/install/$distro/$profile.tmpl" ) {
            $tmpl = "$profile.tmpl";
        }
        else {
            # No template exists
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing autoyast/kickstart template: $installDir/custom/install/$distro/$profile.$os.$arch.tmpl" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Create a template under $installDir/custom/install/$distro/" );
            return;
        }

        # Get host IP and hostname from /etc/hosts
        $out = `cat /etc/hosts | egrep -a -i "$node |$node."`;
        my @words    = split( ' ', $out );
        my $hostIP   = $words[0];
        my $hostname = $words[2];
        if (!($hostname =~ m/./i)) {
            $hostname = $words[1];
        }

        if ( !$hostIP || !$hostname ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the nodes IP address is specified in the hosts table and then run makehosts" );
            return;
        }

        # Check template if DHCP is used
        my $dhcp = 0;
        if ($distro eq "sles") {
            # Check autoyast template
            if ( -e "$installDir/custom/install/sles/$tmpl" ) {
                $out = `cat $installDir/custom/install/sles/$tmpl | egrep -a -i "<bootproto>"`;
                if ($out =~ m/dhcp/i) {
                    $dhcp = 1;
                }
            }
        } elsif ($distro eq "rh") {
            # Check kickstart template
            if ( -e "$installDir/custom/install/rh/$tmpl" ) {
                $out = `cat $installDir/custom/install/rh/$tmpl | egrep -a -ie "--bootproto dhcp"`;
                if ($out =~ m/dhcp/i) {
                    $dhcp = 1;
                }
            }
        }

        # Get the noderes.primarynic
        my $channel = '';
        my $layer;
        my $i;

        @propNames = ( 'primarynic', 'nfsserver', 'xcatmaster' );
        $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames );

        my $repo = $propVals->{'nfsserver'};  # Repository containing Linux ISO
        my $xcatmaster = $propVals->{'xcatmaster'};
        my $primaryNic = $propVals->{'primarynic'};  # NIC to use for OS installation

        # If noderes.primarynic is not specified, find an acceptable NIC shared with the zHCP
        if ($primaryNic) {
            $layer = xCAT::zvmCPUtils->getNetworkLayer($::SUDOER, $hcp, $primaryNic);

            # If DHCP is used and the NIC is not layer 2, then exit
            if ($dhcp && $layer != 2) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) The template selected uses DHCP. A layer 2 VSWITCH or GLAN is required. None were found." );
                xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Modify the template to use <bootproto>static</bootproto> or --bootproto=static, or change the network device attached to virtual machine" );
                return;
            }

            # Find device channel of NIC
            my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`;
            $out = `echo "$userEntry" | grep -a "NICDEF" | grep -a "$primaryNic"`;

            # Check user profile for device channel
            if (!$out) {
                my $profileName = `echo "$userEntry" | grep -a "INCLUDE"`;
                if ($profileName) {
                    @words = split(' ', xCAT::zvmUtils->trimStr($profileName));

                    # Get user profile
                    my $userProfile = xCAT::zvmUtils->getUserProfile($::SUDOER, $hcp, $words[1]);

                    # Get the NICDEF statement containing the HCP network
                    $out = `echo "$userProfile" | grep -a "NICDEF" | grep -a "$primaryNic"`;
                }
            }

            # Grab the device channel from the NICDEF statement
            my @lines = split('\n', $out);
            @words = split(' ',  $lines[0]);
            $channel = sprintf('%d', hex($words[1]));
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: Searching for acceptable network device");
            ($primaryNic, $channel, $layer) = xCAT::zvmUtils->findUsablezHcpNetwork($::SUDOER, $hcp, $userId, $dhcp);

            # If DHCP is used and not layer 2
            if ($dhcp && $layer != 2) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) The template selected uses DHCP. A layer 2 VSWITCH or GLAN is required. None were found." );
                xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Modify the template to use <bootproto>static</bootproto> or change the network device attached to virtual machine" );
                return;
            }
        }

        # Exit if no suitable network found
        if (!$primaryNic || !$channel || !$layer) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) No suitable network device found in user directory entry" );
            return;
        }

        xCAT::zvmUtils->printLn( $callback, "$node: Setting up networking on $primaryNic (layer:$layer | DHCP:$dhcp)" );

        # Generate read, write, and data channels
        my $readChannel = "0.0." . (sprintf('%X', $channel + 0));
        if (length($readChannel) < 8) {
            # Prepend a zero
            $readChannel = "0.0.0" . (sprintf('%X', $channel + 0));
        }

        my $writeChannel = "0.0." . (sprintf('%X', $channel + 1));
        if (length($writeChannel) < 8) {
            # Prepend a zero
            $writeChannel = "0.0.0" . (sprintf('%X', $channel + 1));
        }

        my $dataChannel = "0.0." . (sprintf('%X', $channel + 2));
        if (length($dataChannel) < 8) {
            # Prepend a zero
            $dataChannel = "0.0.0" . (sprintf('%X', $channel + 2));
        }

        # Get MAC address (Only for layer 2)
        my $mac = "";
        my @propNames;
        my $propVals;
        if ($layer == 2) {

            # Search 'mac' table for node
            @propNames = ('mac');
            $propVals  = xCAT::zvmUtils->getTabPropsByKey('mac', 'node', $node, @propNames);
            $mac       = $propVals->{'mac'};

            # If no MAC address is found, exit
            # MAC address should have been assigned to the node upon creation
            if (!$mac) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing MAC address of node");
                return;
            }
        }

        # Get networks in 'networks' table
        my $entries = xCAT::zvmUtils->getAllTabEntries('networks');

        # Go through each network
        my $network = "";
        my $mask;
        foreach (@$entries) {

            # Get network and mask
            $network = $_->{'net'};
            $mask = $_->{'mask'};

            # If the host IP address is in this subnet, return
            if (xCAT::NetworkUtils->ishostinsubnet($hostIP, $mask, $network)) {

                # Exit loop
                last;
            } else {
                $network = "";
            }
        }

        # If no network found
        if ( !$network ) {

            # Exit
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any network in the networks table" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the subnet in the networks table. The mask, gateway, tftpserver, and nameservers must be specified for the subnet." );
            return;
        }

        @propNames = ( 'mask', 'gateway', 'tftpserver', 'nameservers' );
        $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames );
        $mask       = $propVals->{'mask'};
        my $gateway    = $propVals->{'gateway'};

        # Convert <xcatmaster> to nameserver IP
        my $nameserver;
        if ($propVals->{'nameservers'} eq '<xcatmaster>') {
            $nameserver = xCAT::InstUtils->convert_xcatmaster();
        } else {
            $nameserver = $propVals->{'nameservers'};
        }

        if ( !$network || !$mask || !$nameserver ) {
            # It is acceptable to not have a gateway
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing network information" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the mask, gateway, and nameservers for the subnet in the networks table" );
            return;
        }

        @propNames = ( 'nfsserver', 'xcatmaster' );
        $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames );
        $repo = $propVals->{'nfsserver'};  # Repository containing Linux ISO
        $xcatmaster = $propVals->{'xcatmaster'};

        # Use noderes.xcatmaster instead of site.master if it is given
        if ( $xcatmaster ) {
            $master = $xcatmaster;
        }

        # Combine NFS server and installation directory, e.g. 10.0.0.1/install
        my $nfs = $master . $installDir;

        # Get broadcast address
        @words = split(/\./, $hostIP);
        my ($ipUnpack) = unpack("N", pack("C4", @words));
        @words = split(/\./, $mask);
        my ($maskUnpack) = unpack("N", pack( "C4", @words ));

        # Calculate broadcast address by inverting the netmask and do a logical or with network address
        my $math = ( $ipUnpack & $maskUnpack ) + ( ~ $maskUnpack );
        @words = unpack("C4", pack( "N", $math )) ;
        my $broadcast = join(".", @words);

        # Load VMCP module on HCP
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`;

        # Sample paramter file exists in installation CD (Use that as a guide)
        my $sampleParm;
        my $parmHeader;
        my $parms;
        my $parmFile;
        my $kernelFile;
        my $initFile;

        # If punch is successful - Look for this string
        my $searchStr = "created and transferred";

        # Default parameters - SUSE
        my $instNetDev   = "osa";     # Only OSA interface type is supported
        my $osaInterface = "qdio";    # OSA interface = qdio or lcs
        my $osaMedium    = "eth";     # OSA medium = eth (ethernet) or tr (token ring)

        # Default parameters - RHEL
        my $netType  = "qeth";
        my $portName = "FOOBAR";
        my $portNo   = "0";

        # Get postscript content
        my $postScript;
        if ( $os =~ m/sles10/i ) {
            $postScript = "/opt/xcat/share/xcat/install/scripts/post.sles10.s390x";
        } elsif ( $os =~ m/sles11/i ) {
            $postScript = "/opt/xcat/share/xcat/install/scripts/post.sles11.s390x";
        } elsif ( $os =~ m/rhel5/i ) {
            $postScript = "/opt/xcat/share/xcat/install/scripts/post.rhel5.s390x";
        } elsif ( $os =~ m/rhel6/i ) {
            $postScript = "/opt/xcat/share/xcat/install/scripts/post.rhel6.s390x";
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) No postscript available for $os" );
            return;
        }

        # SUSE installation
        my $customTmpl;
        my $pkglist;
        my $patterns = '';
        my $packages = '';
        my $postBoot = "$installDir/postscripts/xcatinstallpost";
        my $postInit = "$installDir/postscripts/xcatpostinit1";
        if ( $os =~ m/sles/i ) {

            # Create directory in FTP root (/install) to hold template
            $out = `mkdir -p $installDir/custom/install/sles`;

            # Copy autoyast template
            $customTmpl = "$installDir/custom/install/sles/" . $node . "." . $profile . ".tmpl";
            if ( -e "$installDir/custom/install/sles/$tmpl" ) {
                $out = `cp $installDir/custom/install/sles/$tmpl $customTmpl`;
            } else {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) An autoyast template does not exist for $os in $installDir/custom/install/sles/. Please create one." );
                return;
            }

            # Get pkglist from /install/custom/install/sles/compute.sles11.s390x.otherpkgs.pkglist
            # Original one is in /opt/xcat/share/xcat/install/sles/compute.sles11.s390x.otherpkgs.pkglist
            $pkglist = "/install/custom/install/sles/" . $profile . "." . $osBase . "." . $arch . ".pkglist";
            if ( !(-e $pkglist) ) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing package list for $os in /install/custom/install/sles/" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Please create one or copy default one from /opt/xcat/share/xcat/install/sles/" );
                return;
            }

            # Read in each software pattern or package
            open (FILE, $pkglist);
            while (<FILE>) {
                chomp;

                # Create <xml> tags, e.g.
                # <package>apache</package>
                # <pattern>directory_server</pattern>
                $_ = xCAT::zvmUtils->trimStr($_);
                if ($_ && $_ =~ /@/) {
                    $_ =~ s/@//g;
                    $patterns .= "<pattern>$_</pattern>";
                } elsif ($_) {
                    $packages .= "<package>$_</package>";
                }

            }
            close (FILE);

            # Add appropriate software packages or patterns
            $out = `sed -i -e "s,replace_software_packages,$packages,g" \ -e "s,replace_software_patterns,$patterns,g" $customTmpl`;

            # Copy postscript into template
            $out = `sed -i -e "/<scripts>/r $postScript" $customTmpl`;

            # Copy the contents of /install/postscripts/xcatpostinit1
            $out = `sed -i -e "/replace_xcatpostinit1/r $postInit" $customTmpl`;
            $out = `sed -i -e "s,replace_xcatpostinit1,,g" $customTmpl`;

            # Copy the contents of /install/postscripts/xcatinstallpost
            $out = `sed -i -e "/replace_xcatinstallpost/r $postBoot" $customTmpl`;
            $out = `sed -i -e "s,replace_xcatinstallpost,,g" $customTmpl`;

            # Edit template
            my $device;
            my $chanIds = "$readChannel $writeChannel $dataChannel";

            # SLES 11
            if ( $os =~ m/sles11/i ) {
                $device = "eth0";
            } else {
                # SLES 10
                $device = "qeth-bus-ccw-$readChannel";
            }
            # remove any line ends
            chomp($hostIP);
            chomp($hostname);
            chomp($node);
            chomp($domain);
            chomp($node);
            chomp($nameserver);
            chomp($broadcast);
            chomp($device);
            chomp($hostIP);
            chomp($mac);
            chomp($mask);
            chomp($network);
            chomp($chanIds);
            chomp($gateway);
            chomp($passwd);
            chomp($readChannel);
            chomp($master);

            # remove any blanks
            $hostIP      = xCAT::zvmUtils->trimStr($hostIP);
            $hostname    = xCAT::zvmUtils->trimStr($hostname);
            $node        = xCAT::zvmUtils->trimStr($node);
            $domain      = xCAT::zvmUtils->trimStr($domain);
            $node        = xCAT::zvmUtils->trimStr($node);
            $nameserver  = xCAT::zvmUtils->trimStr($nameserver);
            $broadcast   = xCAT::zvmUtils->trimStr($broadcast);
            $device      = xCAT::zvmUtils->trimStr($device);
            $hostIP      = xCAT::zvmUtils->trimStr($hostIP);
            $mac         = xCAT::zvmUtils->trimStr($mac);
            $mask        = xCAT::zvmUtils->trimStr($mask);
            $network     = xCAT::zvmUtils->trimStr($network);
            $chanIds     = xCAT::zvmUtils->trimStr($chanIds);
            $gateway     = xCAT::zvmUtils->trimStr($gateway);
            $passwd      = xCAT::zvmUtils->trimStr($passwd);
            $readChannel = xCAT::zvmUtils->trimStr($readChannel);
            $master      = xCAT::zvmUtils->trimStr($master);

            $out =`sed -i -e "s,replace_host_address,$hostIP,g" $customTmpl`;
            $out = `sed -i -e "s,replace_long_name,$hostname,g" $customTmpl`;
            $out = `sed -i -e "s,replace_short_name,$node,g" $customTmpl`;
            $out = `sed -i -e "s,replace_domain,$domain,g" $customTmpl`;
            $out = `sed -i -e "s,replace_hostname,$node,g" $customTmpl`;
            $out = `sed -i -e "s,replace_nameserver,$nameserver,g" $customTmpl`;
            $out = `sed -i -e "s,replace_broadcast,$broadcast,g" $customTmpl`;
            $out = `sed -i -e "s,replace_device,$device,g" $customTmpl`;
            $out = `sed -i -e "s,replace_ipaddr,$hostIP,g" $customTmpl`;
            $out = `sed -i -e "s,replace_lladdr,$mac,g" $customTmpl`;
            $out = `sed -i -e "s,replace_netmask,$mask,g" $customTmpl`;
            $out = `sed -i -e "s,replace_network,$network,g" $customTmpl`;
            $out = `sed -i -e "s,replace_ccw_chan_ids,$chanIds,g" $customTmpl`;
            $out = `sed -i -e "s,replace_ccw_chan_mode,FOOBAR,g" $customTmpl`;
            $out = `sed -i -e "s,replace_gateway,$gateway,g" $customTmpl`;
            $out = `sed -i -e "s,replace_root_password,$passwd,g" $customTmpl`;
            $out = `sed -i -e "s,replace_nic_addr,$readChannel,g" $customTmpl`;
            $out = `sed -i -e "s,replace_master,$master,g" $customTmpl`;
            $out = `sed -i -e "s,replace_install_dir,$installDir,g" $customTmpl`;

            xCAT::zvmUtils->printSyslog("***Provision settings for SLES:replace_host_address,$hostIP replace_long_name,$hostname replace_short_name,$node replace_domain,$domain replace_hostname,$node replace_nameserver,$nameserver replace_broadcast,$broadcast replace_device,$device replace_ipaddr,$hostIP replace_lladdr,$mac replace_netmask,$mask replace_network,$network replace_ccw_chan_ids,$chanIds replace_ccw_chan_mode,FOOBAR replace_gateway,$gateway replace_root_password,$passwd replace_nic_addr,$readChannel replace_master,$master replace_install_dir,$installDir $customTmpl");

            # Attach SCSI FCP devices (if any)
            # Go through each pool
            # Find the SCSI device belonging to host
            my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`);
            my $hasZfcp = 0;
            my $entry;
            my $zfcpSection = "";
            foreach (@pools) {
                if (!(length $_)) {next;}
                $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$_\"", $hcp, "nodeSet", $entry, $node );
                if ($rc != 0) {
                    xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                    return;
                }
                $entry = `echo "$entry" | egrep -a -i ",$node,"`;
                chomp($entry);
                if (!$entry) {
                    next;
                }

                # Go through each zFCP device
                my @device = split('\n', $entry);
                foreach (@device) {
                    if (!(length $_)) {next;}
                    # Each entry contains: status,wwpn,lun,size,range,owner,channel,tag
                    @tmp = split(',', $_);
                    my $wwpn = $tmp[1];
                    my $lun = $tmp[2];
                    my $device = lc($tmp[6]);
                    my $tag = $tmp[7];

                    # If multiple WWPNs or device channels are specified (multipathing), just take the 1st one
                    if ($wwpn =~ m/;/i) {
                        @tmp = split(';', $wwpn);
                        $wwpn = xCAT::zvmUtils->trimStr($tmp[0]);
                    }

                    if ($device =~ m/;/i) {
                        @tmp = split(';', $device);
                        $device = xCAT::zvmUtils->trimStr($tmp[0]);
                    }

                    # Make sure WWPN and LUN do not have 0x prefix
                    $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
                    $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

                    # Make sure channel has a length of 4
                    while (length($device) < 4) {
                        $device = "0" . $device;
                    }

                    # zFCP variables must be in lower-case or AutoYast would get confused
                    $device = lc($device);
                    $wwpn = lc($wwpn);
                    $lun = lc($lun);

                    # Find tag in template and attach SCSI device associated with it
                    $out = `sed -i -e "s#$tag#/dev/disk/by-path/ccw-0.0.$device-zfcp-0x$wwpn:0x$lun#i" $customTmpl`;

                    # Generate <zfcp> section
                    $zfcpSection .= <<END;
      <listentry>\\
        <controller_id>0.0.$device</controller_id>\\
        <fcp_lun>0x$lun</fcp_lun>\\
        <wwpn>0x$wwpn</wwpn>\\
      </listentry>\\
END
                    $hasZfcp = 1;
                }
            }

            if ($hasZfcp) {
                # Insert <zfcp> device list
                my $find = 'replace_zfcp';
                my $replace = <<END;
    <devices config:type="list">\\
END
                $replace .= $zfcpSection;
                $replace .= <<END;
    </devices>\\
END
                my $expression = "'s#" . $find . "#" .$replace . "#i'";
                $out = `sed -i -e $expression $customTmpl`;

                xCAT::zvmUtils->printLn($callback, "$node: Inserting FCP devices into template... Done");
            }

            # Read sample parmfile in /install/sles10.2/s390x/1/boot/s390x/
            $sampleParm = "$installDir/$os/s390x/1/boot/s390x/parmfile";
            open( SAMPLEPARM, "<$sampleParm" );

            # Search parmfile for -- ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb
            while (<SAMPLEPARM>) {

                # If the line contains 'ramdisk_size'
                if ( $_ =~ m/ramdisk_size/i ) {
                    $parmHeader = xCAT::zvmUtils->trimStr($_);
                }
            }

            # Close sample parmfile
            close(SAMPLEPARM);

            # Create parmfile -- Limited to 10 lines
            # End result should be:
            #   ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb
            #   HostIP=10.0.0.5 Hostname=gpok5.endicott.ibm.com
            #   Gateway=10.0.0.1 Netmask=255.255.255.0
            #   Broadcast=10.0.0.0 Layer2=1 OSAHWaddr=02:00:01:FF:FF:FF
            #   ReadChannel=0.0.0800  WriteChannel=0.0.0801  DataChannel=0.0.0802
            #   Nameserver=10.0.0.1 Portname=OSAPORT Portno=0
            #   Install=ftp://10.0.0.1/sles10.2/s390x/1/
            #   UseVNC=1  VNCPassword=12345678
            #   InstNetDev=osa OsaInterface=qdio OsaMedium=eth Manual=0
            if (!$repo) {
                $repo = "http://$nfs/$os/s390x/1";
            }

            my $ay = "http://$nfs/custom/install/sles/" . $node . "." . $profile . ".tmpl";

            $parms = $parmHeader . "\n";
            $parms = $parms . "AutoYaST=$ay\n";
            $parms = $parms . "Hostname=$hostname\n";
            $parms = $parms . " HostIP=$hostIP Gateway=$gateway Netmask=$mask\n";

            # Set layer in autoyast profile
            if ( $layer == 2 ) {
                $parms = $parms . "Broadcast=$broadcast Layer2=1 OSAHWaddr=$mac\n";
            } else {
                $parms = $parms . "Broadcast=$broadcast Layer2=0\n";
            }

            $parms = $parms . "ReadChannel=$readChannel WriteChannel=$writeChannel DataChannel=$dataChannel\n";
            $parms = $parms . "Nameserver=$nameserver Portname=$portName Portno=0\n";
            $parms = $parms . "Install=$repo\n";
            $parms = $parms . "UseVNC=1 VNCPassword=12345678\n";
            $parms = $parms . "InstNetDev=$instNetDev OsaInterface=$osaInterface OsaMedium=$osaMedium Manual=0\n";

            xCAT::zvmUtils->printSyslog("***Parm file SLES(should be max 80 cols, 10 lines:\n$parms");
            # Write to parmfile
            $parmFile = "/tmp/" . $node . "Parm";
            open( PARMFILE, ">$parmFile" );
            print PARMFILE "$parms";
            close(PARMFILE);

            # Send kernel, parmfile, and initrd to reader to HCP
            $kernelFile = "/tmp/" . $node . "Kernel";
            $initFile   = "/tmp/" . $node . "Initrd";

            if ($repo) {
                $out = `/usr/bin/wget $repo/boot/s390x/vmrdr.ikr -O $kernelFile --no-check-certificate`;
                xCAT::zvmUtils->printLn( $callback, "Attempting to copy $repo/boot/s390x/vmrdr.ikr to $kernelFile" );
                $out = `/usr/bin/wget $repo/boot/s390x/initrd -O $initFile --no-check-certificate`;
            } else {
                $out = `cp $installDir/$os/s390x/1/boot/s390x/vmrdr.ikr $kernelFile`;
                xCAT::zvmUtils->printLn( $callback, "Attempting to copy $installDir/$os/s390x/1/boot/s390x/vmrdr.ikr to $kernelFile" );
                $out = `cp $installDir/$os/s390x/1/boot/s390x/initrd $initFile`;
            }
            $out = `ls $kernelFile 2>1`;
            $rc = $? >> 8;
            if ($rc) {
               xCAT::zvmUtils->printLn( $callback, "(Failed) Did not copy the file. Did you forget to process the ISO?");
               $out = '(Failed) Did not copy the file.';
               return;
            }

            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $kernelFile, $kernelFile );
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $parmFile,   $parmFile );
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $initFile,   $initFile );

            # Set the virtual unit record devices online on HCP
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "c" );
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "d" );

            # Purge reader
            $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId );
            xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" );

            # Punch kernel to reader on HCP
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $kernelFile, "sles.kernel", "", "" );
            xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" );
            if ( $out =~ m/Failed/i ) {
                return;
            }

            # Punch parm to reader on HCP
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $parmFile, "sles.parm", "-t", "" );
            xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" );
            if ( $out =~ m/Failed/i ) {
                return;
            }

            # Punch initrd to reader on HCP
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $initFile, "sles.initrd", "", "" );
            xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" );
            if ( $out =~ m/Failed/i ) {
                return;
            }

            # Remove kernel, parmfile, and initrd from /tmp
            $out = `rm $parmFile $kernelFile $initFile`;
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $parmFile $kernelFile $initFile"`;

            xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader.  Ready for boot." );
        }

        # RHEL installation
        elsif ( $os =~ m/rhel/i ) {

            # Create directory in FTP root (/install) to hold template
            $out = `mkdir -p $installDir/custom/install/rh`;

            # Copy kickstart template
            $customTmpl = "$installDir/custom/install/rh/" . $node . "." . $profile . ".tmpl";
            if ( -e "$installDir/custom/install/rh/$tmpl" ) {
                $out = `cp $installDir/custom/install/rh/$tmpl $customTmpl`;
            } else {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) An kickstart template does not exist for $os in $installDir/custom/install/rh/" );
                return;
            }

            # Get pkglist from /install/custom/install/rh/compute.rhel6.s390x.otherpkgs.pkglist
            # Original one is in /opt/xcat/share/xcat/install/rh/compute.rhel6.s390x.otherpkgs.pkglist
            $pkglist = "/install/custom/install/rh/" . $profile . "." . $osBase . "." . $arch . ".pkglist";
            if ( !(-e $pkglist) ) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing package list for $os in /install/custom/install/rh/" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Please create one or copy default one from /opt/xcat/share/xcat/install/rh/" );
                return;
            }

            # Read in each software pattern or package
            open (FILE, $pkglist);
            while (<FILE>) {
                chomp;
                $_ = xCAT::zvmUtils->trimStr($_);
                $packages .= "$_\\n";
            }
            close (FILE);

            # Add appropriate software packages or patterns
            $out = `sed -i -e "s,replace_software_packages,$packages,g"  $customTmpl`;

            # Copy postscript into template
            $out = `sed -i -e "/%post/r $postScript" $customTmpl`;

            # Copy the contents of /install/postscripts/xcatpostinit1
            $out = `sed -i -e "/replace_xcatpostinit1/r $postInit" $customTmpl`;
            $out = `sed -i -e "s,replace_xcatpostinit1,,g" $customTmpl`;

            # Copy the contents of /install/postscripts/xcatinstallpost
            $out = `sed -i -e "/replace_xcatinstallpost/r $postBoot" $customTmpl`;
            $out = `sed -i -e "s,replace_xcatinstallpost,,g" $customTmpl`;

            # Edit template
            if (!$repo) {
                $repo = "http://$nfs/$os/s390x";
            }
            # remove newlines
            chomp($repo);
            chomp($hostIP);
            chomp($mask);
            chomp($gateway);
            chomp($nameserver);
            chomp($hostname);
            chomp($passwd);
            chomp($master);

            # trim blanks
            $repo       = xCAT::zvmUtils->trimStr($repo);
            $hostIP     = xCAT::zvmUtils->trimStr($hostIP);
            $mask       = xCAT::zvmUtils->trimStr($mask);
            $gateway    = xCAT::zvmUtils->trimStr($gateway);
            $nameserver = xCAT::zvmUtils->trimStr($nameserver);
            $hostname   = xCAT::zvmUtils->trimStr($hostname);
            $passwd     = xCAT::zvmUtils->trimStr($passwd);
            $master     = xCAT::zvmUtils->trimStr($master);

            $out = `sed -i -e "s,replace_url,$repo,g" $customTmpl`;
            $out = `sed -i -e "s,replace_ip,$hostIP,g" $customTmpl`;
            $out = `sed -i -e "s,replace_netmask,$mask,g" $customTmpl`;
            $out = `sed -i -e "s,replace_gateway,$gateway,g" $customTmpl`;
            $out = `sed -i -e "s,replace_nameserver,$nameserver,g" $customTmpl`;
            $out = `sed -i -e "s,replace_hostname,$hostname,g" $customTmpl`;
            $out = `sed -i -e "s,replace_rootpw,$passwd,g" $customTmpl`;
            $out = `sed -i -e "s,replace_master,$master,g" $customTmpl`;
            $out = `sed -i -e "s,replace_install_dir,$installDir,g" $customTmpl`;

            xCAT::zvmUtils->printSyslog("***Provision settings for RedHat:replace_url,$repo replace_ip,$hostIP replace_netmask,$mask replace_gateway,$gateway replace_nameserver,$nameserver replace_hostname,$hostname replace_rootpw,$passwd replace_master,$master replace_install_dir,$installDir  for file==>$customTmpl");

            # Attach SCSI FCP devices (if any)
            # Go through each pool
            # Find the SCSI device belonging to host
            my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`);
            my $hasZfcp = 0;
            my $entry;
            my $zfcpSection = "";
            foreach (@pools) {
                if (!(length $_)) {next;}
                $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$_\"", $hcp, "nodeSet", $entry, $node );
                if ($rc != 0) {
                    xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                    return;
                }
                $entry = `echo "$entry" | egrep -a -i ",$node,"`;
                chomp($entry);
                if (!$entry) {
                    next;
                }

                # Go through each zFCP device
                my @device = split('\n', $entry);
                foreach (@device) {
                    if (!(length $_)) {next;}
                    # Each entry contains: status,wwpn,lun,size,range,owner,channel,tag
                    @tmp = split(',', $_);
                    my $wwpn = $tmp[1];
                    my $lun = $tmp[2];
                    my $device = lc($tmp[6]);
                    my $tag = $tmp[7];

                    # If multiple WWPNs or device channels are specified (multipathing), just take the 1st one
                    if ($wwpn =~ m/;/i) {
                        @tmp = split(';', $wwpn);
                        $wwpn = xCAT::zvmUtils->trimStr($tmp[0]);
                    }

                    if ($device =~ m/;/i) {
                        @tmp = split(';', $device);
                        $device = xCAT::zvmUtils->trimStr($tmp[0]);
                    }

                    # Make sure WWPN and LUN do not have 0x prefix
                    $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
                    $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

                    # Make sure channel has a length of 4
                    while (length($device) < 4) {
                        $device = "0" . $device;
                    }

                    # zFCP variables must be in lower-case or AutoYast would get confused.
                    $device = lc($device);
                    $wwpn = lc($wwpn);
                    $lun = lc($lun);

                    # Create zfcp section
                    $zfcpSection = "zfcp --devnum 0.0.$device --wwpn 0x$wwpn --fcplun 0x$lun" . '\n';

                    # Look for replace_zfcp keyword in template and replace it
                    $out = `sed -i -e "s,$tag,$zfcpSection,i" $customTmpl`;
                    $hasZfcp = 1;
                }
            }

            if ($hasZfcp) {
                xCAT::zvmUtils->printLn($callback, "$node: Inserting FCP devices into template... Done");
            }

            # Read sample parmfile in /install/rhel5.3/s390x/images
            $sampleParm = "$installDir/$os/s390x/images/generic.prm";
            open( SAMPLEPARM, "<$sampleParm" );

            # Search parmfile for -- root=/dev/ram0 ro ip=off ramdisk_size=40000
            while (<SAMPLEPARM>) {

                # If the line contains 'ramdisk_size'
                if ( $_ =~ m/ramdisk_size/i ) {
                    $parmHeader = xCAT::zvmUtils->trimStr($_);

                    # RHEL 6.1 needs cio_ignore in order to install
                    if ( !($os =~ m/rhel6.1/i) ) {
                        $parmHeader =~ s/cio_ignore=all,!0.0.0009//g;
                    }
                }
            }

            # Close sample parmfile
            close(SAMPLEPARM);

            # Get mdisk virtual address
            my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $node );
            if (xCAT::zvmUtils->checkOutput( $mdisks[0] ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$mdisks[0]" );
                return;
            }
            @mdisks = sort(@mdisks);
            my $dasd   = "";
            my $devices = "";
            my $i      = 0;
            foreach (@mdisks) {
                if (!(length $_)) {next;}
                $i     = $i + 1;
                @words = split( ' ', $_ );

                # Do not put a comma at the end of the last disk address
                if ( $i == @mdisks ) {
                    $dasd = $dasd . "0.0.$words[1]";
                } else {
                    $dasd = $dasd . "0.0.$words[1],";
                }
            }

            # Character limit of 50 in parm file for DASD parameter
            if (length($dasd) > 50) {
                @words = split( ',', $dasd );
                $dasd = $words[0] . "-" . $words[@words - 1];
            }

            # Get dedicated virtual address
            my @dedicates = xCAT::zvmUtils->getDedicates( $callback, $::SUDOER, $node );
            if (xCAT::zvmUtils->checkOutput( $dedicates[0] ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$dedicates[0]" );
                return;
            }
            @dedicates = sort(@dedicates);
            $i = 0;
            foreach (@dedicates) {
                $i = $i + 1;
                @words = split( ' ', $_ );

                # Do not put a comma at the end of the last disk address
                if ( $i == @dedicates ) {
                    $devices = $devices . "0.0.$words[1]";
                } else {
                    $devices = $devices . "0.0.$words[1],";
                }
            }

            # Character limit of 50 in parm file for DASD parameter
            if (length($devices) > 50) {
                @words = split( ',', $devices );
                $devices = $words[0] . "-" . $words[@words - 1];
            }

            # Concat dedicated devices and DASD together
            if ($devices) {
                if ($dasd) {
                    $dasd = $dasd . "," . $devices;
                } else {
                    $dasd = $devices;
                }
            }

            # Create parmfile -- Limited to 80 characters/line, maximum of 11 lines
            # End result should be:
            #    ramdisk_size=40000 root=/dev/ram0 ro ip=off
            #     ks=ftp://10.0.0.1/rhel5.3/s390x/compute.rhel5.s390x.tmpl
            #    RUNKS=1 cmdline
            #    DASD=0.0.0100 HOSTNAME=gpok4.endicott.ibm.com
            #    NETTYPE=qeth IPADDR=10.0.0.4
            #    SUBCHANNELS=0.0.0800,0.0.0801,0.0.0800
            #    NETWORK=10.0.0.0 NETMASK=255.255.255.0
            #    SEARCHDNS=endicott.ibm.com BROADCAST=10.0.0.255
            #    GATEWAY=10.0.0.1 DNS=9.0.2.11 MTU=1500
            #    PORTNAME=UNASSIGNED PORTNO=0 LAYER2=0
            #    vnc vncpassword=12345678
            my $ks = "http://$nfs/custom/install/rh/" . $node . "." . $profile . ".tmpl";

            $parms = $parmHeader . "\n";
            $parms = $parms . "ks=$ks\n";
            $parms = $parms . "RUNKS=1 cmdline\n";
            $parms = $parms . "DASD=$dasd NETTYPE=$netType IPADDR=$hostIP\n";
            $parms = $parms . "HOSTNAME=$hostname\n";
            $parms = $parms . "SUBCHANNELS=$readChannel,$writeChannel,$dataChannel\n";
            $parms = $parms . "NETWORK=$network NETMASK=$mask\n";
            $parms = $parms . "SEARCHDNS=$domain BROADCAST=$broadcast\n";
            $parms = $parms . "GATEWAY=$gateway DNS=$nameserver MTU=1500\n";

            # Set layer in kickstart profile
            if ( $layer == 2 ) {
                $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=1 MACADDR=$mac\n";
            } else {
                $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=0\n";
            }

            $parms = $parms . "vnc vncpassword=12345678\n";

            xCAT::zvmUtils->printSyslog("***Parm file RedHat(should be max 80 cols, 11 lines:\n$parms");
            # Write to parmfile
            $parmFile = "/tmp/" . $node . "Parm";
            open( PARMFILE, ">$parmFile" );
            print PARMFILE "$parms";
            close(PARMFILE);

            # Send kernel, parmfile, conf, and initrd to reader to HCP
            $kernelFile = "/tmp/" . $node . "Kernel";
            $initFile   = "/tmp/" . $node . "Initrd";

            # Copy over kernel, parmfile, conf, and initrd from remote repository
            if ($repo) {
                $out = `/usr/bin/wget $repo/images/kernel.img -O $kernelFile --no-check-certificate`;
                xCAT::zvmUtils->printLn( $callback, "Attempting to copy $repo/images/kernel.img to $kernelFile" );
                $out = `/usr/bin/wget $repo/images/initrd.img -O $initFile --no-check-certificate`;
            } else {
                $out = `cp $installDir/$os/s390x/images/kernel.img $kernelFile`;
                xCAT::zvmUtils->printLn( $callback, "Attempting to copy $installDir/$os/s390x/images/kernel.img to $kernelFile" );
                $out = `cp $installDir/$os/s390x/images/initrd.img $initFile`;
            }
            $out = `ls $kernelFile 2>1`;
            $rc = $? >> 8;
            if ($rc) {
               xCAT::zvmUtils->printLn( $callback, "(Failed) Did not copy the file. Did you forget to process the ISO?");
               $out = '(Failed) Did not copy the file.';;
               return;
            }

            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $kernelFile, $kernelFile );
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $parmFile,   $parmFile );
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $initFile,   $initFile );

            # Set the virtual unit record devices online
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "c" );
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "d" );

            # Purge reader
            $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId );
            xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" );

            # Punch kernel to reader on HCP
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $kernelFile, "rhel.kernel", "", "" );
            xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" );
            if ( $out =~ m/Failed/i ) {
                return;
            }

            # Punch parm to reader on HCP
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $parmFile, "rhel.parm", "-t", "" );
            xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" );
            if ( $out =~ m/Failed/i ) {
                return;
            }

            # Punch initrd to reader on HCP
            $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $initFile, "rhel.initrd", "", "" );
            xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" );
            if ( $out =~ m/Failed/i ) {
                return;
            }

            # Remove kernel, parmfile, and initrd from /tmp
            $out = `rm $parmFile $kernelFile $initFile`;
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $parmFile $kernelFile $initFile"`;

            xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader.  Ready for boot." );
        }
    } elsif ( $action eq "statelite" ) {

        # Get node group from 'nodelist' table
        @propNames = ('groups');
        $propVals = xCAT::zvmUtils->getTabPropsByKey( 'nodelist', 'node', $node, @propNames );
        my $group = $propVals->{'groups'};

        # Get node statemnt (statelite mount point) from 'statelite' table
        @propNames = ('statemnt');
        $propVals = xCAT::zvmUtils->getTabPropsByKey( 'statelite', 'node', $node, @propNames );
        my $stateMnt = $propVals->{'statemnt'};
        if ( !$stateMnt ) {
            $propVals = xCAT::zvmUtils->getTabPropsByKey( 'statelite', 'node', $group, @propNames );
            $stateMnt = $propVals->{'statemnt'};

            if ( !$stateMnt ) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node statemnt in statelite table. Please specify one." );
                return;
            }
        }

        # Netboot directory
        my $netbootDir = "$installDir/netboot/$os/$arch/$profile";
        my $kernelFile = "$netbootDir/kernel";
        my $parmFile   = "$netbootDir/parm-statelite";
        my $initFile   = "$netbootDir/initrd-statelite.gz";

        # If parmfile exists
        if ( -e $parmFile ) {
            # Do nothing
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: Creating parmfile" );

            my $sampleParm;
            my $parmHeader;
            my $parms;
            if ( $os =~ m/sles/i ) {
                if ( -e "$installDir/$os/s390x/1/boot/s390x/parmfile" ) {
                    # Read sample parmfile in /install/sles11.1/s390x/1/boot/s390x/
                    $sampleParm = "$installDir/$os/s390x/1/boot/s390x/parmfile";
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing $installDir/$os/s390x/1/boot/s390x/parmfile" );
                    return;
                }
            } elsif ( $os =~ m/rhel/i ) {
                if ( -e "$installDir/$os/s390x/images/generic.prm" ) {
                    # Read sample parmfile in /install/rhel5.3/s390x/images
                    $sampleParm = "$installDir/$os/s390x/images/generic.prm";
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing $installDir/$os/s390x/images/generic.prm" );
                    return;
                }
            }

            open( SAMPLEPARM, "<$sampleParm" );

            # Search parmfile for -- ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb
            while (<SAMPLEPARM>) {
                # If the line contains 'ramdisk_size'
                if ( $_ =~ m/ramdisk_size/i ) {
                    $parmHeader = xCAT::zvmUtils->trimStr($_);
                }
            }

            # Close sample parmfile
            close(SAMPLEPARM);

            # Create parmfile
            # End result should be:
            #   ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb
            #   NFSROOT=10.1.100.1:/install/netboot/sles11.1.1/s390x/compute
            #   STATEMNT=10.1.100.1:/lite/state XCAT=10.1.100.1:3001
            $parms = $parmHeader . "\n";
            $parms = $parms . "NFSROOT=$master:$netbootDir\n";
            $parms = $parms . "STATEMNT=$stateMnt XCAT=$master:$xcatdPort\n";

            # Write to parmfile
            open( PARMFILE, ">$parmFile" );
            print PARMFILE "$parms";
            close(PARMFILE);
        }

        # Temporary kernel, parmfile, and initrd
        my $tmpKernelFile = "/tmp/$os-kernel";
        my $tmpParmFile   = "/tmp/$os-parm-statelite";
        my $tmpInitFile   = "/tmp/$os-initrd-statelite.gz";

        xCAT::zvmUtils->printLn( $callback, "$node: Looking for kernel $os-kernel." );
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $::SUDOER\@$hcp \"$::SUDO ls /tmp\"", $hcp, "nodeSet", $out, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        if (`echo "$out" | egrep -a -i "$os-kernel"`) {
            # Do nothing
        } else {
            # Send kernel to reader to HCP
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $kernelFile, $tmpKernelFile );
            xCAT::zvmUtils->printLn( $callback, "sendfile $kernelFile, $tmpKernelFile" );
        }

        if (`echo "$out" | egrep -a -i "$"os-parm-statelite"`) {
            # Do nothing
        } else {
            # Send parmfile to reader to HCP
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $parmFile, $tmpParmFile );
        }

        if (`echo "$out" | egrep -a -i "$os-initrd-statelite.gz"`) {
            # Do nothing
        } else {
            # Send initrd to reader to HCP
            xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $initFile, $tmpInitFile );
        }

        # Set the virtual unit record devices online
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "c" );
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "d" );

        # Purge reader
        $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId );
        xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" );

        # Kernel, parm, and initrd are in /install/netboot/<os>/<arch>/<profile>
        # Punch kernel to reader on HCP
        $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $tmpKernelFile, "sles.kernel", "", "" );
        xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" );
        if ( $out =~ m/Failed/i ) {
            return;
        }

        # Punch parm to reader on HCP
        $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $tmpParmFile, "sles.parm", "-t", "" );
        xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" );
        if ( $out =~ m/Failed/i ) {
            return;
        }

        # Punch initrd to reader on HCP
        $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $tmpInitFile, "sles.initrd", "", "" );
        xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" );
        if ( $out =~ m/Failed/i ) {
            return;
        }

        xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader.  Ready for boot." );
    } elsif (( $action eq "netboot" ) || ( $action eq "sysclone" )) {

        # Obtain the location of the install root directory
        my $installRoot = xCAT::TableUtils->getInstallDir();

        # Verify the image exists
        my $deployImgDir = "$installRoot/$action/$os/$arch/$profile";
        my @imageFiles = glob "$deployImgDir/*.img";
        my %imageFileList;
        if ( @imageFiles == 0 ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir does not contain image files" );
            return;
        } else {
            # Obtain the list of image files and the vaddr to which they relate
            foreach my $imageFileFull ( @imageFiles ) {
                my $imageFile = (split('/', $imageFileFull))[-1];
                my $vaddr = (split('\.', $imageFile))[0];
                $imageFileList{ $vaddr } = $imageFile;
            }
        }

        # Build the list of image files and their target device addresses
        if ( $action eq "netboot" ) {
            if ( @imageFiles > 1 ) {
                # Can only have one image file for netboot
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir contains more than the expected number of image files" );
                return;
            }
            if (! defined $device) {
                # A device must be specified
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Image device was not specified" );
                return;
            }
            # For netboot, image device address is not necessarily the same as the original device name
            my $origKey = (keys %imageFileList)[0];
            if ( $origKey ne $device ) {
                # file name was different with a different name than the target device.  Update to use the target device.
                $imageFileList{ $device } = $imageFileList{ $origKey };
                delete $imageFileList{ $origKey };
            }
        } else {
            # Handle sysclone which can have multiple image files which MUST match each mdisk
            # Get the list of mdisks
            my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $node );
            if (xCAT::zvmUtils->checkOutput( $srcDisks[0] ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$srcDisks[0]" );
                return;
            }

            # Verify the list of images and matching disks
            my $validArrayDisks = 0;
            foreach (@srcDisks) {
                # Get disk address
                my @words = split( ' ', $_ );
                my $vaddr = $words[1];
                my $diskType = $words[2];

                if ( $diskType eq 'FB-512' ) {
                    # We do not do not deploy into vdisks
                    next;
                }

                # Add 0 in front if address length is less than 4
                while (length($vaddr) < 4) {
                    $vaddr = '0' . $vaddr;
                }

                if ( defined $imageFileList{$vaddr} ) {
                    # We only count disks that have an image file.
                    $validArrayDisks = $validArrayDisks + 1;
                }
            }
            if ( $validArrayDisks != @imageFiles ) {
                # Number of mdisks does not match the number of image files
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir contains images for devices that do not exist." );
                return;
            }
        }

        # Ensure the staging directory exists in case we need to create subdirectories in it.
        if (!-d "$installRoot/staging") {
            mkpath("$installRoot/staging");
        }

        # Prepare the deployable mount point on zHCP, if it needs to be established.
        my $remoteDeployDir;
        my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $action, "ro", \$remoteDeployDir);
        if ( $rc ) {
            # Mount failed
            return;
        }

        # Drive each device deploy separately.  Up to 10 at a time.
        # Each deploy request to zHCP is driven from a child process.
        # Process ID for xfork()
        my $pid;

        # Child process IDs
        my @children;

        # Make a temporary directory in case a process needs to communicate a problem.
        my $statusDir = mkdtemp("$installRoot/staging/status.$$.XXXXXX");

        xCAT::zvmUtils->printLn( $callback, "$node: Deploying the image using the zHCP node" );
        my $reason;
        for my $vaddr ( keys %imageFileList ) {
            $pid = xCAT::Utils->xfork();

            # Parent process
            if ($pid) {
                push( @children, $pid );
            }

            # Child process.
            elsif ( $pid == 0 ) {
                # Drive the deploy on the zHCP node
                # Copy the image to the target disk using the zHCP node
                xCAT::zvmUtils->printSyslog( "nodeset() unpackdiskimage $userId $vaddr $remoteDeployDir/$os/$arch/$profile/$imageFileList{$vaddr}" );
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/unpackdiskimage $userId $vaddr $remoteDeployDir/$os/$arch/$profile/$imageFileList{$vaddr}"`;
                $rc = $?;

                # Check for script errors
                my $reasonString = "";
                $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString );
                if ($rc != 0) {
                    $reason = "Reason: $reasonString";
                    xCAT::zvmUtils->printSyslog( "nodeset() unpackdiskimage of $userId $vaddr failed. $reason" );
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to deploy the image to $userId $vaddr. $reason" );
                    # Create a "FAILED" file to indicate the failure.
                    if ( ! open FILE, '>'."$statusDir/FAILED" ) {
                        # if we can't open it then we log the problem.
                        xCAT::zvmUtils->printSyslog( "nodeset() unable to create a 'FAILED' file." );
                    }
                }

                # Exit the child process
                exit(0);
            }

            else {
                # Ran out of resources
                # Create a "FAILED" file to indicate the failure.
                if ( ! open FILE, '>'."$statusDir/FAILED" ) {
                     # if we can't open it then we log the problem.
                    xCAT::zvmUtils->printSyslog( "nodeset() unable to create a 'FAILED' file." );
                }

                $reason = "Reason: Could not fork\n";
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to deploy the image to $userId $vaddr. $reason" );
                last;
            }

            # Handle 10 nodes at a time, else you will get errors
            if ( !( @children % 10 ) ) {

                # Wait for all processes to end
                foreach (@children) {
                    waitpid( $_, 0 );
                }

                # Clear children
                @children = ();
            }
        }   # End of foreach

        # If any children remain, then wait for them to complete.
        foreach $pid ( @children ) {
            xCAT::zvmUtils->printSyslog( "nodeset() Waiting for child process $pid to complete" );
            waitpid( $pid, 0 );
        }

        # If the deploy failed then clean up and return
        if ( -e "$statusDir/FAILED" ) {
            # Failure occurred in one of the child processes.  A message was already generated.
            rmtree "$statusDir";
            return;
        }
        rmtree "$statusDir";

        # If the transport file was specified then setup the transport disk.
        if ($transport) {
            my $transImgDir = "$installRoot/staging/transport";
            if(!-d $transImgDir) {
                mkpath($transImgDir);
            }

            # Create unique transport directory and copy the transport file to it
            my $transportDir = `/bin/mktemp -d $installDir/staging/transport/XXXXXX`;
            chomp($transportDir);
            if ($remoteHost) {
                # Copy the transport file from the remote system to the local transport directory.
                xCAT::zvmUtils->printLn( $callback, "/usr/bin/scp -B $remoteHost:$transport $transportDir" );
                $out = `/usr/bin/scp -v -B $remoteHost:$transport $transportDir`;
                $rc = $?;
            } else {
                # Safely copy the transport file from a local directory.
                $out = `/bin/cp $transport $transportDir`;
                $rc = $?;
            }

            if ($rc != 0) {
                # Copy failed  Get rid of the unique directory that was going to receive the copy.
                rmtree $transportDir;
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to copy the transport file");
                return;
            }

            # Check the zvm table to see if the node flag "XCATCONF4Z=0" is set or not in status column,
            # if set put it to a temp dir for later use, otherwise punched the transport file directly to node's reader,
            my $nodeFlag = '';
            my $cfgTrunkDir = "/tmp/configdrive/$node/";
            my @propNames = ('status');
            my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames );
            $nodeFlag = $propVals->{'status'};
            if ($nodeFlag =~ /XCATCONF4Z=0/) {
                if (!-d $cfgTrunkDir) {
                    mkpath($cfgTrunkDir, 0, 0750);
                }
                $rc = `/bin/cp -r $transportDir/* $cfgTrunkDir/ 2>/dev/null; echo $?`;
                `rm -rf $transportDir`;
                if ($rc != '0') {
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source directory $transportDir to directory $cfgTrunkDir with rc: $rc, please check if xCAT is running out of space" );
                    `rm -rf $cfgTrunkDir`;
                    return;
                }

            } else {
                # Purge the target node's reader
                $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId );
                xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done");

                # Online zHCP's punch device
                $out = xCAT::zvmUtils->onlineZhcpPunch($::SUDOER, $hcp);
                if ( $out =~ m/Failed/i ) {
                    xCAT::zvmUtils->printLn( $callback, "$node: Online zHCP's punch device... $out" );
                    return;
                }

                # Punch files to node's reader so it could be pulled on boot
                # Reader = transport disk
                my @files = glob "$transportDir/*";
                foreach (@files) {
                    my $file = basename($_);
                    my $filePath = "/tmp/$node-" . $file;

                    # Spool file only accepts [A-Za-z] and file name can only be 8-characters long
                    my @filePortions = split( '\.', $file );
                    if (( @filePortions > 2 ) ||
                        ( $filePortions[0] =~ m/[^a-zA-Z0-9]/ ) || ( length($filePortions[0]) > 8 ) || ( length($filePortions[0]) < 1 ) ||
                        ( $filePortions[1] =~ m/[^a-zA-Z0-9]{1,8}/ ) || ( length($filePortions[1]) > 8 )) {
                        $out = `/bin/rm -rf $transportDir`;
                        xCAT::zvmUtils->printLn($callback, "$node: (Error) $file contains a file name or file type portion that is longer than 8 characters, or not alphanumeric ");
                        return;
                    }

                    xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $_, $filePath);

                    my $punchOpt = "";
                    if ($file =~ /.txt/ || $file =~ /.sh/) {
                        $punchOpt = "-t";
                    }
                    $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $filePath, "$file", $punchOpt, "X" );

                    # Clean up file
                    `ssh $::SUDOER\@$hcp "$::SUDO /bin/rm $filePath"`;
                    xCAT::zvmUtils->printLn($callback, "$node: Punching $file to reader... $out");
                    if ($out =~ m/Failed/i) {
                        # Clean up transport directory.  Message was already generated.
                        $out = `/bin/rm -rf $transportDir`;
                        return;
                    }
                }
            }

            # Clean up transport directory
            $out = `/bin/rm -rf $transportDir`;
            xCAT::zvmUtils->printLn( $callback, "$node: Completed deploying image($os-$arch-$action-$profile)" );
        }

    } else {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" );
        return;
    }

    return;
}

#-------------------------------------------------------

=head3   getMacs

    Description : Get the MAC address of a given node
                    * Requires the node be online
                    * Saves MAC address in 'mac' table
    Arguments   : Node
    Returns     : Nothing, errors returned in $callback
    Example     : getMacs($callback, $node, $args);

=cut

#-------------------------------------------------------
sub getMacs {

    # Get inputs
    my ( $callback, $node, $args ) = @_;
    my $force = '';
    if ($args) {
        @ARGV = @$args;

        # Parse options
        GetOptions( 'f' => \$force );
    }

    my $out;
    my $outmsg;
    my $rc;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    # Get MAC address in 'mac' table
    @propNames = ('mac');
    $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames );
    my $mac;
    if ( $propVals->{'mac'} && !$force) {

        # Get MAC address
        $mac = $propVals->{'mac'};
        xCAT::zvmUtils->printLn( $callback, "$node: $mac" );
        return;
    }

    # If MAC address is not in the 'mac' table, get it using VMCP
    xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node);

    # Get xCat MN Lan/VSwitch name
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q v nic"`;
    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q v nic\"", $hcp, "getMacs", $out, $node );
    if ($rc != 0) {
        xCAT::zvmUtils->printLn( $callback, "$outmsg" );
        return;
    }
    $out = `echo "$out" | egrep -a -i "VSWITCH|LAN"`;
    my @lines = split( '\n', $out );
    my @words;

    # Go through each line and extract VSwitch and Lan names
    # and create search string
    my $searchStr = "";
    my $i;
    for ( $i = 0 ; $i < @lines ; $i++ ) {

        # Extract VSwitch name
        if ( $lines[$i] =~ m/VSWITCH/i ) {
            @words = split( ' ', $lines[$i] );
            $searchStr = $searchStr . "$words[4]";
        }

        # Extract Lan name
        elsif ( $lines[$i] =~ m/LAN/i ) {
            @words = split( ' ', $lines[$i] );
            $searchStr = $searchStr . "$words[4]";
        }

        if ( $i != ( @lines - 1 ) ) {
            $searchStr = $searchStr . "|";
        }
    }

    # Get MAC address of node
    # This node should be on only 1 of the networks that the xCAT MN is on
    #$out = `ssh -o ConnectTimeout=5 $::SUDOER\@$node "/sbin/vmcp q v nic" | egrep -a -i "$searchStr"`;
    my $cmd = $::SUDO . ' /sbin/vmcp q v nic | egrep -a -i "' . $searchStr . '"';
    $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
        return;
    }

    if ( !$out ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find MAC address" );
        return;
    }

    @lines = split( '\n', $out );
    @words = split( ' ',  $lines[0] );
    $mac   = $words[1];

    # Replace - with :
    $mac = xCAT::zvmUtils->replaceStr( $mac, "-", ":" );
    xCAT::zvmUtils->printLn( $callback, "$node: $mac" );

    # Save MAC address and network interface into 'mac' table
    xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac );

    return;
}

#-------------------------------------------------------

=head3   netBoot

    Description : Boot from network
    Arguments   :   Node
                    Address to IPL from
    Returns     : Nothing
    Example     : netBoot($callback, $node, $args);

=cut

#-------------------------------------------------------
sub netBoot {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    # Get IPL
    my @ipl = split( '=', $args->[0] );
    if ( !( $ipl[0] eq "ipl" ) ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IPL" );
        return;
    }

    # Boot node
    my $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $userId"`;
    xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $userId");
    xCAT::zvmUtils->printSyslog("$out");

    # IPL when virtual server is online
    sleep(5);
    $out = xCAT::zvmCPUtils->sendCPCmd( $::SUDOER, $hcp, $userId, "IPL $ipl[1]" );
    xCAT::zvmUtils->printSyslog("IPL $ipl[1]");
    xCAT::zvmUtils->printSyslog("$out");
    xCAT::zvmUtils->printLn( $callback, "$node: Booting from $ipl[1]... Done" );

    return;
}

#-------------------------------------------------------

=head3   updateNode (No longer supported)

    Description : Update node
    Arguments   : Node
                  Option
    Returns     : Nothing
    Example     : updateNode($callback, $node, $args);

=cut

#-------------------------------------------------------
sub updateNode {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    # Get install directory
    my @entries = xCAT::TableUtils->get_site_attribute("installdir");
    my $installDir = $entries[0];

    # Get host IP and hostname from /etc/hosts
    my $out      = `cat /etc/hosts | egrep -a -i "$node |$node."`;
    my @words    = split( ' ', $out );
    my $hostIP   = $words[0];
    my $hostname = $words[2];
    if (!($hostname =~ m/./i)) {
        $hostname = $words[1];
    }

    if ( !$hostIP || !$hostname ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" );
        xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the node's IP address is specified in the hosts table and then run makehosts" );
        return;
    }

    # Get first 3 octets of node IP (IPv4)
    @words = split( /\./, $hostIP );
    my $octets = "$words[0].$words[1].$words[2]";

    # Get networks in 'networks' table
    my $entries = xCAT::zvmUtils->getAllTabEntries('networks');

    # Go through each network
    my $network;
    foreach (@$entries) {

        # Get network
        $network = $_->{'net'};

        # If networks contains the first 3 octets of the node IP
        if ( $network =~ m/$octets/i ) {

            # Exit loop
            last;
        } else {
            $network = "";
        }
    }

    # If no network found
    if ( !$network ) {

        # Exit
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any network in the networks table" );
        xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the subnet in the networks table. The mask, gateway, tftpserver, and nameservers must be specified for the subnet." );
        return;
    }

    # Get FTP server
    @propNames = ('tftpserver');
    $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames );
    my $nfs = $propVals->{'tftpserver'};
    if ( !$nfs ) {

        # It is acceptable to not have a gateway
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing FTP server" );
        xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the tftpserver for the subnet in the networks table" );
        return;
    }

    # Update node operating system
    if ( $args->[0] eq "--release" ) {
        my $version = $args->[1];

        if ( !$version ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system release. Please specify one." );
            return;
        }

        # Get node operating system
        my $os = xCAT::zvmUtils->getOs($::SUDOER, $node);

        # Check node OS is the same as the version OS given
        # You do not want to update a SLES with a RHEL
        if ( ( ( $os =~ m/SUSE/i ) && !( $version =~ m/sles/i ) ) || ( ( $os =~ m/Red Hat/i ) && !( $version =~ m/rhel/i ) ) ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node operating system is different from the operating system given to upgrade to. Please correct." );
            return;
        }

        # Generate FTP path to operating system image
        my $path;
        if ( $version =~ m/sles/i ) {

            # The following only applies to SLES 10
            # SLES 11 requires zypper

            # SuSE Enterprise Linux path - ftp://10.0.0.1/sles10.3/s390x/1/
            $path = "http://$nfs/install/$version/s390x/1/";

            # Add installation source using rug
            #$out = `ssh $::SUDOER\@$node "rug sa -t zypp $path $version"`;
            my $cmd = "$::SUDO rug sa -t zypp $path $version";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            xCAT::zvmUtils->printLn( $callback, "$node: $out" );

            # Subscribe to catalog
            #$out = `ssh $::SUDOER\@$node "rug sub $version"`;
            $cmd = "$::SUDO rug sub $version";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            xCAT::zvmUtils->printLn( $callback, "$node: $out" );

            # Refresh services
            #$out = `ssh $::SUDOER\@$node "rug ref"`;
            $cmd = "$::SUDO rug ref";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            xCAT::zvmUtils->printLn( $callback, "$node: $out" );

            # Update
            #$out = `ssh $::SUDOER\@$node "rug up -y"`;
            $cmd = "$::SUDO rug up -y";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        } else {

            # Red Hat Enterprise Linux path - ftp://10.0.0.1/rhel5.4/s390x/Server/
            $path = "http://$nfs/install/$version/s390x/Server/";

            # Check if file.repo already has this repository location
            #$out = `ssh $::SUDOER\@$node "cat /etc/yum.repos.d/file.repo"`;
            my $cmd = "$::SUDO cat /etc/yum.repos.d/file.repo";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            if ( $out =~ m/[$version]/i ) {

                # Send over release key
                my $key = "$installDir/$version/s390x/RPM-GPG-KEY-redhat-release";
                my $tmp = "/tmp/RPM-GPG-KEY-redhat-release";
                xCAT::zvmUtils->sendFile( $::SUDOER, $node, $key, $tmp );

                # Import key
                #$out = `ssh $::SUDOER\@$node "rpm --import /tmp/$key"`;
                $cmd = "$::SUDO rpm --import /tmp/$key";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    return;
                }


                # Upgrade
                #$out = `ssh $::SUDOER\@$node "yum upgrade -y"`;
                $cmd = "$::SUDO yum upgrade -y";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    return;
                }

                xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            } else {

                # Create repository
                $out =  xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo [$version] >> /etc/yum.repos.d/file.repo");
                $out =  xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo baseurl=$path >> /etc/yum.repos.d/file.repo");
                $out =  xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo enabled=1 >> /etc/yum.repos.d/file.repo");

                # Send over release key
                my $key = "$installDir/$version/s390x/RPM-GPG-KEY-redhat-release";
                my $tmp = "/tmp/RPM-GPG-KEY-redhat-release";
                xCAT::zvmUtils->sendFile( $::SUDOER, $node, $key, $tmp );

                # Import key
                #$out = `ssh $::SUDOER\@$node "rpm --import $tmp"`;
                my $cmd = "$::SUDO rpm --import $tmp";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    return;
                }


                # Upgrade
                #$out = `ssh $::SUDOER\@$node "yum upgrade -y"`;
                $cmd = "$::SUDO yum upgrade -y";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    return;
                }

                xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            }
        }
    }

    # Otherwise, print out error
    else {
        $out = "$node: (Error) Option not supported";
    }

    xCAT::zvmUtils->printLn( $callback, "$out" );
    return;
}

#-------------------------------------------------------

=head3   listTree

    Description : Show the nodes hierarchy tree
    Arguments   : Node range (zHCP)
    Returns     : Nothing
    Example     : listHierarchy($callback, $nodes, $args);

=cut

#-------------------------------------------------------
sub listTree {

    # Get inputs
    my ( $callback, $nodes, $args ) = @_;
    my @nodes = @$nodes;

    # Directory where executables are on zHCP
    $::DIR = "/opt/zhcp/bin";

    # Use sudo or not
    # This looks in the passwd table for a key = sudoer
    ($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer();

    # In order for this command to work, issue under /opt/xcat/bin:
    # ln -s /opt/xcat/bin/xcatclient lstree

    my %tree;
    my $node;
    my $hcp;
    my $parent;
    my %ssi = {};
    my $found;

    # Create hierachy structure: CEC -> LPAR -> zVM -> VM
    # Get table
    my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 );

    # Get CEC entries
    # There should be few of these nodes
    my @entries = $tab->getAllAttribsWhere( "nodetype = 'cec'", 'node', 'parent' );
    foreach (@entries) {
        $node = $_->{'node'};

        # Make CEC the tree root
        $tree{$node} = {};
    }

    # Get LPAR entries
    # There should be a couple of these nodes
    @entries = $tab->getAllAttribsWhere( "nodetype = 'lpar'", 'node', 'parent' );
    foreach (@entries) {
        $node = $_->{'node'};        # LPAR
        $parent = $_->{'parent'};    # CEC

        # Add LPAR branch
        $tree{$parent}{$node} = {};
    }

    # Get zVM entries
    # There should be a couple of these nodes
    $found = 0;
    @entries = $tab->getAllAttribsWhere( "nodetype = 'zvm'", 'node', 'hcp', 'parent' );
    foreach (@entries) {
        $node = $_->{'node'};        # zVM
        $hcp = $_->{'hcp'};          # zHCP
        $parent = $_->{'parent'};    # LPAR

        # Find out if this z/VM belongs to an SSI cluster
        $ssi{$node} = xCAT::zvmUtils->querySSI($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $ssi{$node} ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$ssi{$node}" );
            return;
        }

        # Find CEC root based on LPAR
        # CEC -> LPAR
        $found = 0;
        foreach my $cec(sort keys %tree) {
            foreach my $lpar(sort keys %{$tree{$cec}}) {
                if ($lpar eq $parent) {
                    # Add LPAR branch
                    $tree{$cec}{$parent}{$node} = {};
                    $found = 1;
                    last;
                }

                # Handle second level zVM
                foreach my $vm(sort keys %{$tree{$cec}{$lpar}}) {
                    if ($vm eq $parent) {
                        # Add VM branch
                        $tree{$cec}{$lpar}{$parent}{$node} = {};
                        $found = 1;
                        last;
                    }
                } # End of foreach zVM
            } # End of foreach LPAR

            # Exit loop if LPAR branch added
            if ($found) {
                last;
            }
        } # End of foreach CEC
    }

    # Get VM entries
    # There should be many of these nodes
    $found = 0;
    @entries = $tab->getAllAttribsWhere( "nodetype = 'vm'", 'node', 'parent', 'userid' );
    foreach (@entries) {
        $node = $_->{'node'};        # VM
        $parent = $_->{'parent'};    # zVM

        # Skip node if it is not in noderange
        if (!xCAT::zvmUtils->inArray($node, @nodes)) {
            next;
        }

        # Find CEC/LPAR root based on zVM
        # CEC -> LPAR -> zVM
        $found = 0;
        foreach my $cec(sort keys %tree) {
            foreach my $lpar(sort keys %{$tree{$cec}}) {
                foreach my $zvm(sort keys %{$tree{$cec}{$lpar}}) {
                    if ($zvm eq $parent) {
                        # Add zVM branch
                        $tree{$cec}{$lpar}{$parent}{$node} = $_->{'userid'};
                        $found = 1;
                        last;
                    }

                    # Handle second level zVM
                    foreach my $vm(sort keys %{$tree{$cec}{$lpar}{$zvm}}) {
                        if ($vm eq $parent) {
                            # Add VM branch
                            $tree{$cec}{$lpar}{$zvm}{$parent}{$node} = $_->{'userid'};
                            $found = 1;
                            last;
                        }
                    } # End of foreach VM
                } # End of foreach zVM

                # Exit loop if zVM branch added
                if ($found) {
                    last;
                }
            } # End of foreach LPAR

            # Exit loop if zVM branch added
            if ($found) {
                last;
            }
        } # End of foreach CEC
    } # End of foreach VM node

    # Print tree
    # Loop through CECs
    foreach my $cec(sort keys %tree) {
        xCAT::zvmUtils->printLn( $callback, "CEC: $cec" );

        # Loop through LPARs
        foreach my $lpar(sort keys %{$tree{$cec}}) {
            xCAT::zvmUtils->printLn( $callback, "|__LPAR: $lpar" );

            # Loop through zVMs
            foreach my $zvm(sort keys %{$tree{$cec}{$lpar}}) {
                if ($ssi{$zvm}) {
                    xCAT::zvmUtils->printLn( $callback, "   |__zVM: $zvm ($ssi{$zvm})" );
                } else {
                    xCAT::zvmUtils->printLn( $callback, "   |__zVM: $zvm" );
                }

                # Loop through VMs
                foreach my $vm(sort keys %{$tree{$cec}{$lpar}{$zvm}}) {
                    # Handle second level zVM
                    if (ref($tree{$cec}{$lpar}{$zvm}{$vm}) eq 'HASH') {
                        if ($ssi{$zvm}) {
                            xCAT::zvmUtils->printLn( $callback, "      |__zVM: $vm ($ssi{$zvm})" );
                        } else {
                            xCAT::zvmUtils->printLn( $callback, "      |__zVM: $vm" );
                        }

                        foreach my $vm2(sort keys %{$tree{$cec}{$lpar}{$zvm}{$vm}}) {
                            xCAT::zvmUtils->printLn( $callback, "         |__VM: $vm2 ($tree{$cec}{$lpar}{$zvm}{$vm}{$vm2})" );
                        }
                    } else {
                        xCAT::zvmUtils->printLn( $callback, "      |__VM: $vm ($tree{$cec}{$lpar}{$zvm}{$vm})" );
                    }
                } # End of foreach VM
            } # End of foreach zVM
        } # End of foreach LPAR
    } # End of foreach CEC
    return;
}

#-------------------------------------------------------

=head3   changeHypervisor

    Description : Configure the virtualization hosts
    Arguments   :   Node
                    Arguments
    Returns     : Nothing, errors returned in $callback
    Example     : changeHypervisor($callback, $node, $args);

=cut

#-------------------------------------------------------
sub changeHypervisor {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get zHCP shortname because $hcp could be zhcp.endicott.ibm.com
    my $hcpNode = $hcp;
    if ($hcp =~ /./) {
        my @tmp = split(/\./, $hcp);
        $hcpNode = $tmp[0];  # Short hostname of zHCP
    }

    # Get zHCP user ID
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp);
    $hcpUserId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    # Output string
    my $out = "";
    my $outmsg;
    my $rc;

    # adddisk2pool [function] [region] [volume] [group]
    if ( $args->[0] eq "--adddisk2pool" ) {
        my $funct   = $args->[1];
        my $region  = $args->[2];
        my $volume  = "";
        my $group   = "";

        # Create an array for regions
        my @regions;
        if ( $region =~ m/,/i ) {
            @regions = split( ',', $region );
        } else {
            push( @regions, $region );
        }

        my $tmp;
        foreach (@regions) {
            if (!(length $_)) {next;}
            $_ = xCAT::zvmUtils->trimStr($_);

            # Define region as full volume and add to group
            if ($funct eq "4") {
                $volume = $args->[3];
                # In case multiple regions/volumes are specified, just use the same name
                if (scalar(@regions) > 1) {
                    $volume = $_;
                }

                $group  = $args->[4];
                $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -v $volume -p $group -y 0"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -v $volume -p $group -y 0");
            }

            # Add existing region to group
            elsif($funct eq "5") {
                $group = $args->[3];
                $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -p $group -y 0"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -p $group -y 0");
            }

            $out .= $tmp;
        }
    }

    # addvolume [dev_no] [volser]
    elsif ( $args->[0] eq "--addvolume" ) {
        my $argsSize = @{$args};
        if ($argsSize != 3) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = $args->[1];
        my $volser = $args->[2];

        # Add a DASD volume to the z/VM system configuration
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Add -T $hcpUserId -v $devNo -l $volser"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Volume_Add -T $hcpUserId -v $devNo -l $volser");
    }

    # removevolume [dev_no] [volser]
    elsif ( $args->[0] eq "--removevolume" ) {
        my $argsSize = @{$args};
        if ($argsSize != 3) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = $args->[1];
        my $volser = $args->[2];

        # Remove a DASD volume from the z/VM system configuration
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Delete -T $hcpUserId -v $devNo -l $volser"`;
        xCAT::zvmUtils->printSyslog("smcli Image_Volume_Delete -T $hcpUserId -v $devNo -l $volser");
    }

    # addeckd [dev_no]
    elsif ( $args->[0] eq "--addeckd" ) {
        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = "dev_num=" . $args->[1];

        # Add an ECKD disk to a running z/VM system
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Disk_Add -T $hcpUserId -k $devNo"`;
        xCAT::zvmUtils->printSyslog("smcli System_Disk_Add -T $hcpUserId -k $devNo");
    }

    # addscsi [dev_no] [dev_path] [option] [persist]
    elsif ( $args->[0] eq "--addscsi" ) {
        # Sample command would look like: chhypervisor zvm62 --addscsi 12A3 "1,0x123,0x100;2,0x123,0x101" 1 NO
        my $argsSize = @{$args};
        if ($argsSize < 3 && $argsSize > 5) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # Option can be: (1) Add new SCSI (default), (2) Add new path, or (3) Delete path
        if ($args->[3] != 1 && $args->[3] !=2 && $args->[3] !=3) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Options can be one of the following:\n  (1) Add new SCSI disk (default)\n  (2) Add new path to existing disk\n  (3) Delete path from existing disk" );
            return;
        }

        # Persist can be: (YES) SCSI device updated in active and configured system, or (NO) SCSI device updated only in active system
        if ($argsSize > 3 && $args->[4] ne "YES" && $args->[4] ne "NO") {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Persist can be one of the following:\n  (YES) SCSI device updated in active and configured system\n  (NO) SCSI device updated only in active system" );
            return;
        }

        my $devNo = "dev_num=" . $args->[1];

        # Device path array, each device separated by semi-colon
        # e.g. fcp_devno1 fcp_wwpn1 fcp_lun1; fcp_devno2 fcp_wwpn2 fcp_lun2;
        my @fcps;
        if ($args->[2] =~ m/;/i) {
            @fcps = split( ';', $args->[2] );
        } else {
            push( @fcps, $args->[2] );
        }

        # Append the correct prefix
        my @fields;
        my $pathStr = "";
        foreach (@fcps) {
            @fields = split( ',', $_ );
            $pathStr .= "$fields[0] $fields[1] $fields[2];";
        }


        my $devPath = "dev_path_array='" . $pathStr . "'";

        my $option = "option=" . $args->[3];
        my $persist = "persist=" . $args->[4];

        # Add disk to running system
        $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_SCSI_Disk_Add -T $hcpUserId -k $devNo -k $devPath -k $option -k $persist"`;
        xCAT::zvmUtils->printSyslog("smcli System_SCSI_Disk_Add -T $hcpUserId -k $devNo -k $devPath -k $option -k $persist");
    }

    # addvlan [name] [owner] [type] [transport]
    elsif ( $args->[0] eq "--addvlan" ) {
        my $name = $args->[1];
        my $owner = $args->[2];
        my $type = $args->[3];
        my $transport = $args->[4];

        my $argsSize = @{$args};
        if ($argsSize != 5) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_LAN_Create -T $hcpUserId -n $name -o $owner -t $type -p $transport"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_LAN_Create -T $hcpUserId -n $name -o $owner -t $type -p $transport");
    }

    # addvswitch [name] [osa_dev_addr] [port_name] [controller] [connect (0, 1, or 2)] [memory_queue] [router] [transport] [vlan_id] [port_type] [update] [gvrp] [native_vlan]
    elsif ( $args->[0] eq "--addvswitch" ) {
        my $i;
        my $argStr = "";

        my $argsSize = @{$args};
        if ($argsSize < 5) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my @options = ("", "-n", "-r", "-a", "-i", "-c", "-q", "-e", "-t", "-v", "-p", "-u", "-G", "-V");
        foreach $i ( 1 .. $argsSize ) {
            if ( $args->[$i] ) {
                # Prepend options prefix to argument
                $argStr .= "$options[$i] $args->[$i] ";
            }
        }

        $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Vswitch_Create -T $hcpUserId $argStr"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Vswitch_Create -T $hcpUserId $argStr");
    }

    # addzfcp2pool [pool] [status] [wwpn] [lun] [size] [range (optional)] [owner (optional)]
    elsif ( $args->[0] eq "--addzfcp2pool" ) {
        # zFCP disk pool located on zHCP at /var/opt/zhcp/zfcp/{pool}.conf
        # Entries contain: status,wwpn,lun,size,range,owner,channel,tag
        # store pool file in lower case
        my $pool  = lc($args->[1]);
        my $status = $args->[2];
        my $wwpn = $args->[3];
        my $lun = $args->[4];
        my $size = $args->[5];

        my $argsSize = @{$args};
        if ($argsSize < 6 || $argsSize > 8) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # Size can be M(egabytes) or G(igabytes)
        if ($size =~ m/G/i || $size =~ m/M/i || !$size) {
            # Do nothing
        } else {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes).");
            return;
        }

        # Status can be free/used/reserved
        chomp($status);
        if ($status !~ m/^(free|used|reserved)$/i) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Status not recognized. Status can be free, used, or reserved.");
            return;
        }

        # Make sure WWPN and LUN do not have 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr($wwpn, '"', "");  # Strip off enclosing quotes
        $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
        $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

        # Validate wwpn and lun values.
        # The pattern '[0-9a-f]{16}' means 16 characters in char-set [0-9a-f]. The pattern '(;[0-9a-f]{16})'
        # in last half part is used in the case of multipath. It will not appear in the case of signal path
        # so * is used to handle both cases.
        if ($wwpn !~ m/^[0-9a-f]{16}(;[0-9a-f]{16})*$/i) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid world wide portname $wwpn." );
            return;
        }
        if ($lun !~ m/^[0-9a-f]{16}$/i) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid logical unit number $lun." );
            return;
        }

        # You cannot have a unique SCSI/FCP device in multiple pools
        my @wwpnList = split(";", $wwpn);
        foreach (@wwpnList) {
            my $cur_wwpn = $_;
            my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO grep -a -i -l \",$cur_wwpn,$lun\" $::ZFCPPOOL/*.conf"`);
            if (scalar(@pools)) {
                foreach (@pools) {
                    my $cur_pool = $_;
                    if (!(length $cur_pool)) {next;}
                    my $otherPool = basename($cur_pool);
                    $otherPool =~ s/\.[^.]+$//;  # Do not use extension
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $cur_wwpn/$lun already exists in $otherPool." );
                }
                return;
            }
        }

        # Optional parameters
        my $range = "";
        my $owner = "";
        if ($argsSize > 6) {
            $range = $args->[6];
        } if ($argsSize > 7) {
            $owner = $args->[7];
        }

        # Verify syntax of FCP channel range
        if ($range =~ /[^0-9a-f\-;]/i) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid FCP device range. An acceptable range can be specified as 1A80-1B90 or 1A80-1B90;2A80-2B90." );
            return;
        }

        # Owner must be specified if status is used
        if ($status =~ m/used/i && !$owner) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Owner must be specified if status is used." );
            return;
        } elsif ($status =~ m/free/i && $owner) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Owner must not be specified if status is free." );
            return;
        }

        # Find disk pool (create one if non-existent)
        if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -d $::ZFCPPOOL && echo Exists"`)) {
            # Create pool directory
            $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $::ZFCPPOOL"`;
        }

        # Change the file owner if using a sudoer
        if ($::SUDOER ne "root") {
            my $priv = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/stat -c \"%G:%U\" /var/opt/zhcp"`);
            if (!($priv =~ m/$::SUDOER:users/i)) {
                `ssh $::SUDOER\@$hcp "$::SUDO chown -R $::SUDOER:users /var/opt/zhcp"`;
            }
        }

        if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -e $::ZFCPPOOL/$pool.conf && echo Exists"`)) {
            # Create pool configuration file
            $out = `ssh $::SUDOER\@$hcp "$::SUDO echo '#status,wwpn,lun,size,range,owner,channel,tag' > $::ZFCPPOOL/$pool.conf"`;
            xCAT::zvmUtils->printLn( $callback, "$node: New zFCP device pool $pool created" );
        }

        # Update file with given WWPN, LUN, size, and owner
        my $entry = "'" . "$status,$wwpn,$lun,$size,$range,$owner,," . "'";
        $out = `ssh $::SUDOER\@$hcp "$::SUDO echo $entry >> $::ZFCPPOOL/$pool.conf"`;
        xCAT::zvmUtils->printLn( $callback, "$node: Adding zFCP device to $pool pool... Done" );
        $out = "";
    }

    # copyzfcp [device address (or auto)] [source wwpn] [source lun] [target wwpn (optional)] [target lun (option)]
    elsif ( $args->[0] eq "--copyzfcp" ) {
        my $fcpDevice = $args->[1];
        my $srcWwpn = $args->[2];
        my $srcLun = $args->[3];

        my $argsSize = @{$args};
        if ($argsSize != 4 && $argsSize != 6) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # Check if WWPN and LUN are given
        my $useWwpnLun = 0;
        my $tgtWwpn;
        my $tgtLun;
        if ($argsSize == 6) {
            $useWwpnLun = 1;
            $tgtWwpn = $args->[4];
            $tgtLun = $args->[5];

            # Make sure WWPN and LUN do not have 0x prefix
            $tgtWwpn = xCAT::zvmUtils->replaceStr($tgtWwpn, "0x", "");
            $tgtLun = xCAT::zvmUtils->replaceStr($tgtLun, "0x", "");
        }

        # Find the pool that contains the SCSI/FCP device
        my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $srcWwpn, $srcLun);
        if (!$pool) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool" );
            return;
        } else {
            if (xCAT::zvmUtils->checkOutput( $pool ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$pool" );
                return;
            }
            xCAT::zvmUtils->printLn( $callback, "$node: Found FCP device in $pool" );
        }

        # Get source device's attributes
        my $srcDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $srcWwpn, $srcLun);
        if (xCAT::zvmUtils->checkOutput( $srcDiskRef ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$srcDiskRef" );
            return;
        }
        my %srcDisk = %$srcDiskRef;
        if (!defined($srcDisk{'lun'}) && !$srcDisk{'lun'}) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Source zFCP device $srcWwpn/$srcLun does not exists" );
            return;
        }
        my $srcSize = $srcDisk{'size'};

        # If target disk is specified, check whether it is large enough
        my $tgtSize;
        if ($useWwpnLun) {
            my $tgtDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $tgtWwpn, $tgtLun);
            if (xCAT::zvmUtils->checkOutput( $tgtDiskRef ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$tgtDiskRef" );
                return;
            }
            my %tgtDisk = %$tgtDiskRef;
            if (!defined($tgtDisk{'lun'}) && !$tgtDisk{'lun'}) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun does not exists" );
                return;
            }
            $tgtSize = $tgtDisk{'size'};

            # Convert size unit to M for comparision
            if ($srcSize =~ m/G/i) {
                $srcSize =~ s/\D//g;
                $srcSize = int($srcSize) * 1024
            }
            if ($tgtSize =~ m/G/i) {
                $tgtSize =~ s/\D//g;
                $tgtSize = int($srcSize) * 1024
            }

            if ($tgtSize < $srcSize) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun is not large enough" );
                return;
            }
        }

        # Attach source disk to zHCP
        $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $srcSize "" $srcWwpn $srcLun | sed 1d`;
        if ($out !~ /Done/) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Source zFCP device $srcWwpn/$srcLun cannot be attached");
            return;
        }

        # Obtain source FCP channel
        $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/i;
        my $srcFcpDevice = lc($1);

        # Attach target disk to zHCP
        my $isTgtAttached = 0;
        if ($useWwpnLun) {
            $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $tgtSize "" $tgtWwpn $tgtLun | sed 1d `;
            if ($out !~ /Done/) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun cannot be attached");
            } else {
                $isTgtAttached = 1;
            }
        } else {
            # Try to obtain a target disk automatically if target disk is not specified
            $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $srcSize | sed 1d`;
            if ($out !~ /Done/) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Cannot find a suitable target zFCP device");
            } else {
                $isTgtAttached = 1;
            }
        }

        # Obtain target disk FCP channel, WWPN, and LUN
        $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/i;
        my $tgtFcpDevice = lc($1);
        $tgtWwpn = lc($2);
        $tgtLun = lc($3);

        if (!$isTgtAttached) {
            # Release source disk from zHCP
            $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $fcpDevice $srcWwpn $srcLun 0`;
            return;
        }

        # Get device node of source disk and target disk
        ($srcWwpn, $srcLun, $tgtWwpn, $tgtLun) = (lc($srcWwpn), lc($srcLun), lc($tgtWwpn), lc($tgtLun));
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/readlink /dev/disk/by-path/ccw-0.0.$srcFcpDevice-zfcp-0x$srcWwpn:0x$srcLun"`;
        chomp($out);
        my @srcDiskInfo = split('/', $out);
        my $srcDiskNode = pop(@srcDiskInfo);
        chomp($out);
        xCAT::zvmUtils->printLn( $callback, "$node: Device name of $tgtFcpDevice/$srcWwpn/$srcLun is $srcDiskNode");

        $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/readlink /dev/disk/by-path/ccw-0.0.$tgtFcpDevice-zfcp-0x$tgtWwpn:0x$tgtLun"`;
        chomp($out);
        my @tgtDiskInfo = split('/', $out);
        my $tgtDiskNode = pop(@tgtDiskInfo);
        chomp($tgtDiskNode);
        xCAT::zvmUtils->printLn( $callback, "$node: Device name of $tgtFcpDevice/$tgtWwpn/$tgtLun is $tgtDiskNode");

        my $presist = 0;
        my $rc = "Failed";
        if (!$srcDiskNode || !$tgtDiskNode) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Could not find device nodes for source or target disk.");
        } else {
            # Copy source disk to target disk (512 block size)
            xCAT::zvmUtils->printLn( $callback, "$node: Copying source disk ($srcDiskNode) to target disk ($tgtDiskNode)" );
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDiskNode of=/dev/$tgtDiskNode bs=512 oflag=sync && $::SUDO echo $?"`;
            $out = xCAT::zvmUtils->trimStr($out);
            if (int($out) != 0) {
                # If $? is not 0 then there was an error during Linux dd
                xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to copy /dev/$srcDiskNode");
            }

            $presist = 1;  # Keep target device as reserved
            $rc = "Done";

            # Sleep 2 seconds to let the system settle
            sleep(2);
        }

        # Detatch source and target disks
        xCAT::zvmUtils->printLn($callback, "$node: Detatching source and target disks");
        $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $srcFcpDevice $srcWwpn $srcLun $presist`;
        $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $tgtFcpDevice $tgtWwpn $tgtLun $presist`;

        # Restore original source device attributes
        my %criteria = (
            'status' => $srcDisk{'status'},
            'wwpn' => $srcDisk{'wwpn'},
            'lun' => $srcDisk{'lun'},
            'size' => $srcDisk{'size'},
            'range' => $srcDisk{'range'},
            'owner' => $srcDisk{'owner'},
            'fcp' => $srcDisk{'fcp'},
            'tag' => $srcDisk{'tag'}
        );
        my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        my %results = %$resultsRef;
        if ($results{'rc'} == -1) {
            # Unable to reserve the volume and FCP channel
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Source disk attributes cannot be restored in table");
        }

        xCAT::zvmUtils->printLn( $callback, "$node: Copying zFCP device... $rc");
        if ($rc eq "Done") {
            xCAT::zvmUtils->printLn( $callback, "$node: Source disk copied onto zFCP device $tgtWwpn/$tgtLun");
        }
        $out = "";
    }

    # capturezfcp [profile] [wwpn] [lun] [compression]
    elsif ( $args->[0] eq "--capturezfcp" ) {
        my $out;
        my $rc;
        my $compParm = '';
        my $argsSize = @{$args};
        if ( $argsSize < 4 ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $profile  = $args->[1];
        my $wwpn = $args->[2];
        my $lun  = $args->[3];
        if ( $argsSize >= 5 ) {
            # Set the compression invocation parameter if compression was specified.
            # Note: Some older zHCP do not support compression specification.

            # Determine if zHCP supports the compression property.
            $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/creatediskimage -V"`;
            $rc = $?;

            if ( $rc == 65280 ) {
                xCAT::zvmUtils->printSyslog( "changeHypervisor() Unable to communicate with zHCP agent" );
                xCAT::zvmUtils->printLn( $callback, "$node: changeHypervisor() is unable to communicate with zHCP agent: $hcp" );
                return;
            }

            $rc = xCAT::zvmUtils->checkOutput( $out );
            if ( $rc != -1 ) {
                # No error.  It is probably that the zHCP supports compression.
                # We will check the version to see if it is high enough.  Any error
                # or too low of a version means that we should ignore the compression
                # operand in the future creatediskimage call.
                # Process the version output.
                my @outLn = split("\n", $out);
                if ( $#outLn == 0 ) {
                    # Only a single line of output should come back from a compatable zHCP.
                    my @versionInfo = split( '\.', $out );
                    if ( $versionInfo[0] >= 2 ) {
                        # zHCP supports compression specification.
                        if (( $args->[4] =~ /[\d]/ ) and ( length($args->[4]) == 1 )) {
                            $compParm = "--compression $args->[4]";
                        } else {
                            xCAT::zvmUtils->printLn( $callback, "$node: (Error) compression property is not a single digit from 0 to 9" );
                            return;
                        }
                    }
                }
            }
        }

        # Verify required properties are defined
        if (!defined($profile) || !defined($wwpn) || !defined($lun)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more of the required parameters: profile, wwpn, or lun" );
            return;
        }

        # Make sure WWPN and LUN do not have 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
        $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

        # Obtain the location of the install root directory
        my $installRoot = xCAT::TableUtils->getInstallDir();

        xCAT::zvmUtils->printSyslog("changeHypervisor() Preparing the staging directory");

        # Create the staging area location for the image
        my $os = "unknown";  # Since we do not inspect the disk contents nor care
        my $provMethod = "raw";
        my $arch = "s390x";
        my $stagingImgDir = "$installRoot/staging/$os/$arch/$profile";

        if(-d $stagingImgDir) {
            rmtree $stagingImgDir;
        }
        mkpath($stagingImgDir);

        # Prepare the staging mount point on zHCP, if they need to be established.
        my $remoteStagingDir;
        $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, $installRoot, "staging", "rw", \$remoteStagingDir);
        if ($rc) {
            # Mount failed.
            rmtree "$stagingImgDir";
            return;
        }

        # Find the pool that contains the SCSI/FCP device
        my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun);
        if (!$pool) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool");
            return;
        } else {
            if (xCAT::zvmUtils->checkOutput( $pool ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$pool" );
                return;
            }
            xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool");
        }

        # Get source device's attributes
        my $srcDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun);
        if (xCAT::zvmUtils->checkOutput( $srcDiskRef ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$srcDiskRef" );
            return;
        }
        my %srcDisk = %$srcDiskRef;
        if (!defined($srcDisk{'lun'}) && !$srcDisk{'lun'}) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Source zFCP device $wwpn/$lun does not exists");
            return;
        }

        # Reserve the volume and associated FCP channel for the zHCP node
        my %criteria = (
           'status' => 'used',
           'fcp' => 'auto',
           'wwpn' => $wwpn,
           'lun' => $lun,
           'owner' => $hcpNode
        );
        my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        my %results = %$resultsRef;

        my $device = $results{'fcp'};
        $wwpn = $results{'wwpn'};
        $lun = $results{'lun'};

        if ($results{'rc'} == -1) {
            # Unable to reserve the volume and FCP channel
            xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be reserved");
            rmtree "$stagingImgDir";
            return;
        }

        xCAT::zvmUtils->printLn($callback, "$node: Capturing volume using zHCP node");

        # Drive the capture on the zHCP node
        xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage $device 0x$wwpn/0x$lun $remoteStagingDir/$os/$arch/$profile/0x${wwpn}_0x${lun}.img $compParm");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/creatediskimage $device 0x$wwpn 0x$lun $remoteStagingDir/$os/$arch/$profile/${wwpn}_${lun}.img $compParm"`;
        $rc = $?;

        # Check for capture errors
        my $reasonString = "";
        $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString );
        if ($rc != 0) {
            my $reason = "Reason: $reasonString";
            xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage of volume 0x$wwpn/0x$lun failed. $reason");
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Image capture of volume 0x$wwpn/0x$lun failed on the zHCP node. $reason");
            rmtree "$stagingImgDir" ;
            return;
        }

        # Restore original source device attributes
        %criteria = (
            'status' => $srcDisk{'status'},
            'wwpn' => $srcDisk{'wwpn'},
            'lun' => $srcDisk{'lun'},
            'size' => $srcDisk{'size'},
            'range' => $srcDisk{'range'},
            'owner' => $srcDisk{'owner'},
            'fcp' => $srcDisk{'fcp'},
            'tag' => $srcDisk{'tag'}
        );
        $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        %results = %$resultsRef;
        if ($results{'rc'} == -1) {
            # Unable to reserve the volume and FCP channel
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Source disk attributes cannot be restored in table");
        }

        my $imageName = "$os-$arch-$provMethod-$profile";
        my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile";

        xCAT::zvmUtils->printLn($callback, "$node: Moving the image files to the deployable directory: $deployImgDir");

        # Move the image directory to the deploy directory
        mkpath($deployImgDir);

        my @stagedFiles = glob "$stagingImgDir/*";
        foreach my $oldFile (@stagedFiles) {
            move($oldFile, $deployImgDir) or die "$node: (Error) Could not move $oldFile to $deployImgDir: $!\n";
        }

        # Remove the staging directory
        rmtree "$stagingImgDir" ;

        xCAT::zvmUtils->printSyslog("changeHypervisor() Updating the osimage table");

        my $osTab = xCAT::Table->new('osimage',-create => 1,-autocommit => 0);
        my %keyHash;

        unless ($osTab) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to open table 'osimage'");
            return 0;
        }

        $keyHash{provmethod} = $provMethod;
        $keyHash{profile} = $profile;
        $keyHash{osvers} = $os;
        $keyHash{osarch} = $arch;
        $keyHash{imagetype} = 'linux';
        $keyHash{imagename} = $imageName;

        $osTab->setAttribs({imagename => $imageName }, \%keyHash);
        $osTab->commit;

        xCAT::zvmUtils->printSyslog("changeHypervisor() Updating the linuximage table");

        my $linuxTab = xCAT::Table->new('linuximage',-create => 1,-autocommit => 0);

        %keyHash = ();
        $keyHash{imagename} = $imageName;
        $keyHash{rootimgdir} = $deployImgDir;

        $linuxTab->setAttribs({imagename => $imageName }, \%keyHash );
        $linuxTab->commit;

        xCAT::zvmUtils->printLn($callback, "$node: Completed capturing the volume. Image($imageName) is stored at $deployImgDir");
        $out = "";
    }

    # deployzfcp [imageName] [wwpn] [lun]
    elsif ( $args->[0] eq "--deployzfcp" ) {
        my $imageName  = $args->[1];
        my $wwpn = $args->[2];
        my $lun  = $args->[3];

        # Verify required properties are defined
        if ( !defined($imageName) || !defined($wwpn) || !defined($lun)) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing one or more arguments: image name, wwpn, or lun");
            return;
        }

        # Make sure WWPN and LUN do not have 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
        $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

        # Obtain the location of the install root directory
        my $installRoot = xCAT::TableUtils->getInstallDir();

        # Build the image location from the image name
        my @nameParts = split('-', $imageName);
        if (!defined $nameParts[3]) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) The image name is not valid");
            return;
        }
        my $profile = $nameParts[3];
        my $os = "unknown";
        my $provMethod = "raw";
        my $arch = "s390x";

        my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile";

        # Find the image filename.
        my $imageFile;
        my @imageFiles = glob "$deployImgDir/*.img";
        if (@imageFiles == 0) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir does not contain image files");
            return;
        } elsif (@imageFiles > 1) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir contains more than the expected number of image files");
            return;
        } else {
            $imageFile = (split( '/', $imageFiles[0]))[-1];
        }

        # Prepare the deployable netboot mount point on zHCP, if they need to be established.
        my $remoteDeployDir;
        my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $provMethod, "ro", \$remoteDeployDir);
        if ($rc) {
            # Mount failed.
            return;
        }

        # Find the pool that contains the SCSI/FCP device
        my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun);
        if (!$pool) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool");
            return;
        } else {
            if (xCAT::zvmUtils->checkOutput( $pool ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$pool" );
                return;
            }
            xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool");
        }

        # Reserve the volume and associated FCP channel for the zHCP node.
        my %criteria = (
           'status' => 'used',
           'fcp' => 'auto',
           'wwpn' => $wwpn,
           'lun' => $lun,
           'owner' => $hcpNode
        );
        my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        my %results = %$resultsRef;

        # Obtain the device assigned by xCAT
        my $device = $results{'fcp'};
        $wwpn = $results{'wwpn'};
        $lun = $results{'lun'};

        if ($results{'rc'} == -1) {
            # Unable to reserve the volume and FCP channel
            xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be reserved");
            return;
        }

        xCAT::zvmUtils->printLn($callback, "$node: Deploying volume using zHCP node");

        # Drive the deploy on the zHCP node
        xCAT::zvmUtils->printSyslog("changeHypervisor() unpackdiskimage $device 0x$wwpn 0x$lun $remoteDeployDir/$os/$arch/$profile/$imageFile");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/unpackdiskimage $device 0x$wwpn 0x$lun $remoteDeployDir/$os/$arch/$profile/$imageFile"`;
        $rc = $?;

        # Release the volume from the zHCP node
        %criteria = (
           'status' => 'reserved',
           'wwpn' => $wwpn,
           'lun' => $lun
        );
        $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        if ($results{'rc'} == -1) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be released");
        }

        # Check for deploy errors
        my $reasonString = "";
        $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString );
        if ($rc != 0) {
            my $reason = "Reason: $reasonString";
            xCAT::zvmUtils->printSyslog("changeHypervisor() unpackdiskimage of volume 0x$wwpn/0x$lun failed. $reason");
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Image deploy to volume 0x$wwpn/0x$lun failed on the zHCP node. $reason");
            return;
        }

        xCAT::zvmUtils->printLn($callback, "$node: Completed deploying image($imageName)");
        $out = "";
    }

    # removediskfrompool [function] [region] [group]
    elsif ( $args->[0] eq "--removediskfrompool" ) {
        my $funct  = $args->[1];
        my $region = $args->[2];
        my $group  = "";

        # Create an array for regions
        my @regions;
        if ( $region =~ m/,/i ) {
            @regions = split( ',', $region );
        } else {
            push( @regions, $region );
        }

        my $tmp;
        foreach ( @regions ) {
            if (!(length $_)) {next;}
            $_ = xCAT::zvmUtils->trimStr($_);

            # Remove region from group | Remove entire group
            if ($funct eq "2" || $funct eq "7") {
                $group  = $args->[3];
                $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Remove_DM -T $hcpUserId -f $funct -r $_ -g $group"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Remove_DM -T $hcpUserId -f $funct -r $_ -g $group");
            }

            # Remove region | Remove region from all groups
            elsif ($funct eq "1" || $funct eq "3") {
                $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Remove_DM -T $hcpUserId -f $funct -r $_"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Remove_DM -T $hcpUserId -f $funct -r $_");
            }

            $out .= $tmp;
        }
    }

    # removescsi [device number] [persist (YES or NO)]
    elsif ( $args->[0] eq "--removescsi" ) {
        my $argsSize = @{$args};
        if ($argsSize != 3) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = "dev_num=" . $args->[1];
        my $persist = "persist=" . $args->[2];

        # Delete a real SCSI disk
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_SCSI_Disk_Delete -T $hcpUserId -k $devNo -k $persist"`;
        xCAT::zvmUtils->printSyslog("smcli System_SCSI_Disk_Delete -T $hcpUserId -k $devNo -k $persist");
    }

    # removevlan [name] [owner]
    elsif ( $args->[0] eq "--removevlan" ) {
        my $name = $args->[1];
        my $owner = $args->[2];

        # Delete a virtual network
        $out = `ssh $hcp "$::DIR/smcli Virtual_Network_LAN_Delete -T $hcpUserId -n $name -o $owner"`;
        xCAT::zvmUtils->printSyslog("ssh $hcp $::DIR/smcli Virtual_Network_LAN_Delete -T $hcpUserId -n $name -o $owner");
    }

    # removevswitch [name]
    elsif ( $args->[0] eq "--removevswitch" ) {
        my $name = $args->[1];

        # Delete a VSWITCH
        $out = `ssh $hcp "$::DIR/smcli Virtual_Network_Vswitch_Delete -T $hcpUserId -n $name"`;
        xCAT::zvmUtils->printSyslog("ssh $hcp $::DIR/smcli Virtual_Network_Vswitch_Delete -T $hcpUserId -n $name");
    }

    # removezfcpfrompool [pool] [lun] [wwpn]
    elsif ( $args->[0] eq "--removezfcpfrompool" ) {
        my $argsSize = @{$args};
        my $pool = $args->[1];
        my $lun = $args->[2];
        if ($argsSize < 4) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) WWPN is required." );
            return;
        }
        my $wwpn = $args->[3];
        if ($argsSize > 4) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # Make sure WWPN and LUN do not have 0x prefix
        $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", "");
        $lun = xCAT::zvmUtils->replaceStr($lun, "0x", "");

        # Verify WWPN and LUN have the correct syntax
        # The pattern '[0-9a-f]{16}' means 16 characters in char-set [0-9a-f]. The pattern '(;[0-9a-f]{16})'
        # in last half part is used in the case of multipath. It will not appear in the case of signal path
        # so * is used to handle both cases.
        if ($wwpn !~ m/^[0-9a-f]{16}(;[0-9a-f]{16})*$/i) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid world wide port name $wwpn." );
            return;
        }
        # The pattern '[0-9a-f]{16}' means 16 characters in char-set [0-9a-f]. The pattern '(,[0-9a-f]{16})'
        # in last half part is used to deal with lun list.
        if ($lun !~ m/^[0-9a-f]{16}(,[0-9a-f]{16})*$/i) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid logical unit number $lun." );
            return;
        }

        my @luns;
        if ($lun =~ m/,/i) {
            @luns = split( ',', $lun );
        } else {
            push(@luns, $lun);
        }

        # Find disk pool
        if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -e $::ZFCPPOOL/$pool.conf && echo Exists"`)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP pool does not exist" );
            return;
        }

        # Go through each LUN, look for matches of lun + wwpn (if specified)
        my $entry;
        my @args;
        foreach (@luns) {
            my $cur_lun = $_;
            if (!(length $cur_lun)) {next;}
            # Entry should contain: status, wwpn, lun, size, range, owner, channel, tag
            $entry =  `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf"`;
            ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$pool.conf\"", $hcp, "changeHypervisor", $entry, $node );
            if ($rc != 0) {
                xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                return;
            }
            $entry = `echo "$entry" | egrep -a -i "$cur_lun"`;
            # Do not update if LUN does not exists, stop checking other luns if this one not found
            if (!$entry) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $cur_lun does not exist" );
                return;
            }
            # process multiple lines if they exist with this lun
            my @lines = split("\n", $entry);
            my $foundit = 0;
            foreach (@lines) {
                my $fcpline = $_;
                $fcpline = xCAT::zvmUtils->trimStr($fcpline);
                if (!(length $fcpline)) {next;} # in case split causes an empty item

                # Skip if WWPN specified, and WWPN/LUN combo does not exists for this line
                @args = split(',', $fcpline);
                if ( (length $wwpn) && !($args[1] =~ m/$wwpn/i)) {
                    next;
                }

                # delete this line in the file with given WWPN and LUN
                $foundit = 1;
                $fcpline = "'" . $fcpline . "'";
                $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "sed -i -e /$fcpline/d $::ZFCPPOOL/$pool.conf");

                xCAT::zvmUtils->printLn( $callback, "$node: Removing zFCP device $wwpn/$cur_lun from $pool pool... Done" );

                # Check if pool is empty, if so delete the pool.conf (If empty it only contains a header line)
                $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "cat $::ZFCPPOOL/$pool.conf");
                my @linesLeft = split("\n",$out);
                my $lineCount = scalar(@linesLeft);
                my $emptyLines = `grep -cavP '\\S' $::ZFCPPOOL/$pool.conf`; # Count "empty" lines
                if ($lineCount <= 1 || $lineCount == $emptyLines) {
                    $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "rm -f $::ZFCPPOOL/$pool.conf");
                    xCAT::zvmUtils->printLn( $callback, "$node: Deleting empty zFCP $pool pool." );
                    return;
                }
            }
            if ($foundit == 0) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $wwpn/$cur_lun does not exist." );
                return;
            }
        }
        # clear any data left in out so it does not display on callback
        $out = "";
    }

    # releasezfcp [pool] [wwpn] [lun]
    elsif ( $args->[0] eq "--releasezfcp" ) {
        my $pool = lc($args->[1]);
        my $wwpn = lc($args->[2]);
        my $lun = lc($args->[3]);

        my $argsSize = @{$args};
        if ($argsSize != 4) {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters");
            return;
        }

        my $device = "";

        # In case multiple LUNs are given, push LUNs into an array to be processed
        my @luns;
        if ($lun =~ m/,/i) {
            @luns = split( ',', $lun );
        } else {
            push(@luns, $lun);
        }

        # Go through each LUN
        foreach (@luns) {
            if (!(length $_)) {next;}
            my %criteria = (
               'status' => 'free',
               'wwpn' => $wwpn,
               'lun' => $_
            );

            my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
            my %results = %$resultsRef;
            if ($results{'rc'} == 0) {
                xCAT::zvmUtils->printLn($callback, "$node: Releasing FCP device... Done");
                xCAT::zvmUtils->printLn($callback, "$node: FCP device 0x$wwpn/0x$_ was released");
            } else {
                xCAT::zvmUtils->printLn($callback, "$node: Releasing FCP device... Failed");
            }
        }
    }

    # reservezfcp [pool] [status] [owner] [device address (or auto)] [size] [wwpn (optional)] [lun (optional)]
    elsif ( $args->[0] eq "--reservezfcp" ) {
        my $pool = lc($args->[1]);
        my $status = $args->[2];
        my $owner = $args->[3];
        my $device = $args->[4];
        my $size = $args->[5];

        my $argsSize = @{$args};
        if ($argsSize != 6 && $argsSize != 8) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # status can be used or reserved but not free
        if ($status =~ m/^free$/i) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Status can be used or reserved but not free." );
            return;
        }

        # Obtain the FCP device, WWPN, and LUN (if any)
        my $wwpn = "";
        my $lun = "";
        if ($argsSize == 8) {
            $wwpn = lc($args->[6]);
            $lun = lc($args->[7]);

            # WWPN and LUN must both be specified or both not
            if ($wwpn xor $lun) {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) WWPN and LUN must both be specified or both not.");
                return;
            }

            # Ignore the size if the WWPN and LUN are given
            $size = "";
        }

        my %criteria;
        my $resultsRef;
        if ($wwpn && $lun) {
            # Check the status of the FCP device
            my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun);
            if (xCAT::zvmUtils->checkOutput( $deviceRef ) == -1) {
                xCAT::zvmUtils->printLn( $callback, "$deviceRef" );
                return;
            }
            my %zFCP = %$deviceRef;
            if ($zFCP{'status'} eq 'used') {
                xCAT::zvmUtils->printLn($callback, "$node: (Error) FCP device 0x$wwpn/0x$lun is in use.");
                return;
            }

            %criteria = (
               'status' => $status,
               'fcp' => $device,
               'wwpn' => $wwpn,
               'lun' => $lun,
               'owner' => $owner
            );
            $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        } else {
            # Do not know the WWPN or LUN in this case
            %criteria = (
               'status' => $status,
               'fcp' => $device,
               'size' => $size,
               'owner' => $owner
            );
            $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria);
        }

        my %results = %$resultsRef;

        # Obtain the device assigned by xCAT
        $device = $results{'fcp'};
        $wwpn = $results{'wwpn'};
        $lun = $results{'lun'};

        if ($results{'rc'} == 0) {
            xCAT::zvmUtils->printLn($callback, "$node: Reserving FCP device... Done");
            my $fcpDevice = $device ? "$device/0x$wwpn/0x$lun" : "0x$wwpn/0x$lun";
            xCAT::zvmUtils->printLn($callback, "$node: FCP device $fcpDevice was reserved... Done");
        } else {
            xCAT::zvmUtils->printLn($callback, "$node: (Error) Reserving FCP device... Failed");
        }
    }

    # resetsmapi
    elsif ( $args->[0] eq "--resetsmapi" ) {
        # IMPORTANT:
        #   This option is only supported for class A privilege!
        #   We cannot change it to use SMAPI only because SMAPI cannot be used to restart itself.

        # Check for VSMGUARD in z/VM 6.2 or newer
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q users VSMGUARD"`;
        if (!($out =~ m/HCPCQU045E/i)) {
            # Force VSMGUARD and log it back on using XAUTOLOG
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp force VSMGUARD logoff immediate"`;
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp xautolog VSMGUARD"`;
        } else {
            # Assuming zVM 6.1 or older
            # Force each worker machine off
            my @workers = ('VSMWORK1', 'VSMWORK2', 'VSMWORK3', 'VSMREQIN', 'VSMREQIU');
            foreach ( @workers ) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp force $_ logoff immediate"`;
            }

            # Log on VSMWORK1
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp xautolog VSMWORK1"`;
        }

        $out = "Resetting SMAPI... Done";
    }

    # smcli [api] [args]
    elsif ( $args->[0] eq "--smcli" ) {
        # Invoke SMAPI API directly through zHCP smcli
        my $str = "@{$args}";
        $str =~ s/$args->[0]//g;
        $str = xCAT::zvmUtils->trimStr($str);

        # Pass arguments directly to smcli
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli $str"`;
    }

    # Otherwise, print out error
    else {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" );
    }

    # Only print if there is content
    if ($out) {
        $out = xCAT::zvmUtils->appendHostname( $node, $out );
        chomp($out);
        xCAT::zvmUtils->printLn( $callback, "$out" );
    }

    return;
}

#-------------------------------------------------------

=head3   inventoryHypervisor

    Description : Get hardware and software inventory of a given hypervisor
    Arguments   :   Node
                    Type of inventory (config|all)
    Returns     : Nothing, errors returned in $callback
    Example     : inventoryHypervisor($callback, $node, $args);

=cut

#-------------------------------------------------------
sub inventoryHypervisor {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Set cache directory
    my $cache = '/var/opt/zhcp/cache';

    # Output string
    my $str = "";

    my $rc;
    my $out;
    my $outmsg;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node zHCP" );
        return;
    }

    xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO");

    # Get the user Id of the zHCP
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp);

    # Load VMCP module
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/modprobe vmcp"`;

    # Get configuration
    if ( $args->[0] eq 'config' ) {
        # Get z/VM host for zhcp
        my $hypname = xCAT::zvmCPUtils->getHost($::SUDOER, $hcp);

        # Get total physical CPU in this LPAR
        my $lparCpuTotal = xCAT::zvmUtils->getLparCpuTotal($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparCpuTotal ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparCpuTotal" );
            return;
        }

        # Get used physical CPU in this LPAR
        my $lparCpuUsed = xCAT::zvmUtils->getLparCpuUsed($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparCpuUsed ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparCpuUsed" );
            return;
        }

        # Get LPAR memory total
        my $lparMemTotal = xCAT::zvmUtils->getLparMemoryTotal($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparMemTotal ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparMemTotal" );
            return;
        }

        # Get LPAR memory Offline
        my $lparMemOffline = xCAT::zvmUtils->getLparMemoryOffline($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparMemOffline ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparMemOffline" );
            return;
        }

        # Get LPAR memory Used
        my $lparMemUsed = xCAT::zvmUtils->getLparMemoryUsed($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparMemUsed ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparMemUsed" );
            return;
        }

        $str .= "z/VM Host: $hypname\n";
        $str .= "zHCP: $hcp\n";
        $str .= "LPAR CPU Total: $lparCpuTotal\n";
        $str .= "LPAR CPU Used: $lparCpuUsed\n";
        $str .= "LPAR Memory Total: $lparMemTotal\n";
        $str .= "LPAR Memory Used: $lparMemUsed\n";
        $str .= "LPAR Memory Offline: $lparMemOffline\n";
        $str .= "xCAT Hypervisor Node: $node\n"; # need node name from table unmodified

    } elsif ( $args->[0] eq 'all' ) {
        # Get z/VM system name  for zhcp
        my $hypname = xCAT::zvmCPUtils->getHost($::SUDOER, $hcp);
        # Get total physical CPU in this LPAR
        my $lparCpuTotal = xCAT::zvmUtils->getLparCpuTotal($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparCpuTotal ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparCpuTotal" );
            return;
        }

        # Get used physical CPU in this LPAR
        my $lparCpuUsed = xCAT::zvmUtils->getLparCpuUsed($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparCpuUsed ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparCpuUsed" );
            return;
        }

        # Get CEC model
        my $cecModel = xCAT::zvmUtils->getCecModel($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $cecModel ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$cecModel" );
            return;
        }

        # Get vendor of CEC
        my $cecVendor = xCAT::zvmUtils->getCecVendor($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $cecVendor ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$cecVendor" );
            return;
        }

        # Get hypervisor type and version
        my $hvInfo = xCAT::zvmUtils->getHypervisorInfo($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $hvInfo ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$hvInfo" );
            return;
        }

        # Get processor architecture
        my $arch = xCAT::zvmUtils->getArch($::SUDOER, $hcp);

        # Get hypervisor name
        my $host = xCAT::zvmCPUtils->getHost($::SUDOER, $hcp);

        # Get LPAR memory total
        my $lparMemTotal = xCAT::zvmUtils->getLparMemoryTotal($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparMemTotal ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparMemTotal" );
            return;
        }

        # Get LPAR memory Offline
        my $lparMemOffline = xCAT::zvmUtils->getLparMemoryOffline($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparMemOffline ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparMemOffline" );
            return;
        }

        # Get LPAR memory Used
        my $lparMemUsed = xCAT::zvmUtils->getLparMemoryUsed($::SUDOER, $hcp);
        if (xCAT::zvmUtils->checkOutput( $lparMemUsed ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$lparMemUsed" );
            return;
        }

        # Get IPL Time
        my $ipl = xCAT::zvmCPUtils->getIplTime($::SUDOER, $hcp);

        # Create output string

        $str .= "z/VM Host: $hypname\n";
        $str .= "zHCP: $hcp\n";
        $str .= "Architecture: $arch\n";
        $str .= "CEC Vendor: $cecVendor\n";
        $str .= "CEC Model: $cecModel\n";
        $str .= "Hypervisor OS: $hvInfo\n";
        $str .= "Hypervisor Name: $host\n";
        $str .= "LPAR CPU Total: $lparCpuTotal\n";
        $str .= "LPAR CPU Used: $lparCpuUsed\n";
        $str .= "LPAR Memory Total: $lparMemTotal\n";
        $str .= "LPAR Memory Used: $lparMemUsed\n";
        $str .= "LPAR Memory Offline: $lparMemOffline\n";
        $str .= "IPL Time: $ipl\n";
        $str .= "xCAT Hypervisor Node: $node\n"; # need node name from table unmodified
    }

    # diskpoolspace
    elsif ( $args->[0] eq '--diskpoolspace' ) {
        # Check whether disk pool was given
        my @pools;
        if (!$args->[1]) {
            # Get all known disk pool names
            $out = `/opt/xcat/bin/rinv $node "--diskpoolnames"`;
            $out =~ s/$node: //g;
            $out = xCAT::zvmUtils->trimStr($out);
            @pools = split('\n', $out);
        } else {
            my $pool = uc($args->[1]);
            push(@pools, $pool);

            # Check whether disk pool is a valid pool
            xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Query_DM -q 1 -e 3 -n $pool -T $hcpUserId");
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Query_DM -q 1 -e 3 -n $pool -T $hcpUserId"`;
            if ( $out eq '') {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to communicate with zHCP agent" );
                return;
            } elsif ($out =~ m/Failed/i) {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to obtain disk pool information for $pool, additional information: $out" );
                return;
            }
        }

        # Go through each pool and find it's space
        foreach(@pools) {
            # Skip empty pool
            if (!$_) {
                next;
            }

            my $free = xCAT::zvmUtils->getDiskPoolFree($::SUDOER, $hcp, $_);
            my $used = xCAT::zvmUtils->getDiskPoolUsed($::SUDOER, $hcp, $_);
            my $total = $free + $used;

            # Change the output format from cylinders to 'G' or 'M'
            $total = xCAT::zvmUtils->getSizeFromCyl($total);
            $used = xCAT::zvmUtils->getSizeFromCyl($used);
            $free = xCAT::zvmUtils->getSizeFromCyl($free);

            $str .= "$_ Total: $total\n";
            $str .= "$_ Used: $used\n";
            $str .= "$_ Free: $free\n";
        }
    }

    # diskpool [pool] [all|free|used]
    elsif ( $args->[0] eq "--diskpool" ) {
        # Get disk pool configuration
        my $pool  = $args->[1];
        my $space = $args->[2];

        if ($space eq "all" || !$space) {
            $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpool $hcpUserId $pool free"`;

            # Delete 1st line which is header
            $str .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpool $hcpUserId $pool used" | sed 1d`;
        } else {
            $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpool $hcpUserId $pool $space"`;
        }
    }

    # diskpoolnames
    elsif ( $args->[0] eq "--diskpoolnames" ) {
        # Get disk pool names
        # If the cache directory does not exist
        if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -d $cache && echo Exists"`)) {
            # Create cache directory
            $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $cache"`;
        }
        $rc = $? >> 8;
        if ($rc == 255) {
           xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" );
           xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" );
           return;
        }

        my $file = "$cache/diskpoolnames";

        # If a cache for disk pool names exists
        if (`ssh $::SUDOER\@$hcp "$::SUDO ls $file"`) {
            # Get current Epoch
            my $curTime = time();
            # Get time of last change as seconds since Epoch
            my $fileTime = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO stat -c %Z $file"`);

            # If the current time is greater than 5 minutes of the file timestamp
            my $interval = 300;        # 300 seconds = 5 minutes * 60 seconds/minute
            if ($curTime > $fileTime + $interval) {
                # Get disk pool names and save it in a file
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpoolnames $hcpUserId > $file"`;
            }
        } else {
            # Get disk pool names and save it in a file
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpoolnames $hcpUserId > $file"`;
        }

        # Print out the file contents
        $str = `ssh $::SUDOER\@$hcp "$::SUDO cat $file"`;
    }

    # fcpdevices [active|free|offline] [details (optional)]
    elsif ( $args->[0] eq "--fcpdevices" ) {
        my $argsSize = @{$args};
        my $space = $args->[1];
        my $details = 0;
        if ($argsSize == 3 && $args->[2] eq "details") {
            $details = 1;
        }

        # Display the status of real FCP Adapter devices using System_WWPN_Query
        my @devices;
        my $i;
        my $devNo;
        my $status;
        if ($space eq "active" || $space eq "free" || $space eq "offline") {
            if ($details) {
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId"`;
                xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId");

                @devices = split( "\n", $out );
                for ($i = 0; $i < @devices; $i++) {
                    # Extract the device number and status
                    $devNo = $devices[$i];
                    $devNo =~ s/^FCP device number:(.*)/$1/;
                    $devNo =~ s/^\s+//;
                    $devNo =~ s/\s+$//;

                    $status = $devices[$i + 1];
                    $status =~ s/^Status:(.*)/$1/;
                    $status =~ s/^\s+//;
                    $status =~ s/\s+$//;

                    # Only print out devices matching query
                    if ($status =~ m/$space/i) {
                        $str .= "$devices[$i]\n";
                        $str .= "$devices[$i + 1]\n";
                        $str .= "$devices[$i + 2]\n";
                        $str .= "$devices[$i + 3]\n";
                        $str .= "$devices[$i + 4]\n";
                        $i = $i + 4;
                    }
                }
            } else {
                xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId");
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId"`;
                ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId\"", $hcp, "inventoryHypervisor", $out, $node );
                if ($rc != 0) {
                    xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                    return;
                }
                $out = `echo "$out" | egrep -a -i "FCP device number|Status"`;

                @devices = split( "\n", $out );
                for ($i = 0; $i < @devices; $i++) {
                    # Extract the device number and status
                    $devNo = $devices[$i];
                    $devNo =~ s/^FCP device number:(.*)/$1/;
                    $devNo =~ s/^\s+//;
                    $devNo =~ s/\s+$//;

                    $i++;
                    $status = $devices[$i];
                    $status =~ s/^Status:(.*)/$1/;
                    $status =~ s/^\s+//;
                    $status =~ s/\s+$//;

                    # Only print out devices matching query
                    if ($status =~ m/$space/i) {
                        $str .= "$devNo\n";
                    }
                }
            }
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Query supported on active, free, or offline devices" );
        }
    }

    # luns [fcp_device] (supported only on z/VM 6.2)
    elsif ( $args->[0] eq "--luns" ) {
        # Find the LUNs accessible thru given zFCP device
        my $fcp  = lc($args->[1]);
        my $argsSize = @{$args};
        if ($argsSize < 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp\"", $hcp, "inventoryHypervisor", $out, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $out = `echo "$out" | egrep -a -i "FCP device number:|World wide port number:|Logical unit number:|Number of bytes residing on the logical unit:"`;

        my @wwpns = split( "\n", $out );
        my %map;

        my $wwpn = "";
        my $lun = "";
        my $size = "";
        foreach (@wwpns) {
            if (!(length $_)) {next;}
            # Extract the device number
            if ($_ =~ "World wide port number:") {
                $_ =~ s/^\s+World wide port number:(.*)/$1/;
                $_ =~ s/^\s+//;
                $_ =~ s/\s+$//;
                $wwpn = $_;

                if (!scalar($map{$wwpn})) {
                    $map{$wwpn} = {};
                }
            } elsif ($_ =~ "Logical unit number:") {
                $_ =~ s/^\s+Logical unit number:(.*)/$1/;
                $_ =~ s/^\s+//;
                $_ =~ s/\s+$//;
                $lun = $_;

                $map{$wwpn}{$lun} = "";
            } elsif ($_ =~ "Number of bytes residing on the logical unit:") {
                $_ =~ s/^\s+Number of bytes residing on the logical unit:(.*)/$1/;
                $_ =~ s/^\s+//;
                $_ =~ s/\s+$//;
                $size = $_;

                $map{$wwpn}{$lun} = $size;
            }
        }

        xCAT::zvmUtils->printLn($callback, "#status,wwpn,lun,size,range,owner,channel,tag");
        foreach $wwpn (sort keys %map) {
            foreach $lun (sort keys %{$map{$wwpn}}) {
                # status, wwpn, lun, size, range, owner, channel, tag
                $size = sprintf("%.1f", $map{$wwpn}{$lun}/1073741824);  # Convert size to GB

                if ($size > 0) {
                    $size .= "G";
                   xCAT::zvmUtils->printLn($callback, "unknown,$wwpn,$lun,$size,,,,");
                }
            }
        }

        $str = "";
    }

    # networknames
    elsif ( $args->[0] eq "--networknames" || $args->[0] eq "--getnetworknames" ) {
        $str = xCAT::zvmCPUtils->getNetworkNames($::SUDOER, $hcp);
    }

    # network [name]
    elsif ( $args->[0] eq "--network" || $args->[0] eq "--getnetwork" ) {
        my $netName = $args->[1];
        my $netType = $args->[2];
        $str = xCAT::zvmCPUtils->getNetwork( $::SUDOER, $hcp, $netName, $netType );
    }

    # responsedata [failed Id]
    elsif ( $args->[0] eq "--responsedata" ) {
        # This has not be completed!
        my $failedId = $args->[1];
        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Response_Recovery -T $hcpUserId -k $failedId"`;
        xCAT::zvmUtils->printSyslog("smcli Response_Recovery -T $hcpUserId -k $failedId");
    }

    # freefcp [fcp_dev]
    elsif ( $args->[0] eq "--freefcp" ) {
        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $fcp = "fcp_dev=" . $args->[1];

        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k $fcp"`;
        xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k $fcp");
    }

    # scsidisk [dev_no]
    elsif ( $args->[0] eq "--scsidisk" ) {
        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = "dev_num=" . $args->[1];

        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_SCSI_Disk_Query -T $hcpUserId -k $devNo"`;
        xCAT::zvmUtils->printSyslog("smcli System_SCSI_Disk_Query -T $hcpUserId -k $devNo");
    }

    # ssi
    elsif ( $args->[0] eq "--ssi" ) {
        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli SSI_Query"`;
        xCAT::zvmUtils->printSyslog("smcli SSI_Query");
    }

    # smapilevel
    elsif ( $args->[0] eq "--smapilevel" ) {
        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Query_API_Functional_Level -T $hcpUserId"`;
        xCAT::zvmUtils->printSyslog("smcli Query_API_Functional_Level -T $hcpUserId");
    }

    # systemdisk [dev_no]
    elsif ( $args->[0] eq "--systemdisk" ) {
        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = "dev_num=" . $args->[1];

        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Disk_Query -T $hcpUserId -k $devNo"`;
        xCAT::zvmUtils->printSyslog("smcli System_Disk_Query -T $hcpUserId -k $devNo");
    }

    # systemdiskaccessibility [dev_no]
    elsif ( $args->[0] eq "--systemdiskaccessibility" ) {
        my $argsSize = @{$args};
        if ($argsSize != 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $devNo = "dev_num=" . $args->[1];

        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Disk_Accessibility -T $hcpUserId -k $devNo"`;
        xCAT::zvmUtils->printSyslog("smcli System_Disk_Accessibility -T $hcpUserId -k $devNo");
    }

    # userprofilenames
    elsif ( $args->[0] eq "--userprofilenames" ) {
        my $argsSize = @{$args};
        if ($argsSize != 1) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        # Use Directory_Manager_Search_DM to find user profiles
        my $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Directory_Manager_Search_DM -T $hcpUserId -s PROFILE"`;
        my @profiles = split('\n', $tmp);
        foreach (@profiles) {
            if (!(length $_)) {next;}
            # Extract user profile
            if ($_) {
                $_ =~ /([a-zA-Z]*):*/;
                $str .= "$1\n";
            }
        }

        xCAT::zvmUtils->printSyslog("smcli Directory_Manager_Search_DM -T $hcpUserId -s PROFILE");
    }

    # vlanstats [vlan_id] [user_id] [device] [version]
    elsif ( $args->[0] eq "--vlanstats" ) {
        # This is not completed!
        my $argsSize = @{$args};
        if ($argsSize < 4 && $argsSize > 5) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $vlanId = "VLAN_id=" . $args->[1];
        my $tgtUserId = "userid=" . $args->[2];
        my $device = "device=" . $args->[3];
        my $fmtVersion = "fmt_version=" . $args->[4];  # Optional

        my $argStr = "-k $vlanId -k $tgtUserId -k $device";
        if ($argsSize == 5) {
            $argStr .= " -k $fmtVersion"
        }

        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_VLAN_Query_Stats -T $hcpUserId $argStr"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_VLAN_Query_Stats -T $hcpUserId $argStr");
    }

    # vswitchstats [name] [version]
    elsif ( $args->[0] eq "--vswitchstats" ) {
        my $argsSize = @{$args};
        if ($argsSize < 2 && $argsSize > 3) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        my $switchName = "switch_name=" . $args->[1];
        my $fmtVersion = "fmt_version=" . $args->[2];  # Optional
        my $argStr = "-k $switchName";

        if ($argsSize == 3) {
            $argStr .= " -k $fmtVersion"
        }

        $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Vswitch_Query_Stats -T $hcpUserId $argStr"`;
        xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Vswitch_Query_Stats -T $hcpUserId $argStr");
    }

    # wwpn [fcp_device] (supported only on z/VM 6.2)
    elsif ( $args->[0] eq "--wwpns" ) {
        my $fcp  = lc($args->[1]);
        my $argsSize = @{$args};
        if ($argsSize < 2) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" );
            return;
        }

        xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp\"", $hcp, "inventorHypervisor", $out, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $out = `echo "$out" | egrep -a -i "World wide port number:"`;

        my @wwpns = split( "\n", $out );
        my %uniqueWwpns;
        foreach (@wwpns) {
            if (!(length $_)) {next;}
            # Extract the device number
            if ($_ =~ "World wide port number:") {
                $_ =~ s/^\s+World wide port number:(.*)/$1/;
                $_ =~ s/^\s+//;
                $_ =~ s/\s+$//;

                # Save only unique WWPNs
                $uniqueWwpns{$_} = 1;
            }
        }

        my $wwpn;
        for $wwpn ( keys %uniqueWwpns ) {
            $str .= "$wwpn\n";
        }
    }

    # zfcppool [pool] [space]
    elsif ( $args->[0] eq "--zfcppool" ) {
        # Get zFCP disk pool configuration
        my $pool  = lc($args->[1]);
        my $space = $args->[2];

        if ($space eq "all" || !$space) {
            $str = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf"`;
        } else {
            $str = "#status,wwpn,lun,size,range,owner,channel,tag\n";
            $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf"`;
            ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$pool.conf\"", $hcp, "inventoryHypervisor", $out, $node );
            if ($rc != 0) {
                xCAT::zvmUtils->printLn( $callback, "$outmsg" );
                return;
            }
            $out = `echo "$out" | egrep -a -i "$space"`;
            $str .= $out;
        }
    }

    # zfcppoolnames
    elsif ( $args->[0] eq "--zfcppoolnames") {
        # Get zFCP disk pool names
        # Go through each zFCP pool
        # Note: the code makes an assumption that the only files in this directory are
        # zfcp configuration files. (*.conf)
        my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`);
        foreach (@pools) {
            if (!(length $_)) {next;}
            my $pool = $_;
            # Check if pool is empty (just one line), if so delete
            my $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "cat $::ZFCPPOOL/$pool");
            my @linesLeft = split("\n",$out);
            my $lineCount = scalar(@linesLeft);
            my $emptyLines = `grep -cavP '\\S' $::ZFCPPOOL/$pool`; # Count "empty" lines
            if ($lineCount <= 1 || $lineCount == $emptyLines) {
                # Delete the empty pool, write log entry
                xCAT::zvmUtils->printSyslog("Deleting empty zfcp pool: $pool");
                $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "rm -f $::ZFCPPOOL/$pool");
                next;
            }

            # return just the pool name without the ".conf"
            $pool = xCAT::zvmUtils->replaceStr( $pool, ".conf", "" );
            $str .= "$pool\n";
        }
    }

    else {
        $str = "$node: (Error) Option not supported";
        xCAT::zvmUtils->printLn( $callback, "$str" );
        return;
    }

    # Append hostname (e.g. pokdev61) in front
    $str = xCAT::zvmUtils->appendHostname( $node, $str );

    xCAT::zvmUtils->printLn( $callback, "$str" );
    return;
}

#-------------------------------------------------------

=head3   migrateVM

    Description  : Migrate a virtual machine
    Arguments    :   Node
                     Destination
                     Immediate
                     Action (optional)
                     Max_total
                     Max_quiesce
                     Force (optional)
    Returns      : Nothing
    Example      : migrateVM($callback, $node, $args);

=cut

#-------------------------------------------------------
sub migrateVM {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get HCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get node user ID
    my $userId = $propVals->{'userid'};
    if ( !$userId ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" );
        return;
    }
    # Capitalize user ID
    $userId =~ tr/a-z/A-Z/;

    xCAT::zvmUtils->printSyslog("migrateVM() node:$node userid:$userId zHCP:$hcp sudoer:$::SUDOER sudo:$::SUDO");

    # Get zHCP user ID
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp);
    $hcpUserId =~ tr/a-z/A-Z/;

    # Output string
    my $out;
    my $migrateCmd = "VMRELOCATE -T $userId";

    my $destination;
    my $action;
    my $value;
    foreach my $operand ( @$args ) {
        if ( $operand ) {
            # Find destination key
            if ( $operand =~ m/destination=/i ) {
                $destination = $operand;
                $destination =~ s/destination=//g;
                $destination =~ s/"//g;
                $destination =~ s/'//g;
            } elsif ( $operand =~ m/action=/i ) {
                $action = $operand;
                $action =~ s/action=//g;
            } elsif ( $operand =~ m/max_total=/i ) {
                $value = $operand;
                $value =~ s/max_total=//g;

                # Strip leading zeros
                if (!($value =~ m/[^0-9.]/ )) {
                    $value =~ s/^0+//;
                    $operand = "max_total=$value";
                }
            } elsif ( $operand =~ m/max_quiesce=/i ) {
                $value = $operand;
                $value =~ s/max_quiesce=//g;

                # Strip leading zeros
                if (!($value =~ m/[^0-9.]/ )) {
                    $value =~ s/^0+//;
                    $operand = "max_quiesce=$value";
                }
            }

            # Keys passed directly to smcli
            $migrateCmd .= " -k $operand";
        }
    }

    if (!$action || !$destination) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) One or more required operands was not specified: 'action' or 'destination'." );
        return;
    }

    my $destHcp;
    if ($action =~ m/MOVE/i) {
        # Find the zHCP for the destination host and set the node zHCP as it
        # Otherwise, it is up to the user to manually change the zHCP
        @propNames = ( 'hcp' );
        $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', lc($destination), @propNames );
        $destHcp = $propVals->{'hcp'};
        if ( !$destHcp ) {

            # Try upper-case
            $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', uc($destination), @propNames );
            $destHcp = $propVals->{'hcp'};
        }

        if (!$destHcp) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find zHCP of $destination" );
            xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Set the hcp appropriately in the zvm table" );
            return;
        }
    }

    # Begin migration
    $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli $migrateCmd"`;
    xCAT::zvmUtils->printSyslog("On $hcp, smcli $migrateCmd");
    xCAT::zvmUtils->printLn( $callback, "$node: $out" );

    # Check for errors on migration only
    my $rc = xCAT::zvmUtils->checkOutput( $out );
    if ( $rc != -1 && $action =~ m/MOVE/i) {

        # Check the migration status
        my $check = 4;
        my $isMigrated = 0;
        while ($check > 0) {
            $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli VMRELOCATE_Status -T $hcpUserId" -k status_target=$userId`;
            xCAT::zvmUtils->printSyslog("smcli VMRELOCATE_Status -T $hcpUserId -k status_target=$userId");
            if ( $out =~ m/No active relocations found/i ) {
                $isMigrated = 1;
                last;
            }

            $check--;
            sleep(10);
        }

        # Change the zHCP if migration successful
        if ($isMigrated) {
            `/opt/xcat/bin/nodech $node zvm.hcp=$destHcp zvm.parent=$destination`;
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not determine progress of relocation" );
        }
    }

    return;
}

#-------------------------------------------------------

=head3   evacuate

    Description  : Evacuate all virtual machines off a hypervisor
    Arguments    : Node (hypervisor)
    Returns      : Nothing
    Example      : evacuate($callback, $node, $args);

=cut

#-------------------------------------------------------
sub evacuate {

    # Get inputs, e.g. revacuate pokdev62 poktst62
    my ( $callback, $node, $args ) = @_;

    # In order for this command to work, issue under /opt/xcat/bin:
    # ln -s /opt/xcat/bin/xcatclient revacuate

    my $destination = $args->[0];
    if (!$destination) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing z/VM SSI cluster name of the destination system" );
        return;
    }

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'nodetype' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP of hypervisor
    my $srcHcp = $propVals->{'hcp'};
    if ( !$srcHcp ) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    my $type = $propVals->{'nodetype'};
    if ($type ne 'zvm') {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid nodetype" );
        xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Set the nodetype appropriately in the zvm table" );
        return;
    }

    my $destHcp;

    # Find the zHCP for the destination host and set the node zHCP as it
    # Otherwise, it is up to the user to manually change the zHCP
    @propNames = ( 'hcp' );
    $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', lc($destination), @propNames );
    $destHcp = $propVals->{'hcp'};
    if ( !$destHcp ) {
        # Try upper-case
        $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', uc($destination), @propNames );
        $destHcp = $propVals->{'hcp'};
    }

    if (!$destHcp) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find zHCP of $destination" );
        xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Set the hcp appropriately in the zvm table" );
        return;
    }

    # Get nodes managed by this zHCP
    # Look in 'zvm' table
    my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 );
    my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $srcHcp . "%' and nodetype=='vm'", 'node', 'userid' );

    my $out;
    my $iNode;
    my $iUserId;
    my $smcliArgs;
    my $nodes = "";
    foreach (@entries) {
        $iNode = $_->{'node'};
        $iUserId = $_->{'userid'};

        # Skip zHCP entry
        if ($srcHcp =~ m/$iNode./i || $srcHcp eq $iNode) {
            next;
        }

        $nodes .=  $iNode . ",";
    }

    # Strip last comma
    $nodes = substr($nodes, 0, -1);

    # Do not continue if no nodes to migrate
    if (!$nodes) {
        xCAT::zvmUtils->printLn( $callback, "$node: No nodes to evacuate" );
        return;
    }

    # Begin migration
    # Required keys: target_identifier, destination, action, immediate, and max_total
    $out = `/opt/xcat/bin/rmigrate $nodes action=MOVE destination=$destination immediate=NO max_total=NOLIMIT`;
    xCAT::zvmUtils->printLn( $callback, "$out" );

    return;
}

#-------------------------------------------------------

=head3   eventLog

    Description : Retrieve, clear, or set logging options for event logs
    Arguments   :   Node
                    Location of source log
                    Location to place log
    Returns     : Nothing
    Example     : eventLog($callback, $node, $args);

=cut

#-------------------------------------------------------
sub eventLog {

    # Get inputs
    my ( $callback, $node, $args ) = @_;

    my $srcLog = '';
    my $tgtLog = '';
    my $clear = 0;
    my $options = '';
    if ($args) {
        @ARGV = @$args;

        # Parse options
        GetOptions(
            's=s' => \$srcLog,
            't=s' => \$tgtLog,  # Optional
            'c' => \$clear,
            'o=s' => \$options);  # Set logging options
    }

    # Event log required
    if (!$srcLog) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing event log" );
        return;
    }

    # Limit to logs in /var/log/* and configurations in /var/opt/*
    my $tmp = substr($srcLog, 0, 9);
    if ($tmp ne "/var/opt/" && $tmp ne "/var/log/") {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Files are restricted to those in /var/log and /var/opt" );
        return;
    }

    # Check if node is the management node
    my @entries = xCAT::TableUtils->get_site_attribute("master");
    my $master = xCAT::zvmUtils->trimStr($entries[0]);
    my $ip = xCAT::NetworkUtils->getipaddr($node);
    $ip = xCAT::zvmUtils->trimStr($ip);
    my $mn = 0;
    if ($master eq $ip) {
        # If the master IP and node IP match, then it is the management node
        xCAT::zvmUtils->printLn( $callback, "$node: This is the management node" );
        $mn = 1;
    }

    # Just clear the log
    my $out = '';
    if ($clear) {
        if ($mn) {
            $out = `cat /dev/null > $srcLog`;
        } else {
            #$out = `ssh $::SUDOER\@$node "cat /dev/null > $srcLog"`;
            my $cmd = "$::SUDO cat /dev/null > $srcLog";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

        }

        xCAT::zvmUtils->printLn( $callback, "$node: Clearing event log ($srcLog)... Done" );
        return;
    }

    # Just set the logging options
    if ($options) {
        if ($mn) {
            $out = `echo -e \"$options\" > $srcLog`;
        } else {
            $out = `echo -e \"$options\" > /tmp/$node.tracing`;
            #$out = `ssh $::SUDOER\@$node "rm -rf $srcLog"`;
            my $cmd = "$::SUDO rm -rf $srcLog";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            # Get node communicate type.
            my @propNames = ( 'status' );
            my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );
            if ($propVals->{'status'} =~ /SSH=1/){
                $out = `cat /tmp/$node.tracing | ssh $::SUDOER\@$node "cat > /tmp/$node.tracing"`;
            }
            elsif ($propVals->{'status'} =~ /IUCV=1/){
                #$cmd = "$::SUDO cat /tmp/$node.tracing | ssh $::SUDOER\@$node cat > /tmp/$node.tracing";
                $cmd = "file_transport /tmp/$node.tracing  /tmp/$node.tracing";
                $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    return;
                }
            }else{
                xCAT::zvmUtils->printLn( $callback, "$node: Not set communicate type.");
                return;
            }


            #$out = `ssh $::SUDOER\@$node "mv /tmp/$node.tracing $srcLog"`;
            $cmd = "$::SUDO mv /tmp/$node.tracing $srcLog";
            $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }

            $out = `rm -rf /tmp/$node.tracing`;
        }

        xCAT::zvmUtils->printLn( $callback, "$node: Setting event logging options... Done" );
        return;
    }

    # Default log location is /install/logs
    if (!$tgtLog) {
        my @entries =  xCAT::TableUtils->get_site_attribute("installdir");
        my $install = $entries[0];

        $tgtLog = "$install/logs/";
        $out = `mkdir -p $tgtLog`;
    }

    # Copy over event log onto xCAT
    xCAT::zvmUtils->printLn( $callback, "$node: Retrieving event log ($srcLog)" );
    if ($mn) {
        if (!(`test -e $srcLog && echo Exists`)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Specified log does not exist" );
            return;
        }

        $out = `cp $srcLog $tgtLog`;
    } else {
        #if (!(`ssh $::SUDOER\@$node "test -e $srcLog && echo Exists"`)) {
        my $cmd = "$::SUDO test -e $srcLog && echo Exists";
        $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback);
        if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
            return;
        }

        if (!($out)) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Specified log does not exist" );
            return;
        }

        # ???? Must have SSH key to do the copy
        $out = `scp $::SUDOER\@$node:$srcLog $tgtLog`;
    }

    if ( -e $tgtLog ) {
        xCAT::zvmUtils->printLn( $callback, "$node: Log copied to $tgtLog" );
        $out = `chmod -R 644 $tgtLog/*`;
    } else {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy log" );
    }
}

#-------------------------------------------------------

=head3   imageCapture

    Description : Capture a disk image from a Linux system on z/VM.
    Arguments   : Node
                  OS
                  Archictecture
                  Specified provisioning method type
                  Profile
                  Device information
                  Compression level: 0 - none, 1 thru 9 - gzip compression level
    Returns     : Nothing, errors returned in $callback
    Example     : imageCapture( $callback, $node, $os, $arch, $type, $profile, $osimg, $device, $comp );

=cut

#-------------------------------------------------------
sub imageCapture {
    my ($class, $callback, $node, $os, $arch, $type, $profile, $osimg, $device, $comp) = @_;
    my $rc;
    my $out = '';
    my $outmsg;
    my $reason = "";
    my $provMethod;
    my $compParm = "";
    my $cmd = '';

    xCAT::zvmUtils->printSyslog( "imageCapture() node:$node os:$os arch:$arch type:$type profile:$profile osimg:$osimg device:$device comp:$comp" );

    # Verify required properties are defined
    if (!defined($os) || !defined($arch) || !defined($profile)) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) One or more of the required properties is not specified: os version, architecture or profile" );
        return;
    }

    if (defined($type)) {
        $provMethod = $type;
    } else {
        # Type operand was not specified on command, therefore need to get provmethod from the nodetype table.
        my $nodetypetab = xCAT::Table->new("nodetype");
        my $ref_nodetype = $nodetypetab->getNodeAttribs($node, ['provmethod']);
        $provMethod = $ref_nodetype->{provmethod};
        if ( !defined($provMethod) ) {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) provmethod property is not specified in the nodetype table" );
            return;
        }
    }

    # Ensure provmethod is one of the supported methods
    if (( $provMethod ne 'sysclone') && ( $provMethod ne 'netboot' )) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) provmethod property is not 'netboot' or 'sysclone'" );
        return;
    }

    # Ensure the architecture property is 's390x'
    if ($arch ne 's390x') {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Architecture was $arch instead of 's390x'. 's390x' will be used instead of the specified value." );
        $arch = 's390x';
    }

    # Obtain the location of the install root directory
    my $installRoot = xCAT::TableUtils->getInstallDir();

    # Directory where executables are on zHCP.
    # Using a local variable to hold the directory information because this routine is called from another module.
    my $dir = "/opt/zhcp/bin";

    # Use sudo or not
    # This looks in the passwd table for a key = sudoer
    my ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer();

    # Process ID for xfork()
    my $pid;

    # Child process IDs
    my @children;

    # Get node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames );

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if (!$hcp) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" );
        return;
    }

    # Get zHCP user ID
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($sudoer, $hcp);
    $hcpUserId =~ tr/a-z/A-Z/;

    # Get capture target's user ID
    my $targetUserId = $propVals->{'userid'};
    $targetUserId =~ tr/a-z/A-Z/;

    # Get node properties from 'hosts' table
    @propNames = ( 'ip', 'hostnames' );
    $propVals = xCAT::zvmUtils->getNodeProps('hosts', $node, @propNames);

    # Determine the disks to be captured.
    my $vaddr;
    my @vaddrList;

    if ($provMethod eq 'netboot') {

        # Check if node is pingable
        my $ping = xCAT::zvmUtils->pingNode($node);
        if ($ping eq "noping") {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Host is unreachable" );
            return;
        }

        my $devName;
        # Set the default is device option was specified without any parameters.
        if (!$device) {
            $devName = "/dev/root";
        }

        # Obtain the device number from the target system.
        if ($devName eq '/dev/root') {
            # Determine which Linux device is associated with the root directory
            #$out = `ssh $sudoer\@$node $sudo 'cat /proc/cmdline | tr " " "\\n" | grep -a "^root=" | cut -c6-'`;
            $cmd = "$::SUDO" . ' cat /proc/cmdline | tr " " "\\n"';
            $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd, $callback);
            if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                return;
            }
            $out = `echo "$out" | egrep -a -i "^root=" | cut '-c6-'`;

            my $rootDev = '';
            if ($out) {
                if ( $out =~ m/^UUID=/i ) {
                    $rootDev = "/dev/disk/by-uuid/".substr($out, 5);
                }
                elsif ( $out =~ m/^LABEL=/i ) {
                    $rootDev = "/dev/disk/by-label/".substr($out, 6);
                }
                elsif ( $out =~ /mapper/ ) {
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Capturing a disk with root filesystem on logical volume is not supported" );
                    return;
                } else {
                    $rootDev = $out;
                }
                #$out = `ssh $sudoer\@$node $sudo "readlink -f $rootDev 2>&1"`;
                $cmd = "$::SUDO readlink -f $rootDev 2>&1";
                $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd, $callback);
                if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
                    return;
                }


                if ( $rc != 0 ) {
                    xCAT::zvmUtils->printSyslog( "imageCapture() failed to execute readlink -f $rootDev on capture source vm rc: $rc, out: $out" );
                    xCAT::zvmUtils->printLn( $callback, "$node: imageCapture() failed to execute readlink to locate the root device rc: $rc, out: $out" );
                    return;
                }

                if ($out) {
                    $devName = substr($out, 5);
                    $devName =~ s/\s+$//;
                    $devName =~ s/\d+$//;
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to locate the root device from $out" );
                    return;
                }
            } else {
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to get useful info from /proc/cmdline to locate the device associated with the root directory on capture source vm" );
                return;
            }
        } else {
            $devName = substr $devName, 5;
        }

        $vaddr = xCAT::zvmUtils->getDeviceNodeAddr( $sudoer, $node, $devName );
        if ($vaddr) {
            push( @vaddrList, $vaddr );
        } else {
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable determine the device being captured" );
            return 0;
        }

    } else {
        # provmethod is 'sysclone'

        # Get the list of mdisks
        my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sudoer, $node );
        if (xCAT::zvmUtils->checkOutput( $srcDisks[0] ) == -1) {
            xCAT::zvmUtils->printLn( $callback, "$srcDisks[0]" );
            return;
        }
        foreach (@srcDisks) {
            # Get disk address
            my @words = split( ' ', $_ );
            $vaddr = $words[1];
            my $diskType = $words[2];

            if ( $diskType eq 'FB-512' ) {
                # We do not capture vdisks but we will capture minidisks, dedicated disks and tdisks.
                next;
            }

            # Add 0 in front if address length is less than 4
            while (length($vaddr) < 4) {
                $vaddr = '0' . $vaddr;
            }
            push( @vaddrList, $vaddr );
        }
    }

    # Set the compression invocation parameter if compression was specified.
    # Note: Some older zHCP do not support compression specification.
    if ( defined($comp) ) {
        # Determine if zHCP supports the compression property.
        $out = `ssh -o ConnectTimeout=30 $sudoer\@$hcp "$sudo $dir/creatediskimage -V"`;
        $rc = $?;

        if ( $rc == 65280 ) {
            xCAT::zvmUtils->printSyslog( "imageCapture() Unable to communicate with zHCP agent" );
            xCAT::zvmUtils->printLn( $callback, "$node: imageCapture() is unable to communicate with zHCP agent: $hcp" );
            return;
        }

        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ( $rc != -1 ) {
            # No error.  It is probably that the zHCP supports compression.
            # We will check the version to see if it is high enough.  Any error
            # or too low of a version means that we should ignore the compression
            # operand in the future creatediskimage call.
            # Process the version output.
            my @outLn = split("\n", $out);
            if ( $#outLn == 0 ) {
                # Only a single line of output should come back from a compatable zHCP.
                my @versionInfo = split( '\.', $out );
                if ( $versionInfo[0] >= 2 ) {
                    # zHCP supports compression specification.
                    if (( $comp =~ /[\d]/ ) and ( length($comp) == 1 )) {
                        $compParm = "--compression $comp";
                    } else {
                        xCAT::zvmUtils->printLn( $callback, "$node: (Error) compression property is not a single digit from 0 to 9" );
                        return;
                    }
                }
            }
        }
    }

    # Shutdown and logoff the virtual machine so that its disks are stable for the capture step.
    xCAT::zvmUtils->printSyslog( "imageCapture() Shutting down $node prior to disk capture" );
    #$out = `ssh -o ConnectTimeout=10 $node "shutdown -h now"`;
    $cmd = "$::SUDO shutdown -h now";
    $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd);

    sleep(15);  # Wait 15 seconds to let shutdown start before logging user off

    # If the OS is not shutdown and the machine is enabled for shutdown signals
    # then deactivate will cause CP to send the shutdown signal and
    # wait an additional (z/VM installation configurable) time before forcing
    # the virtual machine off the z/VM system.
    xCAT::zvmUtils->printSyslog( "$sudo $dir/smcli Image_Deactivate -T $targetUserId" );
    $out = `ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Deactivate -T $targetUserId"`;
    $rc = $? >> 8;
    if ($rc == 255) {
        xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
        xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
        return;
    }
    $rc = xCAT::zvmUtils->checkOutput( $out );
    if ($out =~ m/Return Code: 200/i){
        if ($out =~ m/Reason Code: 12/i) {
            $out = "$targetUserId already logged off.";
            $rc = 0;
        } elsif ($out =~ m/Reason Code: 16/i) {
            $out = "$targetUserId in process of logging off.";
            $rc = 0;
        }
    }
    if ( $rc == -1 ) {
        xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $targetUserId output: $out");
        xCAT::zvmUtils->printLn( $callback, "$node: $out" );
        return;
    }
    xCAT::zvmUtils->printSyslog( "imageCapture() smcli response: $out" );

    # Wait (checking every 15 seconds) until user is finally logged off or maximum wait time has elapsed
    my $max = 0;
    $out=`ssh $sudoer\@$hcp "$sudo /sbin/vmcp q user $targetUserId 2>/dev/null"`;
    ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $sudoer\@$hcp \"$sudo /sbin/vmcp q user $targetUserId 2>/dev/null\"", $hcp, "imageCapture", $out, $node );
    if ($rc != 0) {
        xCAT::zvmUtils->printLn( $callback, "$outmsg" );
        return;
    }
    $out = `echo "$out" | egrep -a -i "HCPCQU045E"`;
    while ( !$out && $max < 60 ) {
        sleep(15);  # Wait 15 seconds
        $max++;
        $out=`ssh $sudoer\@$hcp "$sudo /sbin/vmcp q user $targetUserId 2>/dev/null"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $sudoer\@$hcp \"$sudo /sbin/vmcp q user $targetUserId 2>/dev/null\"", $hcp, "imageCapture", $out, $node );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $out = `echo "$out" | egrep -a -i "HCPCQU045E"`;
    }
    my $totalMinutes = $max * 15 / 60;
    if ( $out ) {
        # Target system was successfully logged off
        xCAT::zvmUtils->printSyslog( "imageCapture() Target system was logged off after $totalMinutes minutes" );
    } else {
        # Target system was not logged off
        xCAT::zvmUtils->printSyslog( "imageCapture() Target system was not logged off after $totalMinutes minutes" );
        xCAT::zvmUtils->printSyslog( "$sudo $dir/smcli Image_Deactivate -T $targetUserId -f IMMED" );
        $out = `ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Deactivate -T $targetUserId -f IMMED"`;
        $rc = $? >> 8;
        if ($rc == 255) {
            xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" );
            xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" );
            return;
        }
        $rc = xCAT::zvmUtils->checkOutput( $out );
        if ($out =~ m/Return Code: 200/i){
            if ($out =~ m/Reason Code: 12/i) {
                $out = "$targetUserId already logged off.";
                $rc = 0;
            } elsif ($out =~ m/Reason Code: 16/i) {
                $out = "$targetUserId in process of logging off.";
                $rc = 0;
            }
        }
        if ( $rc == -1 ) {
            xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $targetUserId output: $out");
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );
            return;
        }
        xCAT::zvmUtils->printSyslog( "imageCapture() smcli response: $out" );
        sleep(15);  # Wait 15 seconds
    }

    xCAT::zvmUtils->printSyslog( "imageCapture() Preparing the staging directory" );

    # Create the staging area location for the image
    my $stagingImgDir = "$installRoot/staging/$os/$arch/$profile";
    if(-d $stagingImgDir) {
        rmtree $stagingImgDir;
    }
    mkpath($stagingImgDir);

    # Prepare the staging mount point on zHCP, if they need to be established.
    my $remoteStagingDir;
    $rc = xCAT::zvmUtils->establishMount( $callback, $sudoer, $sudo, $hcp, $installRoot, "staging", "rw", \$remoteStagingDir );
    if ($rc) {
        # Mount failed
        rmtree "$stagingImgDir";
        return;
    }

    xCAT::zvmUtils->printLn( $callback, "$node: Capturing the image using zHCP node" );

    # Drive each device capture separately.  Up to 10 at a time.
    # Each capture request to zHCP is driven from a child process.
    foreach my $vaddr ( @vaddrList ) {
        $pid = xCAT::Utils->xfork();

        # Parent process
        if ($pid) {
            push( @children, $pid );
        }

        # Child process.
        elsif ( $pid == 0 ) {
            # Drive the capture on the zHCP node
            xCAT::zvmUtils->printSyslog( "imageCapture() creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img $compParm" );
            $out = `ssh $sudoer\@$hcp "$sudo $dir/creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img $compParm"`;
            $rc=$?;
            xCAT::zvmUtils->printLn( $callback, "$node: $out" );

            # Check for script errors
            my $reasonString = "";
            $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString );
            if ($rc != 0) {
                $reason = "Reason: $reasonString";
                xCAT::zvmUtils->printSyslog( "imageCapture() creatediskimage of $targetUserId $vaddr failed. $reason" );
                xCAT::zvmUtils->printLn( $callback, "$node: (Error) Image capture of $targetUserId $vaddr failed on the zHCP node. $reason" );
                # Create a "FAILED" file to indicate the failure.
                if ( ! open FILE, '>'."$stagingImgDir/FAILED" ) {
                    # if we can't open it then we log the problem.
                    xCAT::zvmUtils->printSyslog( "imageCapture() unable to create a 'FAILED' file." );
                }
            }

            # Exit the child process
            exit(0);
        }

        else {
            # Ran out of resources
            # Create a "FAILED" file to indicate the failure.
            if ( ! open FILE, '>'."$stagingImgDir/FAILED" ) {
                # if we can't open it then we log the problem.
                xCAT::zvmUtils->printSyslog( "imageCapture() unable to create a 'FAILED' file." );
            }

            $reason = ". Reason: Could not fork\n";
            last;
        }

        # Handle 10 nodes at a time, else you will get errors
        if ( !( @children % 10 ) ) {

            # Wait for all processes to end
            foreach (@children) {
                waitpid( $_, 0 );
            }

            # Clear children
            @children = ();
        }
    }   # End of foreach

    # If any children remain, then wait for them to complete.
    foreach $pid ( @children ) {
        xCAT::zvmUtils->printSyslog( "imageCapture() Waiting for child process $pid to complete" );
        waitpid( $pid, 0 );
    }

    # if the capture failed then clean up and return
    if ( -e "$stagingImgDir/FAILED" ) {
        xCAT::zvmUtils->printSyslog( "imageCapture() 'FAILED' file found.  Removing staging directory." );
        rmtree "$stagingImgDir";
        return;
    }

    # Now that all image files have been successfully created, move them to the deployable directory.
    my $imageName = "$os-$arch-$provMethod-$profile";
    my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile";

    xCAT::zvmUtils->printLn( $callback, "$node: Moving the image files to the deployable directory: $deployImgDir" );

    my @stagedFiles = glob "$stagingImgDir/*.img";
    if (!@stagedFiles) {
        rmtree "$stagingImgDir";
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) No image files were created" );
        return 0;
    }

    if ( -e "$deployImgDir" ) {
        $out=`/bin/rm -f $deployImgDir/*.img`;
    } else {
        mkpath($deployImgDir);
    }

    foreach my $oldFile (@stagedFiles) {
        $rc = move($oldFile, $deployImgDir);
        $reason = $!;
        if ($rc == 0) {
            # Move failed
            rmtree "$stagingImgDir";
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not move $oldFile to $deployImgDir. $reason" );
            return;
        }
    }

    # For sysclone, obtain the userid/identity entry for the user and move it to the deploy image directory.
    if ($provMethod eq 'sysclone') {
        $out=`ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Query_DM -T $targetUserId | sed '\$d' > $remoteStagingDir/$os/$arch/$profile/$targetUserId.direct"`;
        # Move the direct file to the deploy image directory
        $rc = move("$stagingImgDir/$targetUserId.direct", $deployImgDir);
        $reason = $!;
        if ($rc == 0) {
            # Move failed
            rmtree "$stagingImgDir";
            xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not move $stagingImgDir/$targetUserId.direct to $deployImgDir. $reason" );
            return;
        }
    }

    # Remove the staging directory and files
    rmtree "$stagingImgDir";

    xCAT::zvmUtils->printSyslog( "imageCapture() Updating the osimage table" );

    # Update osimage table
    my $osTab = xCAT::Table->new('osimage',-create => 1,-autocommit => 0);
    my %keyHash;

    unless ($osTab) {
        xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to open table 'osimage'" );
        return;
    }

    $keyHash{provmethod} = $provMethod;
    $keyHash{profile} = $profile;
    $keyHash{osvers} = $os;
    $keyHash{osarch} = $arch;
    $keyHash{imagetype} = 'linux';
    $keyHash{osname} = 'Linux';
    $keyHash{imagename} = $imageName;

    $osTab->setAttribs({imagename => $imageName}, \%keyHash);
    $osTab->commit;

    xCAT::zvmUtils->printSyslog( "imageCapture() Updating the linuximage table" );

    # Update linuximage table
    my $linuxTab = xCAT::Table->new('linuximage',-create => 1,-autocommit => 0);

    %keyHash = ();
    $keyHash{imagename} = $imageName;
    $keyHash{rootimgdir} = $deployImgDir;

    $linuxTab->setAttribs({imagename => $imageName}, \%keyHash);
    $linuxTab->commit;

    xCAT::zvmUtils->printLn( $callback, "$node: Completed capturing the image($imageName) and stored at $deployImgDir" );

    return;
}

#-------------------------------------------------------

=head3   specialcloneVM

    Description : Do a special clone of virtual server
    Arguments   :   callback
                    Node(s) array
                    args with: maybe disk password, imagename
                    clone info hash, can be empty
    Returns     : Nothing
    Example     : cloneVM($callback, \@targetNodes, $args, \%cloneInfoHash);

=cut

#-------------------------------------------------------
sub specialcloneVM {

    # Get inputs
    my ( $callback, $nodes, $args, $cloneInfoHash ) = @_;

    # Get nodes
    my @nodes = @$nodes;
    my $nodeCount = @nodes;

    my %cloneInfo = %$cloneInfoHash;

    my $sourceId;
    my $srcOS = "unknown";

    my $sudo = "sudo";
    my $user = $::SUDOER;

    # Return code for each command
    my $rc;
    my $out;
    my $i;

    # Child process IDs
    my @children;

    # Process ID for xfork()
    my $pid;

    # Directory where executables are
    my $dir = '/opt/zhcp/bin';

    if ($user eq "root") {
        $sudo = "";
    }

    # Do some parameter checking
    if ( defined $cloneInfo{'CLONE_FROM'} ) {
        $sourceId = $cloneInfo{'CLONE_FROM'};
        xCAT::zvmUtils->printSyslog("Clone of <@nodes> count:$nodeCount to be done from: $sourceId\n");
    } else {
        xCAT::zvmUtils->printLn( $callback, "(Error) CLONE_FROM value is missing from DOCLONE COPY on 193." );
        return;
    }


    if ($nodeCount < 1) {
        xCAT::zvmUtils->printLn( $callback, "(Error) Missing target nodes to clone." );
        return;
    }

    # Verify that all the target nodes use the same zhcp
    my $tgtFirstNode = $nodes[0];
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtFirstNode, @propNames );

    # Get target zhcp, all nodes must use the same one, and the source userid must be
    # also on that zhcp
    my $tgtZhcp = $propVals->{'hcp'};
    my $tgtFirstUserid = $propVals->{'userid'};
    my $founderror = 0;

    foreach (@nodes) {
        $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames );
        my $temp = $propVals->{'hcp'};
        my $tempUserid = $propVals->{'userid'};
        if ($temp ne $tgtZhcp) {
            $founderror = 1;
            xCAT::zvmUtils->printLn( $callback, "(Error) node $_ does not match zhcp $tgtZhcp." );
        }
        if ( length($tempUserid) < 1) {
            $founderror = 1;
            xCAT::zvmUtils->printLn( $callback, "(Error) node $_ does not have a zVM userid." );
        }
    }
    if ($founderror == 1) { return; }

    # Get the source userid directory using original call.
    xCAT::zvmUtils->printSyslog("Executing: ssh $::SUDOER\@$tgtZhcp $::SUDO $dir/smcli Image_Query_DM -T $sourceId | sed '\$d'\n");
    $out = `ssh $::SUDOER\@$tgtZhcp "$::SUDO $dir/smcli Image_Query_DM -T $sourceId" | sed '\$d'`;
    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
        xCAT::zvmUtils->printLn( $callback, "(Error) Did not get the $sourceId directory. Return output was: $out." );
        return;
    }

    my @sourceDirectory = split('\n', $out);
    my $sourceMdisks = `echo "$out" | grep -a -E -i "MDISK"`; # maybe not use this?
    $sourceMdisks = xCAT::zvmUtils->trimStr($sourceMdisks);

    my $sourceLinks  = `echo "$out" | grep -a -E -i "LINK"`;
    $sourceLinks = xCAT::zvmUtils->trimStr($sourceLinks);

    my $sourceWithoutMdisks = `echo "$out" | grep -a -E -i -v "MDISK"`;
    $sourceWithoutMdisks = xCAT::zvmUtils->trimStr($sourceWithoutMdisks);

    # Get the available mini disks using Image Definition in case this is SSI
    # Output is similar to:
    #   MDISK=VDEV=0100 DEVTYPE=3390 START=0001 COUNT=10016 VOLID=EMC2C4 MODE=MR
    $out = `ssh $::SUDOER\@$tgtZhcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $sourceId -k MDISK"`;
    if (xCAT::zvmUtils->checkOutput( $out ) == -1) {
        xCAT::zvmUtils->printLn( $callback, "(Error) Did not get the $sourceId mini disks. Return output was: $out." );
        return;
    }

    my @lines = split( '\n', $out );
    my @srcMdisks = ();
    my $foundECKD = 0;
    my $foundFBA = 0;

    # Loop through each mini disk line, make a hash table and add it to the disk array
    for ( $i = 0 ; $i < @lines ; $i++ ) {
        # remove the MDISK= from the line
        $lines[$i] =~ s/MDISK=//g;

        my %hash = ($lines[$i] =~ m/(\w+)\s*=\s*(\w+)/g);
        #foreach (keys%hash) {
        #    xCAT::zvmUtils->printSyslog("Mdisk key: $_ value: $hash{$_}\n");
        #}
        if ($hash{'DEVTYPE'} eq '3390') {
            $foundECKD = 1;
        } else {
            $foundFBA = 1;
        }
        push @srcMdisks, \%hash;
    }

    # Check for missing disk pool
    if ($foundFBA && !(defined $cloneInfo{'FBA_POOL'})) {
        xCAT::zvmUtils->printLn( $callback, "(Error) FBA disk was found but no FBA_POOL was defined in DOCLONE COPY." );
        xCAT::zvmUtils->printSyslog("(Error) FBA disk was found but no FBA_POOL was defined in DOCLONE COPY.\n" );
        return;
    }
    # Check for missing disk pool
    if ($foundECKD && !(defined $cloneInfo{'ECKD_POOL'})) {
        xCAT::zvmUtils->printLn( $callback, "(Error) ECKD disk was found but no ECKD_POOL was defined in DOCLONE COPY." );
        xCAT::zvmUtils->printSyslog("(Error) ECKD disk was found but no ECKD_POOL was defined in DOCLONE COPY.\n" );
        return;
    }

    # Update the zvm table status column to indicate the special processing.
    foreach (@nodes) {
        my %propHash = ();
        %propHash = ( 'status'  => 'CLONE_ONLY=1;CLONING=1;');
        xCAT::zvmUtils->setNodeProps( 'zvm', $_, \%propHash );
        %propHash = ();
        %propHash = ( 'disable'  => '1');
        xCAT::zvmUtils->setNodeProps( 'hosts', $_, \%propHash );
        xCAT::zvmUtils->setNodeProps( 'mac', $_, \%propHash );

    }

    #*** Link source disks
    # Hash table of source disk addresses to linked address
    my %srcLinkAddr;
    my $addr;
    my $linkAddr;

    for my $rowdisk ( @srcMdisks ) {
        my %rowhash = %$rowdisk;
        # Get disk address from the array entry hash
        $addr = $rowhash{'VDEV'};

        # Add 0 in front if address length is less than 4
        while (length($addr) < 4) {
            $addr = '0' . $addr;
        }
        # Save any updates to length of addr
        $rowhash{'VDEV'} = $addr;

        # If source disk is not linked
        my $try = 5;
        while ( $try > 0 ) {

            # New disk address
            $linkAddr = $addr + 1000;

            # Check if new disk address is used (source)
            $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $tgtZhcp, $linkAddr );

            # If disk address is used (source)
            while ( $rc == 0 ) {

                # Generate a new disk address
                # Sleep 5 seconds to let existing disk appear
                sleep(5);
                $linkAddr = $linkAddr + 1;
                $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $tgtZhcp, $linkAddr );
            }

            $srcLinkAddr{$addr} = $linkAddr;

            # Link source disk to HCP
            xCAT::zvmUtils->printLn( $callback, "Linking source disk ($addr) as ($linkAddr)" );
            $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$tgtZhcp "$::SUDO /sbin/vmcp link $sourceId $addr $linkAddr RR"`;

            if ( $out =~ m/not linked/i ) {
                xCAT::zvmUtils->printSyslog("Retry linking source disk ($addr) as ($linkAddr)\n");
                xCAT::zvmUtils->printLn($callback, "Retry linking source disk ($addr) as ($linkAddr)");
                # Do nothing
            } else {
                last;
            }

            $try = $try - 1;

            # Wait before next try
            sleep(5);
        } # End of while ( $try > 0 )

        # If source disk is not linked
        if ( $out =~ m/not linked/i ) {
            xCAT::zvmUtils->printSyslog("Failed to link source disk $addr from userid $sourceId.\n" );
            xCAT::zvmUtils->printLn( $callback, "Failed to link source disk $addr from userid $sourceId." );
            # Exit
            return;
        }

        # Enable source disk
        $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $tgtZhcp, "-e", $linkAddr );
    } # End of foreach (@srcMdisks)


    # Save the source directory without any mdisk statements, use temp file name
    my $srcUserEntry = `/bin/mktemp /tmp/$sourceId.txtXXXXXXXX`;
    if ( $? ) {
        xCAT::zvmUtils->printLn( $callback, "Failed to create temp file using pattern /tmp/$sourceId.txtXXXXXXXX" );
        xCAT::zvmUtils->printSyslog( "Failed to create temp file using pattern /tmp/$sourceId.txtXXXXXXXX" );
        return;
    }
    chomp ($srcUserEntry);  # need to remove line ends
    #xCAT::zvmUtils->printSyslog("Created temp file called ($srcUserEntry)");

    # Create a file to save output
    open( DIRENTRY, ">$srcUserEntry" );
    @lines = split( '\n', $sourceWithoutMdisks );
    foreach (@lines) {

        # Trim line
        $_ = xCAT::zvmUtils->trimStr($_);

        # Write directory entry into file
        print DIRENTRY "$_\n";
    }
    close(DIRENTRY);

    # Turn off source node
    xCAT::zvmUtils->printSyslog("Calling smcli Image_Deactivate -T $sourceId (On clone for @nodes)");
    $out = `ssh $::SUDOER\@$tgtZhcp "$::SUDO $::DIR/smcli Image_Deactivate -T $sourceId"`;
    $rc = $? >> 8;
    if ($rc == 255) {
        xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $tgtZhcp" );
        xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $tgtZhcp" );
        return;
    }
    $rc = xCAT::zvmUtils->checkOutput( $out );
    if ($out =~ m/Return Code: 200/i){
        if ($out =~ m/Reason Code: 12/i) {
            $out = "$sourceId already logged off.";
            $rc = 0;
        } elsif ($out =~ m/Reason Code: 16/i) {
            $out = "$sourceId in process of logging off.";
            $rc = 0;
        }
    }
    if ( $rc == -1 ) {
            xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $sourceId output: $out");
            xCAT::zvmUtils->printLn( $callback, "$out");
            return;
    }
    xCAT::zvmUtils->printSyslog("$out");

    #*** Clone source node ***
    foreach (@nodes) {
        $pid = xCAT::Utils->xfork();

        # Parent process
        if ($pid) {
            push( @children, $pid );
        }

        # Child process
        elsif ( $pid == 0 ) {
             specialClone( $callback, $_, $args, \@srcMdisks, \%srcLinkAddr, \%cloneInfo, $sourceId, $srcUserEntry);
            # Exit process
            exit(0);
        }

        # End of elsif
        else {
            # Ran out of resources
            die "Error: Could not fork\n";
        }

        # Clone 4 nodes at a time
        # If you handle more than this, some nodes will not be cloned
        # You will get errors because SMAPI cannot handle many nodes
        if ( !( @children % 4 ) ) {

            # Wait for all processes to end
            foreach (@children) {
                waitpid( $_, 0 );
            }

            # Clear children
            @children = ();
        }
    }    # End of foreach

    # Handle the remaining nodes
    # Wait for all processes to end
    foreach (@children) {
        waitpid( $_, 0 );
    }

    # Remove source user entry
    $out = `rm $srcUserEntry`;


   #*** Detatch source disks ***

   for $addr ( keys %srcLinkAddr ) {

       $linkAddr = $srcLinkAddr{$addr};


       # Disable and detatch source disk

       $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $tgtZhcp, "-d", $linkAddr );

       $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$tgtZhcp "$::SUDO /sbin/vmcp det $linkAddr"`;


       xCAT::zvmUtils->printLn( $callback, "Detatching source disk ($addr) at ($linkAddr)" );

   }


   #*** Done ***

   foreach (@nodes) {

       xCAT::zvmUtils->printLn( $callback, "$_: Done" );

   }

    return;
}


#-------------------------------------------------------

=head3   specialClone

    Description : Clone a virtual server from a zvm userid
    Arguments   :   Target node
                    args (Disk pool, Disk password (optional))
                    Source disks array of hash
                    Source disk link addresses
                    clone info hash from doclone.txt
                    Source zvm userid
                    Source userid directory file name
    Returns     : Nothing, errors returned in $callback
    Example     : specialClone($callback, $_, $args, \@srcMdisks, \%srcLinkAddr, \%cloneInfoHash, $sourceId, $srcUserEntry);

=cut

#-------------------------------------------------------
sub specialClone {

    # Get inputs
    my (
        $callback, $tgtNode, $args, $srcMdisksRef, $srcLinkAddrRef, $cloneInfoHash, $sourceId, $srcUserEntry)= @_;

    # Get source disks
    my @srcMdisks    = @$srcMdisksRef;
    my %srcLinkAddr = %$srcLinkAddrRef;
    my %cloneInfo = %$cloneInfoHash;

    # Return code for each command
    my $rc;

    # Disk pools
    my $ECKD_Pool = '';
    my $FBA_Pool = '';

    my $cmsVDEVs = '';
    if ( defined $cloneInfo{'CMS_VDEVS'} ) {
        $cmsVDEVs = $cloneInfo{'CMS_VDEVS'};
    }

    # Get the Dirmaint pool information
    if (defined $cloneInfo{'ECKD_POOL'}) {
        $ECKD_Pool = $cloneInfo{'ECKD_POOL'};
    }
    if (defined $cloneInfo{'FBA_POOL'}) {
        $FBA_Pool = $cloneInfo{'FBA_POOL'};
    }

    # Get target node properties from 'zvm' table
    my @propNames = ( 'hcp', 'userid' );
    my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames );

    # Get node user ID
    my $tgtUserId = $propVals->{'userid'};
    if ( !$tgtUserId ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target user ID" );
        return;
    }
    # Capitalize user ID
    $tgtUserId =~ tr/a-z/A-Z/;

    # Get zHCP
    my $hcp = $propVals->{'hcp'};
    if ( !$hcp ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target node HCP" );
        return;
    }

    # Get zHCP user ID
    my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp);
    $hcpUserId =~ tr/a-z/A-Z/;

    my $out;
    my $outmsg;
    my @lines;
    my @words;

    # Get disk pool and multi password
    # parameters are in "--key value"
    my $i;
    my %inputs;
    my $argsSize = @{$args};
    for ( my $i = 0 ; $i < $argsSize ; $i++ ) {
        if ( ($i+1) < $argsSize) {
            # add to hash array
            $inputs{ $args->[$i] } = $args->[$i+1];
        }
    }

    # Get multi password
    # It is Ok not have a password
    my $tgtPw = "''";
    if ($inputs{"--password"}) {
        $tgtPw = $inputs{"--password"};
    }

    # Save user directory entry as /tmp/hostname.txt, e.g. /tmp/gpok3.txt
    # The source user entry is retrieved and passed as parameter in specialCloneVM()
    my $userEntry = "/tmp/$tgtNode.txt";

    # Remove existing user entry if any
    $out = `rm $userEntry`;
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $userEntry"`;

    # Copy user entry of source node
    $out = `cp $srcUserEntry $userEntry`;
    if ( $? ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to copy $srcUserEntry to $userEntry" );
        xCAT::zvmUtils->printSyslog( "$tgtNode: (Error) Failed to copy $srcUserEntry to $userEntry" );
        return;
    }
    xCAT::zvmUtils->printSyslog("Copied temp named source <$srcUserEntry> to <$userEntry>");

    # Replace source userID with target userID
    $out = `sed -i -e "s,$sourceId,$tgtUserId,i" $userEntry`;

    # SCP user entry file over to HCP
    my $reasonString = '';
    my $remoteUserEntry = '';
    $rc = xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $userEntry, $userEntry );
    if ( $rc == 0 ) {
        $remoteUserEntry = $userEntry;
    } else {
        $reasonString = "Unable to send $userEntry to $hcp, SCP rc: $rc";
    }

    #*** Create new virtual server ***
    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" );
    if ( $remoteUserEntry ne '' ) {
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Create_DM -T $tgtUserId -f $remoteUserEntry"`;
        $rc = $? >> 8;
        xCAT::zvmUtils->printSyslog( "$tgtNode: Result from smcli Image_Create_DM -T $tgtUserId, rc: $rc, out: $out" );

        if ( $rc == 0 ) {
            $rc  = xCAT::zvmUtils->checkOutput( $out );
            if ( $rc != 0 ) {
                $rc = -1;
                $reasonString = "Image_Create_DM returned $out";
            }
        } elsif ( $rc == 255 ) {
            $reasonString = "Unable to communicate with $hcp";
        } else {
            $reasonString = "Image_Create_DM returned rc: $rc, out: $out";
        }
    }

    # Remove user entry
    $out = `rm $userEntry`;
    if ( $remoteUserEntry ne '' ) {
        $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $remoteUserEntry"`;
    }

    # Exit on bad output
    if ( $rc != 0 ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry.  $reasonString" );
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify that the node's zHCP and its zVM's SMAPI are both online" );
        return;
    }

    # Load VMCP module on HCP and source node
    $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`;

    #*** Add MDisk to target user entry ***
    my $addr;
    my @tgtDisks;
    my $type;
    my $mode;
    my $disksize;
    my $try;
    for my $rowdisk ( @srcMdisks ) {
        my %rowhash = %$rowdisk;
        # Get disk address from the array entry hash
        $addr = $rowhash{'VDEV'};

        push( @tgtDisks, $addr );

        $type = $rowhash{'DEVTYPE'};
        $disksize = $rowhash{'COUNT'};

        if ( defined $rowhash{'MODE'} ) {
            $mode = $rowhash{'MODE'};
        } else {
           $mode = "MR";
        }

        # Add ECKD disk
        if ( $type eq '3390' ) {

            $try = 5;
            while ( $try > 0 ) {

                # Add ECKD disk
                if ( $try > 4 ) {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" );
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" );
                }
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $ECKD_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $ECKD_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw");
                xCAT::zvmUtils->printSyslog("$out");
                xCAT::zvmUtils->printLn( $callback, "$out" );

                # Check output
                $rc = xCAT::zvmUtils->checkOutput( $out );
                if ( $rc == -1 ) {

                    # Wait before trying again
                    sleep(5);

                    # One less try
                    $try = $try - 1;
                } else {

                    # If output is good, exit loop
                    last;
                }
            }    # End of while ( $try > 0 )

            # Exit on bad output
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" );
                return;
            }
        }    # End of if ( $type eq '3390' )

        # Add FBA disk
        elsif ( $type eq '9336' ) {

            # Get disk size (blocks)
            my $blkSize = '512';

            $try = 10;
            while ( $try > 0 ) {

                # Add FBA disk
                if ( $try > 9 ) {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" );
                } else {
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" );
                }
                $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $FBA_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`;
                xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $FBA_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw");
                xCAT::zvmUtils->printSyslog("$out");
                xCAT::zvmUtils->printLn( $callback, "$out" );

                # Check output
                $rc = xCAT::zvmUtils->checkOutput( $out );
                if ( $rc == -1 ) {

                    # Wait before trying again
                    sleep(5);

                    # One less try
                    $try = $try - 1;
                } else {

                    # If output is good, exit loop
                    last;
                }
            }    # End of while ( $try > 0 )

            # Exit on bad output
            if ( $rc == -1 ) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" );
                return;
            }
        }    # End of elsif ( $type eq '9336' )
    }

    # Check if the number of disks in target user entry
    # is equal to the number of disks added
    my @disks;
    $try = 10;
    xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks added (@tgtDisks). Checking directory for those disks..." );
    while ( $try > 0 ) {

        # Get disks within user entry
        xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId");
        $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId"`;
        ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId\"", $hcp, "specialClone", $out, $tgtNode );
        if ($rc != 0) {
            xCAT::zvmUtils->printLn( $callback, "$outmsg" );
            return;
        }
        $out = `echo "$out" | sed '\$d' | grep -a -i "MDISK"`;
        xCAT::zvmUtils->printSyslog("$out");
        @disks = split( '\n', $out );

        if ( @disks != @tgtDisks ) {
            $try = $try - 1;

            # Wait before trying again
            sleep(5);
        } else {
            last;
        }
    }

    # Exit if all disks are not present
    if ( @disks != @tgtDisks ) {
        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." );
        xCAT::zvmUtils->printSyslog( "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." );

        xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks found in $sourceId source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" );
        xCAT::zvmUtils->printSyslog( "$tgtNode: Disks found in $sourceId + source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" );

        xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify disk pool has free disks and that directory updates are working" );
        return;
    }

    #**  * Link, format, and copy source disks ***
    my $srcAddr;
    my $srcHcpLinkAddr;
    my $mdiskAddr;
    my $tgtAddr;
    my $srcDevNode;
    my $tgtDevNode;
    my $tgtDiskType;

    # source and target disks will be same type and size
    for my $rowdisk ( @srcMdisks ) {
        my %rowhash = %$rowdisk;
        # Get disk address from the array entry hash
        $mdiskAddr = $rowhash{'VDEV'};
        $tgtDiskType = $rowhash{'DEVTYPE'};

        #*** Try to use SMAPI flashcopy first if ECKD  ***
        # Otherwise link the target disks and if ECKD, try CP Flashcopy. If
        # CP flashcopy does not work or not ECKD; use Linux DD
        my $ddCopy = 0;
        my $cpFlashcopy = 1;
        my $smapiFlashCopyDone = 0;

        if ($tgtDiskType eq '3390') {
            # Try SMAPI FLASHCOPY
            if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) {
                xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($mdiskAddr) to target disk ($mdiskAddr) using FLASHCOPY" );
                xCAT::zvmUtils->printSyslog( "$tgtNode: Doing SMAPI flashcopy source disk ($sourceId $mdiskAddr) to target disk ($tgtUserId $mdiskAddr) using FLASHCOPY" );
                $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $mdiskAddr, $tgtUserId, $mdiskAddr);

                # Check if flashcopy completed successfully, or it completed and is asynchronous
                if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) {
                    chomp($out);
                    xCAT::zvmUtils->printSyslog( "$tgtNode: SMAPI flashcopy done. output($out)");
                    $cpFlashcopy = 0;
                    $smapiFlashCopyDone = 1;
                } else {
                    # Continue to try a Linux format and DD. Put out information message to log and back to caller
                    chomp($out);
                    my $outlen = length($out);
                    xCAT::zvmUtils->printSyslog("$tgtNode: SMAPI Flashcopy did not work, continuing with Linux DD. SMAPI output($outlen bytes):" );
                    xCAT::zvmUtils->printSyslog("$tgtNode: $out" );

                    # Change any (error) or "failed" to info so that OpenStack does not reject this clone
                    $out =~ s/\(error\)/\(info\)/gi;
                    $out =~ s/failed/info/gi;
                    xCAT::zvmUtils->printLn( $callback, "$out" );
                    xCAT::zvmUtils->printLn( $callback, "$tgtNode: SMAPI Flashcopy did not work, continuing with Linux format and DD." );
                }
            }
        }

        # If SMAPI flashcopy did not work or this is not an ECKD, then link the target disks in write mode
        if ( !$smapiFlashCopyDone ) {
            $ddCopy = 1;

           #*** Link target disk ***
           $try = 10;
           while ( $try > 0 ) {

               # New disk address
               $srcAddr = $mdiskAddr;
               $srcHcpLinkAddr = $srcLinkAddr{$mdiskAddr};
               $tgtAddr = $mdiskAddr + 2000;

               # Check if new disk address is used (target)
               $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr );

               # If disk address is used (target)
               while ( $rc == 0 ) {

                   # Generate a new disk address
                   # Sleep 5 seconds to let existing disk appear
                   sleep(5);
                   $tgtAddr = $tgtAddr + 1;
                   $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr );
               }

               # Link target disk
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($mdiskAddr) as ($tgtAddr) in write mode" );
               $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $mdiskAddr $tgtAddr MR $tgtPw"`;

               # If link fails
               if ( $out =~ m/not linked/i || $out =~ m/not write-enabled/i ) {

                   # Wait before trying again
                   sleep(5);

                   $try = $try - 1;
               } else {
                   last;
               }
           }    # End of while ( $try > 0 )

           # If target disk is not linked
           if ( $out =~ m/not linked/i ) {
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($mdiskAddr) in write mode" );
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" );

               # Exit
               return;
           }
           # Flashcopy not supported, use Linux dd
           if ($ddCopy) {

               #*** Use Linux dd to copy ***
               xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not working. Using Linux DD" );

               # Enable target disk
               $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr );

               # Determine source device node
               $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcHcpLinkAddr);

               # Determine target device node
               $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr);

               # Format target disk
               # Only ECKD disks need to be formated
               if ($tgtDiskType eq '3390') {
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" );
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`;
                   xCAT::zvmUtils->printSyslog("dasdfmt -b 4096 -y -f /dev/$tgtDevNode");

                   # Check for errors
                   $rc = xCAT::zvmUtils->checkOutput( $out );
                   if ( $rc == -1 ) {
                       xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                       # Detatch disks from HCP
                       $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;

                       return;
                   }

                   # Sleep 2 seconds to let the system settle
                   sleep(2);

                   # Copy source disk to target disk
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" );
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync && $::SUDO echo $?"`;
                   $out = xCAT::zvmUtils->trimStr($out);
                   if (int($out) != 0) {
                       # If $? is not 0 then there was an error during Linux dd
                       $out = "(Error) Failed to copy /dev/$srcDevNode";
                   }

                   xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync");
                   xCAT::zvmUtils->printSyslog("$out");
               } else {
                   # Copy source disk to target disk
                   # Block size = 512
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" );
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync && $::SUDO echo $?"`;
                   $out = xCAT::zvmUtils->trimStr($out);
                   if (int($out) != 0) {
                       # If $? is not 0 then there was an error during Linux dd
                       $out = "(Error) Failed to copy /dev/$srcDevNode";
                   }

                   xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync");
                   xCAT::zvmUtils->printSyslog("$out");

                   # Force Linux to re-read partition table
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: Forcing Linux to re-read partition table" );
                   $out =
   `ssh $::SUDOER\@$hcp "$::SUDO cat<<EOM | fdisk /dev/$tgtDevNode
   p
   w
   EOM"`;
               }

               # Check for error
               $rc = xCAT::zvmUtils->checkOutput( $out );
               if ( $rc == -1 ) {
                   xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" );

                   # Disable disks
                   $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr );

                   # Detatch disks from zHCP
                   $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;

                   return;
               }

               # Sleep 2 seconds to let the system settle
               sleep(2);
           } # end if ddcopy
        } # end if SMAPI flashcopy did not complete


        # If not Flashcopy Disable and enable target disk
        if ( !$smapiFlashCopyDone ) {
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr );
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr );

            # Determine target device node (it might have changed)
            $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr);
        }

        # Flush disk
        $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/sync"`;

        # If not Flashcopy disable and detach disk from zhcp
        if ( !$smapiFlashCopyDone ) {
            # Disable disks
            $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr );

            # Detatch disks from HCP
            $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`;
        }

        sleep(5);
    }    # End of foreach (@srcMdisks)

    # Power on target virtual server will be done in later call
}
