#!/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,2007 
# 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 
# @(#)85   1.30   src/csm/core/cmds/mgmtsvr.perl, csmcore, csm_rfish, rfishs001b 4/10/06 13:09:43

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::Std;
require NodeUtils;
require MessageUtils;

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

$::MSGCAT     = 'nodecmds.cat';
$::MSGMAPPATH = "$::csmroot/msgmaps";

@::MSIPs;      # global array of IPs used for the management server
@::NodeIPs;    # global array of IPs used for the node

@::PrevMSIPs;
@::PrevNodeIPs;

# Parse the cmd line args and check them
if (!getopts('n:NhvVdkH:')) { &usage; }
if ($::opt_h)               { &usage(0); }
if (scalar(@ARGV) > 1)      { &usage; }

#Now allow -d & -N to be used with ARGV
#if (scalar(@ARGV) > 0 && ($::opt_d||$::opt_N)) { &usage; }
if (scalar(@ARGV) == 0 && defined($::opt_n)) { &usage; }

if (defined($::opt_n))
{
	&waitForCTRMC();
}

$::VERBOSE = $::opt_v || $::opt_V;
$ENV{'CT_MANAGEMENT_SCOPE'} =
  1;    # set local scope because we only want the local Management Server class
delete $ENV{'CT_CONTACT'};

if ($::opt_H)
{       #set HA management server
	&setHAMS();
	exit;
}

if (scalar(@ARGV) > 0)    # they are setting the mgmt svr
{
    # Support was added to supply multiple ips/hostnames for the
    # -n hostname and the MS_IP_address agruments. The hostname/IPs must be separated
    # by commas and both arguments must have the same number of hostname/ips.
    # For example,
    # mgmtsvr -n 1.1.1.1,2.2.2.2 3.3.3.3,4.4.4.4

	my $ms = shift @ARGV;
    @::MSIPs = split(/,/, $ms);

    foreach my $line (@::MSIPs)
    {
        my $msIp = NetworkUtils->validate_ip($line);
        if ($msIp == -1)
        {
            MessageUtils->message('E','EMsgManagementServerInvalid',$ms);
            exit(1);
        }
    }

    $ms = @::MSIPs[0];

	#my $ms = shift @ARGV;
	#print "opt_n=$::opt_n\n";
	if ($::opt_d)
	{    #just delete this ms
		&rmMgmtSvr($ms);
	}
	elsif ($::opt_n)
	{
        # ensure msips and nodeips have the same number of ips.
        @::NodeIPs = split(/,/, $::opt_n);

        if ( (scalar(@::MSIPs)) != (scalar(@::NodeIPs)) ) {
            # put out error message and exit
            MessageUtils->message('E','EMsgMnMsArgumentLengthMismatch',$ms);
            exit(1);
        }
        $::opt_n = @::NodeIPs[0];

		&setMgmtSvr($ms, $::opt_n);
	}
	else
	{    #just display ms
		&displayMS($ms);
	}
}
elsif ($::opt_d)    # they are removing the mgmt svr
{
	&rmMgmtSvr();
}
elsif (!$::opt_d)    # display ALL mgmt svrs
{
	&displayMS();
}

exit;

# Return true if this machine is the mgmt svr for itself.
sub isOwnMgmtSvr
{
	my ($ms, $lhn) = @_;

	# If this is a mgmt svr for itself then we should be able to look up the $lhn in the ManagedNode
	# class and the ManagementServer attribute should match $ms.
	my $outref =
	  NodeUtils->runrmccmd('lsrsrc-api', "-i -D ':|:'",
				   qq(-s IBM.ManagedNode::"Hostname='$lhn'"::ManagementServer), -1);
	if (scalar(@$outref) == 0) { return 0; }
	return $$outref[0] eq $ms;
}

