#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2000,2008 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)27   1.86   src/csm/core/cmds/rmnode.perl, csmcore, csm_rgar2h, rgar2hs001a 10/9/07 01:29:48

use strict;
use locale;

BEGIN
{
	# this enables us to redirect where it looks for other CSM files during development
	$::csmroot = $ENV{'CSM_ROOT'} ? $ENV{'CSM_ROOT'} : '/opt/csm';
	$::csmpm   = "$::csmroot/pm";
	$::csmbin  = "$::csmroot/bin";
}
use lib $::csmpm;
use Getopt::Long
require NodeUtils;
require MessageUtils;
require ServerUtils;
require NetworkUtils;
require CSMDefs;
require InstallKRB5Utils;
require NodesetUtils;
require DCUtils;
require DCAPI;

# get the global definitions we need for this command
# in this case get_OSDefs uses the attrs of the management
# server to identify the correct definitions
%::myOSDefs = ServerUtils->get_OSDefs();

# For the usage, see nodecmds.msg
sub usage
{
	MessageUtils->message('I', 'IMsgRmnodeUsage');
	exit(scalar(@_) ? $_[0] : 1);
}

# dsh will not set default PATH. We run some commands without full path name 
# before eso release, so we should set PATH manually.
$ENV{'DSH_PATH'}='/usr/bin:/bin:/usr/sbin:/sbin';
$::DELETE_CFM_FILES = $ENV{'CSM_CFM_DELETE_FILES'};
# set local scope because we only want classes on the mgmt svr
$ENV{'CT_MANAGEMENT_SCOPE'} = 1;    
$::MSGSET     = 'rmnode';               # setting this explicitly
$::MSGCAT     = 'nodecmds.cat';
$::MSGMAPPATH = "$::csmroot/msgmaps";

# save input for log
my @command_line = ();
@command_line = @ARGV;
my $command_line = $0 . " " . join(" ", @command_line);

# Parse the cmd line args and check them
$Getopt::Long::ignorecase = 0;    #Checks case in GetOptions
#allows short command line options to be grouped (e.g. -av)
Getopt::Long::Configure("bundling") ;
if (!GetOptions(
                 'h',
                 'v',
                 'V',
                 'u',
                 'N=s',
                 'n=s',
                 'f=s',
                 'force'	=> \$::RMNODE_FORCE,
                ))
{
    &usage;
}	

if ($::opt_h) { &usage(0); }
if (   scalar(@ARGV) < 1 && !defined($::opt_N)
	&& !defined($::opt_f) && !defined($::opt_n))
{
	&usage;
}

if ($::RMNODE_FORCE)
{
    if (NodeUtils->isHMC())
    {
        MessageUtils->message('E', 'EMsgForceFlagNotSupportedOnHMC');
        exit 2;
    }
}

$::VERBOSE       = $::opt_v || $::opt_V;
$::RMNODE_CLIENT = "/opt/csm/csmbin/rmnode_client";
$::RMNODE_LOG="/var/log/csm/rmnode.log";
MessageUtils->start_logging($::RMNODE_LOG);
MessageUtils->message('LI', 'IMsgrmnodeCmd', $command_line);
if ($::DELETE_CFM_FILES) {
  MessageUtils->message('LI', 'IMsgrmnodeEnv');
}

#
# Get KRB5 credentials
#
my $krb5_tgt   = 0;
my $KRB5CCNAME = "";
$KRB5CCNAME = InstallKRB5Utils->getKRB5tgt;
if ($KRB5CCNAME ne "")
{
	$ENV{'KRB5CCNAME'} = $KRB5CCNAME;
	$krb5_tgt = 1;
}

my ($nodeListRef, $lsnodeInfoRef, $nodeHashRef) =
  NodeUtils->get_target_nodes(\@ARGV,'',$::opt_f);
$ENV{'DC_ENVIRONMENT'}="CSM";

#Backup the raw NodeList
my @backup_NodeList = @$nodeListRef;