# Return the mgmt svr and local hostname from the IBM.ManagementServer resource class.
# This function assumes we are on a node.
sub getMgmtSvr
{
	my ($name) = @_;    #if they specify a ms name
	my $outref;
	my %MgmtSvrs;
	if ($name)
	{
		$outref =
		  NodeUtils->runrmccmd(
			'lsrsrc-api',
			"-i -D ':|:'",
			qq(-s IBM.ManagementServer::"ManagerType='CSM' && Hostname='$name'"::LocalHostname::HAState)
		  );
	}
	else
	{
		$outref =
		  NodeUtils->runrmccmd(
			'lsrsrc-api',
			"-i -D ':|:'",
			qq(-s IBM.ManagementServer::"ManagerType='CSM'"::Hostname::LocalHostname::HAState)
		  );
	}
	if (scalar(@$outref) == 0) { return; }
	foreach my $line (@$outref)
	{    #there can be multiple ms defined
		my ($ms, $lhn, $state) = split(/:\|:/, $line);
		$MgmtSvrs{$ms}   = $lhn;
		$::MHAState{$ms} = $state;
	}
	return (\%MgmtSvrs);
}

sub setMgmtSvr
{
	my ($ms, $lhn) = @_;

	if (!NodeUtils->isNode()) {
		MessageUtils->message('E41', 'EMsgNOT_A_NODE');
	}

	my $cmd;
	my $changedHostname = 1;    # used to know if we need to refresh at the end
	     # See if there is already an entry - if so, replace it
	my $MgmtSvrs = &getMgmtSvr();
	my @servers  = keys %$MgmtSvrs;
	foreach my $prevms (@servers)
	{
		my $prevlhn = $$MgmtSvrs{$prevms};
		if ($ms ne $prevms)    # the Hostname has changed
		{
			if (!defined $$MgmtSvrs{$ms})
			{                  #not in an HA MS cluster
				 # Undefine the resource and recreate it.  This is necessary to get the DMSRM to go
				 # through the full processing in the case that this is really a new mgmt svr.  In the
				 # case in which this is the same mgmt svr with a name change, this will also be ok.
				NodeUtils->runrmccmd('rmrsrc-api', '',
							 qq(-s IBM.ManagementServer::"Hostname='$prevms'"));
                my $lhnl = join('","', @::NodeIPs);
                my $msnl = join('","', @::MSIPs);
				my $attr2 = length($lhn) ? qq(::LocalHostname::"$lhn") : '';
                NodeUtils->runrmccmd(
                    'mkrsrc-api', '', 
                    qq(IBM.ManagementServer::Hostname::"$ms"::ManagerType::"CSM"::LocalHostNamesList::'{"$lhnl"}'::MgmtSvrHostNamesList::'{"$msnl"}'$attr2)
				);

			}
			else
			{ #HA MS cluster, prevms is the inactive MS so change its HAState to 0
				NodeUtils->runrmccmd(
					'chrsrc-api',
					'',
					qq(-s IBM.ManagementServer::"Hostname='$prevms'"::HAState::0)
				);
			}
		}
		elsif (length($lhn)
			   && $lhn ne
			   $prevlhn)    # Hostname did not change, but LocalHostname did
		{

			# Note: we don't need to process the -k option in this case because the
			#       ManagementServer resource class will exchange keys if either attribute
			#       changed.

			NodeUtils->runrmccmd(
				'chrsrc-api',
				'',
				qq(-s IBM.ManagementServer::"Hostname='$prevms'"::LocalHostname::"$lhn")
			);

			#my $opflag = '0';
			#NodeUtils->runrmccmd('runact', '', qq(-c IBM.ManagementServer RefreshNodeInfo MgmtSvrHostname=$ms OperationFlag=$opflag));
			#todo: handle the no resources found error in case the resource was deleted after we checked it above.
		}
		elsif ($::opt_k
		  )  # no need to change the resource but need to run the refresh action
		{
			$changedHostname = 0;
			my $opflag = '0';

			#NodeUtils->runrmccmd('runact', '', qq(-c IBM.ManagementServer RefreshNodeInfo MgmtSvrHostname=$ms OperationFlag=$opflag));
			NodeUtils->runrmccmd(
				'runact-api',
				'',
				qq(-c IBM.ManagementServer::::RefreshNodeInfo::MgmtSvrHostname::"$ms"::OperationFlag::"$opflag")
			);
		}
        elsif( $ms eq $prevms && $lhn eq $prevlhn )
        {
            # if we are running on an hmc then remove this ManagementServer Object and add it
            # back with
            if (-e "/opt/hsc/bin/lshsc") 
            {
                NodeUtils->runrmccmd('rmrsrc-api', '', qq(-s IBM.ManagementServer::"Hostname='$prevms'"));
                my $lhnl = join('","', @::NodeIPs);
                my $msnl = join('","', @::MSIPs);
                my $attr2 = length($lhn) ? qq(::LocalHostname::"$lhn") : '';
                NodeUtils->runrmccmd('mkrsrc-api', '', qq(IBM.ManagementServer::Hostname::"$ms"::ManagerType::"CSM"::LocalHostNamesList::'{"$lhnl"}'::MgmtSvrHostNamesList::'{"$msnl"}'$attr2));
            }
        }
	}
	if (scalar(keys %$MgmtSvrs) == 0)    # create the entry
	{
		my $attr2 = length($lhn) ? qq(::LocalHostname::"$lhn") : '';
        my $lhnl = join('","', @::NodeIPs);
        my $msnl = join('","', @::MSIPs);

		NodeUtils->runrmccmd(
			 'mkrsrc-api',
			 '',
             qq(IBM.ManagementServer::Hostname::"$ms"::ManagerType::"CSM"::LocalHostNamesList::'{"$lhnl"}'::MgmtSvrHostNamesList::'{"$msnl"}'$attr2)
		);
	}

	# Refresh the rmc daemon so it picks up the acl file that was possibly modified as a
	# result of changing the management server
	if ($changedHostname) { NodeUtils->runrmccmd('refresh', '-s ctrmc'); }
}