#Check the IS that still being used by other nodes in the cluster.
my $IStoNodelistRef = ServerUtils->getHashInstallServerToNodeInCluster($nodeListRef);
my %IStoNodelist = %$IStoNodelistRef;
if (%IStoNodelist)
{
    foreach my $IS (keys %IStoNodelist)
    {
        #Exclude the IS that all the nodes it served exist in the $nodeListRef
        my $being_used_IS_flag;
        my @available_nodes;
        foreach my $node_served_by_IS (@{$IStoNodelist{$IS}})
        {
            if (!grep (/^$node_served_by_IS$/, (@$nodeListRef)))
            {
                push @available_nodes, $node_served_by_IS;
            }
        }
        if (!scalar (@available_nodes))
        {
            next;
        }

        #Print appropriate message for being used IS
        my $tmp_nodelist = join ',', @available_nodes;
        if ($::RMNODE_FORCE)
        {
            if (scalar (@available_nodes) <= 10)
            {
                 MessageUtils->message('I', 'IMsgRmUsedISForcibly', $IS, $tmp_nodelist);
            }
            else
            {
                 MessageUtils->message('I', 'IMsgRmUsedISForciblyMany', $IS);
            }
        }
        else
        {
            #Exclude the invalid IS from the $nodeListRef
            if (grep (/^$IS$/, (keys %$nodeHashRef)))
            {
                delete $$nodeHashRef{$IS};
            }
            if (scalar (@available_nodes) <= 10)
            {
                MessageUtils->message('E', 'EMsgCannotRmUsedIS', $IS, $tmp_nodelist);
            }
            else
            {
                MessageUtils->message('E', 'EMsgCannotRmUsedISMany', $IS);
            }
        }
    }
}

if (!%$nodeHashRef)
{
    if ($::RMNODE_FORCE)
    {
        goto DELETE_NODES;
    }
    else
    {
        exit 2;
    }
}

#Check out the Nodes that do not match the rmnode condition. And correct some
#attributes if possible.
my %check_opt;
$check_opt{'Rmnode'} = 1;
my $except_node;
#Exclude the exceptional nodes to the $except_node
($nodeHashRef, $except_node) = ServerUtils->common_node_check_correct($nodeHashRef, \%check_opt);

if ($::RMNODE_FORCE)
{
    if (!%$nodeHashRef)
    {
        goto DELETE_NODES;
    }
}
else
{
    if (%$except_node)
    {
        #There are some nodes that do not match the conditon to be removed correctly.
        exit 2;
    }
}

#Get the IS Hash from Node Hash
my ($rc, $nodeHashRef, $ISHashRef) = ServerUtils->getInstallServerHashFromNode($nodeHashRef, 1);
if ($rc)
{
    #There are some nodes have an unavailable IS
    if ($::RMNODE_FORCE)
    {
	goto DELETE_NODES;
    }
    else
    {
        exit 2;
    }
}
my $skip_generate_scripts_list = 1;
my $nodeinfoHashRef = ServerUtils->make_nodeinfo_hash($nodeHashRef, $skip_generate_scripts_list);
my $ISinfoHashRef = ServerUtils->make_nodeinfo_hash($ISHashRef, $skip_generate_scripts_list);
my %clusterInfoHash = ();
$clusterInfoHash{"logfile_DCrmnode"}     = "$::RMNODE_LOG";
$clusterInfoHash{"logfile_DCmakedhcp"}     = "$::RMNODE_LOG";
ServerUtils->set_cluster_info_hash(\%clusterInfoHash);

my $skipFileLoad = 0;
DCAPI->DCrmnode(\%clusterInfoHash,$nodeinfoHashRef,$ISinfoHashRef,$skipFileLoad,$::VERBOSE);


DELETE_NODES:
my $nodelist;
my @nodelist_array;
if ($::RMNODE_FORCE)
{
    $nodelist = \@backup_NodeList;
}
else
{
    @nodelist_array = (keys %$nodeHashRef);
    $nodelist = \@nodelist_array;
}

my $where = qq/"Hostname IN ('XXX')"/;
my @cleanup_nodes;