sub rmMgmtSvr
{
	my ($server) = @_;
	if (!NodeUtils->isNode()) {
		MessageUtils->message('E41', 'EMsgNOT_A_NODE');
	}

	#my ($ms, $lhn) = &getMgmtSvr();
	my $MgmtSvrs = &getMgmtSvr();
	foreach my $ms (keys %$MgmtSvrs)
	{
		if ($server && $ms ne $server) { next; }
		if (length($ms))
		{
			NodeUtils->runrmccmd('rmrsrc-api', '-i',
								 qq(-s IBM.ManagementServer::"Hostname='$ms'"));
		}
	}
}

# This is not currently used
#sub checkMgmtSvr
#  {
#	my ($class, $force) = @_;
#	my $ms = NodeUtils->getMgmtSvr($force);
#	if (length($ms))       # we are on a node
#	  {
#		# Set the environment var so RMC will connect remotely
#		NodeUtils->msg($msgs, 'V', 'CONNECTING_REMOTELY', $ms);
#		$ENV{'CT_CONTACT'} = $ms;
#	  }
#  }

sub isCTRMCrunning
{
	my @output = NodeUtils->runcmd("LANG=C /usr/bin/lssrc -s ctrmc", -1);
	if ($::RUNCMD_RC)
	{
		return 0;
	}    # maybe we should try to catch real errors here
	my ($subsys, $group, $pid, $status) = split(' ', $output[1]);
	if (defined($status) && $status eq 'active')
	{

		#now check to see if IBM.CSMAgentRM is up
		@output =
		  NodeUtils->runcmd("LANG=C /usr/bin/lssrc -s IBM.CSMAgentRM", -1);
		if ($::RUNCMD_RC)
		{
			return 0;
		}    # maybe we should try to catch real errors here
		($subsys, $group, $pid, $status) = split(' ', $output[1]);
		if (defined($status) && $status eq 'active')
		{
			return 1;
		}
		else { return 0; }
	}
	else { return 0; }
}

sub waitForCTRMC
{

	# Wait for the ctrmc and IBM.CSMAgentRM daemons to start. ctrmc will start CSMAgentRM
	# This is important during the first boot of a machine when ctrmc must
	# get started from /etc/inittab before mgmtsvr is successful.
	my $i;
	for ($i = 1 ; $i <= 15 ; $i++)
	{
		if (&isCTRMCrunning())
		{
			last;
		}
		sleep 2;
	}
	if ($i > 14)
	{
		MessageUtils->message('W', 'EMsgDAEMON_NOT_STARTED', "ctrmc");
	}
}