if ($::opt_u)
{
	#Make a Hash of ManagedNodes as keys with their Managemnent Sever as the value
	%::MgNodeMgSvrHash;
	%::MgNodeMgOSnameHash;
	my ($mynode_file, $myhostname, $mymanagemnentserver, $installmethod, @myNodeList, $myDshCmd,
		$myNodeOSName, $mymode, @localresults);
	my ($myval, $myval1, $myval2);
	@myNodeList = @$nodelist;
	my $myresults =
	  NodeUtils->listNodeAttrs(\@myNodeList,
							 "Hostname, ManagementServer, Mode, InstallOSName, InstallMethod");

	foreach $myval (@$myresults)
	{
		($myhostname, $mymanagemnentserver, $mymode, $myNodeOSName, $installmethod) = split(':\|:', $myval);
		if (!($mymode =~ /PreManaged/) and !($mymode =~ /$::CSMLITE/)
			and !($installmethod =~ /warewulf/))
		{
			$::MgNodeMgSvrHash{"$myhostname"}    = $mymanagemnentserver;
			$::MgNodeMgOSnameHash{"$myhostname"} = $myNodeOSName;
			push(@cleanup_nodes, $myhostname);
		}
	}

	if (scalar(keys %::MgNodeMgSvrHash) != 0)
	{
		#Run dsh command on all the nodes to get the Management Server fromn the node

		$myDshCmd =
		  "lsrsrc-api -s IBM.ManagementServer::\"ManagerType='CSM'\"::Hostname";
		my %options_api = ();
		$options_api{'verify'} = 1;
		$options_api{'command'} = "lsrsrc-api -s IBM.ManagementServer::\"ManagerType='CSM'\"::Hostname";
                $options_api{'nodes'} = join(',', @cleanup_nodes);
		@localresults = NodeUtils->runDsh(\%options_api, -1);
		if ($::RUNCMD_RC != 0)
		{
			MessageUtils->messageFromCat('csmInstall.cat', $::MSGMAPPATH,
										 'csminstall', 'E', 'EMsgNO_DSH');
		}

		$myval = "";

		# For each ManagedNode compare the Management server value with value from the dsh command
		# if not equal the delete the node
		foreach my $myval (@localresults)
		{
			chomp $myval;
			($myval1, $myval2) = split(': ', $myval);
			if ($::MgNodeMgSvrHash{"$myval1"} ne $myval2)
			{
				delete $::MgNodeMgSvrHash{"$myval1"};
				delete $::MgNodeMgOSnameHash{"$myval1"};
			}
		}
	}
}

# Delete the nodes
my $outref =
  NodeUtils->runrmccmd('rmrsrc-api', '-i', qq(-s IBM.ManagedNode::$where),undef,$nodelist);
my $numfound  = scalar(@$outref);
my $numtofind = (defined($nodelist) ? scalar(@$nodelist) : 0);

if ($numfound < $numtofind)
{
	$::MSGSET = 'lsnode';
	if (scalar(@$nodelist) == 1)
	{
		MessageUtils->message('E12', 'EMsgNODE_NOT_FOUND', $$nodelist[0]);
	}
	elsif (scalar(@$nodelist) <= 10)
	{
		MessageUtils->message('W12', 'EMsgSOME_NOT_FOUND_LIST',
							  join(', ', @$nodelist));
	}
	else { MessageUtils->message('W12', 'EMsgSOME_NOT_FOUND'); }
}

# wait for DMSRM to be recycled after removing the resource.
# this will prevent the removed node to come back alive after restarting RMC.
sleep(1); 

#if the -u option is set then run rmnode.client on the node which in turn
#uninstalls csm, rsct packages and logs for Linux and log files for AIX on the
#node after removing the node from the database
if ($::opt_u)
{

	if ($::PLTFRM eq "AIX")
	{
		#  Ensure the binaries in /csminstall/csm are current
		ServerUtils->copyBinaries;
	}

	# use dsh run /opt/csm/csmbin/rmnode.client
	# and unmount
	if (scalar @cleanup_nodes)
	{
		my $ref_nodelist = moveRmnode_client(\@cleanup_nodes);
		@cleanup_nodes = @$ref_nodelist;
		if ((scalar @cleanup_nodes) and &run_DSH_Commands(\@cleanup_nodes) != 0)
		{
			# An error occured when executing DSH
			MessageUtils->messageFromCat('csmInstall.cat', $::MSGMAPPATH,
										 'csminstall', 'E2', 'EMsgNO_DSH');
		}
	}

	MessageUtils->messageFromCat('csmInstall.cat', $::MSGMAPPATH, 'csminstall',
								 'I', 'IMsgDidnotUninstallOpenSrcPkgs');
	MessageUtils->messageFromCat(
								 'csmInstall.cat',
								 $::MSGMAPPATH,
								 'csminstall',
								 'I',
								 'IMsgDidnotUnconfigureRemoteShellNode'
								);

}

# get rid of KRB5 tgt if gotten
if ($krb5_tgt == 1)
{
	NodeUtils->runcmd("$::CSMCSMBIN/k5destroytgt 2>&1", -1);    # never errors
}

MessageUtils->stop_logging();
exit;

#--------------------------------------------------------
#
# Sub routines
#
#--------------------------------------------------------
#####################################################################
sub run_DSH_Commands
{
	my ($nodelist) = @_;
	my @dsh_results;
	my $rc                = 0;
	my $rmnode_client_cmd = $::RMNODE_CLIENT;
	if ($::VERBOSE)
	{
		$rmnode_client_cmd .= " -v";
	}
	my @local_dsh_results = ();
	my $dsh_command       = "";
        my %options_api = ();
	$options_api{'verify'} = 1;
	$dsh_command .= "$rmnode_client_cmd 2>&1; ";
	$options_api{'command'} = $dsh_command;
        $options_api{'nodes'} = join(',', @$nodelist);
        @local_dsh_results = NodeUtils->runDsh(\%options_api, -1);
	$rc                = $::RUNCMD_RC;

	if ($rc != 0)
	{
		# Error executing DSH.
		MessageUtils->messageFromCat('csmInstall.cat', $::MSGMAPPATH,
									 'csminstall', 'E', 'EMsgNO_DSH');
	}
	chop @local_dsh_results;
    # push @dsh_results, @local_dsh_results;
    # print errors
	foreach my $line (@local_dsh_results)
	{
		MessageUtils->messageFromCat('csmInstall.cat', $::MSGMAPPATH,
									 'csminstall', 'I', 'IMsgShow_Output',
									 $line);
	}
	return $rc;
}

sub moveRmnode_client
{
	my ($nodelist) = @_;

	#first determine which machines do not have /opt/csm/csmbin/rmnode_client
	my @local_dsh_results = ();
	my $dsh_command       = "";
	my $ENV_OLD = $ENV{'LANG'};
	$ENV{'LANG'} = "C";
	my %options_api = ();
	$options_api{'verify'} = 1;
	$dsh_command .= "$::LS $::RMNODE_CLIENT 2>&1; ";
	$options_api{'command'} = $dsh_command;
        $options_api{'nodes'} = join(',', @$nodelist);
	@local_dsh_results = NodeUtils->runDsh(\%options_api, -1);
	$ENV{'LANG'} = $ENV_OLD;
    
	my @nocsm_nodes;
	foreach my $line (@local_dsh_results)
	{
		chomp $line;
		my ($node, $info) = split ':', $line, 2;
		if ($info =~ m/No such file or directory/)
		{
			push @nocsm_nodes, $node;
		}
	}
	my @target_nodes = ();
	
	if (scalar @nocsm_nodes)
	{
		MessageUtils->message('I', 'IMsgNoCommandInNodes',"$::RMNODE_CLIENT", 
							  join(",", @nocsm_nodes)); 
		foreach my $node (@$nodelist)
		{
			if (! grep /^$node$/, @nocsm_nodes)
			{
				push @target_nodes, $node;
			}
		}
		return \@target_nodes;
	}	
	return $nodelist;
}