sub displayMS
{

	# These are the various cases:
	# - only a node, mgmt svr set - rc=0, display: foo.com
	# - only a node, mgmt svr not set - rc=11, display: This node does not currently have....
	# - mgmt svr/node, mgmt svr set to self - rc=21, display: foo.com (This node is the mgmt svr for itself.)
	# - mgmt svr/node, mgmt svr set to other mgmt svr - rc=22, display: foo.com (This node is also a mgmt svr for other nodes.)
	# - mgmt svr/node, mgmt svr not set - rc=23, display: This machine is a CSM mgmt svr and a node, but the node does not currently...
	# - only a mgmt svr - rc=31, display: This machine is a CSM management server.
	# - more than 1 mgmt svr defined on this node - rc=41, display: This machine has multiple management servers:\nfoo.com\nbar.com
	# - neither a mgmt svr or a node - rc=101, display: This machine is neither a CSM mgmt svr or node.

	my ($ms) = @_;
	if (NodeUtils->isNode())
	{
		my $MgmtSvrs = &getMgmtSvr($ms);
		my $numkeys  = keys %$MgmtSvrs;
		if (keys %$MgmtSvrs <= 1)
		{    #only 1 MS defined
			my @all = keys %$MgmtSvrs;
			my $ms  = $all[0];
			my $lhn = $$MgmtSvrs{$ms};
			if ($::opt_N)
			{
				MessageUtils->message('O', 'IMsgMS_AND_LHN', $ms, $lhn);
			}
			elsif (!NodeUtils->isMgmtSvr())
			{
				if (length($ms)) { print "$ms\n"; }
				else { MessageUtils->message('I11', 'IMsgNOT_SET'); }
			}
			else    # also a mgmt svr
			{
				if (length($ms))
				{
					if (&isOwnMgmtSvr($ms, $lhn))
					{
						MessageUtils->message('I21', 'IMsgBOTH_ITSELF', $ms);
					}
					else {
						MessageUtils->message('I22', 'IMsgBOTH_OTHER', $ms);
					}
				}
				else { MessageUtils->message('I23', 'IMsgBOTH_NOT_SET'); }
			}
		}
		else
		{    #more than 1 MS defined
			my @servers = keys %$MgmtSvrs;
			MessageUtils->message('I', 'IMsgMultipleMS');
			foreach my $ms (@servers)
			{
				if ($::opt_N)
				{
					my $lhn = $$MgmtSvrs{$ms};
					MessageUtils->message('O', 'IMsgMS_AND_LHN', $ms, $lhn);
				}
				else
				{
					print "$ms\n";
				}
			}
			exit 41;
		}
	}
	elsif (NodeUtils->isMgmtSvr())    # only a mgmt svr
	{
		MessageUtils->message('I31', 'IMsgMGMT_SVR');
	}
	else    # neither a node or mgmt svr (must only have csm.core installed)
	{
		MessageUtils->message('E101', 'EMsgNEITHER');
	}

}

sub setHAMS
{

	my $configinfo = "/opt/csm/install/configinfo";
	my $hostname;
	if (-e $configinfo)
	{
		open(CONFIG, $configinfo) or die 
        MessageUtils->messageFromCat(
                                     'csmInstall.cat',    $::MSGMAPPATH, 
                                     'csminstall',        'E1', 
                                     'EMsgCANT_OPEN',     $configinfo
                                     );
		while (my $line = <CONFIG>)
		{
			chomp $line;
			if ($line =~ /^Hostname=(.*)/)
			{
				$hostname = $1;
				last;
			}
		}
		close CONFIG;
		my $ms    = $::opt_H;
		my $state = 0;
		if (@ARGV) { $state = 1; }
		my $MgmtSvrs = &getMgmtSvr();
		my @servers  = keys %$MgmtSvrs;
		my $made     = 0;

		foreach my $exist_ms (@servers)
		{
			if ($exist_ms eq $ms)
			{
				if (   ($hostname ne $$MgmtSvrs{$ms})
					|| ($::MHAState{$ms} != $state))
				{

					#remove it
					NodeUtils->runrmccmd('rmrsrc-api', '',
								 qq(-s IBM.ManagementServer::"Hostname='$ms'"));
				}
				else
				{
					$made = 1;
				}
			}
			else
			{    #inactive ms, set its HAtate to 0
				NodeUtils->runrmccmd(
					'chrsrc-api',
					'',
					qq(-s IBM.ManagementServer::"Hostname='$exist_ms'"::HAState::0)
				);
			}
		}
		if (!$made)
		{
			NodeUtils->runrmccmd(
				'mkrsrc-api',
				'',
				qq(IBM.ManagementServer::Hostname::"$ms"::ManagerType::"CSM"::LocalHostname::${hostname}::HAState::$state)
			);
		}
	}
}

