#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#
#####################################################
#
# This script will read the CSM database and build
# xCAT stanza files to be input into an xCAT database
# using the chdef command
# example node.stanza | chdef -z
#
#####################################################

use strict;
use warnings;
use Getopt::Long;
use Data::Dumper;

my $needhelp  = 0;
my $directory = "/tmp/csm2xcat";

if (
    !GetOptions("help"  => \$needhelp,
                "dir=s" => \$directory,)
  )
{
    &usage;
    exit 1;
}

if ($needhelp)
{
    &usage;
    exit 0;
}

#Create the users choice directory
mkdir $directory unless -d $directory;

# create a log
open(LOG, ">$directory/conversion.log")
  or die "Can't open logfile for writing: $!";
&log_this("Conversion started at " . scalar(localtime()));

# build the stanza files for the site table
#  rsh/rcp attribute not longer supported.  Leave logic here
# incase we decide there are other attribute needed
#&getSiteinfo;

# build the stanza files for the node
&getNodeinfo;

# build the stanza files for the devices
&getDevinfo;

log_this("Conversion finished at " . scalar(localtime()));
print
  "Conversion finished, please carefully review $directory/conversion.log and check results in the $directory  stanza files before using chdef -z to update the database!\n";
close(LOG);
exit 0;

# end main

#
# logger
#

sub log_this
{
    print LOG join('', @_), "\n";
}

#
# write to stanza files
#
sub write_stanza
{
    print STANZA @_;
}

#
# runs csmconfig or uses the csmconfig.output file
#  and builds site table stanza file
#

sub getSiteinfo
{
    log_this("Reading Site information\n");
    print "Running csmconfg to read site info!\n";

    # open the site stanza file
    my $stanzafile = "$directory/site.stanza";
    open(STANZA, ">$stanzafile")
      or die "Can't open $stanzafile for writing: $!";
    write_stanza("# <xCAT data object stanza file>\n\n");
    write_stanza("clustersite:\n");
    write_stanza("    objtype=site\n");

    my @results;
    my $cmd;

    # use of file is for debug
    my $csmconfiginfofile = "$directory/csmconfigdebug.output";
    if (-e $csmconfiginfofile)
    {    # use the imported file
        $cmd = "cat $csmconfiginfofile";
        log_this("Reading $csmconfiginfofile information\n");
        @results = runcmd($cmd);
    }
    else
    {    # run the command
        $cmd     = "/opt/csm/bin/csmconfig";
        @results = runcmd($cmd);
    }
    if ($::RUNCMD_RC != 0)
    {
        my $msg = "Error processing csmconfig information\n";
        log_this($msg);
        print "$msg";
        close(STANZA);
        return;
    }

    foreach my $line (@results)
    {
        my $xcatline;
        $line =~ s/\s*//g;    #remove extra blanks
        my ($attr, $value) = split(/=/, $line);
        if ($attr eq "RemoteShell")
        {

            if ($value)
            {
                $xcatline = "    rsh=";
                $xcatline .= "$value\n";
            }
        }
        if ($attr eq "RemoteCopyCmd")
        {

            if ($value)
            {
                $xcatline = "    rcp=";
                $xcatline .= "$value\n";
            }
        }
        if ($xcatline)
        {
            write_stanza($xcatline);
        }
    }
    close(STANZA);
    return 0;
}

#
# runs lsnodes -l  and build node stanza file
#

sub getNodeinfo
{

    # open the node stanza file
    my $stanzafile = "$directory/node.stanza";
    open(STANZA, ">$stanzafile")
      or die "Can't open $stanzafile for writing: $!";
    write_stanza("# <xCAT data object stanza file\n");
    log_this("Reading Node information\n");
    print "Running lsnode -l for node info!\n";

    my $OS       = "";
    my $GRP      = "";
    my $ARCH     = "";
    my $DISTNAME = "";

    my @results;
    my $cmd;
    my $lsnodefile = "$directory/lsnodedebug.output";

    # use of file is for debug
    if (-e $lsnodefile)
    {    # use the imported file
        log_this("Reading $lsnodefile information\n");
        $cmd     = "cat $lsnodefile";
        @results = runcmd($cmd);
    }
    else
    {    # run the command
        $cmd     = "/opt/csm/bin/lsnode -l";
        @results = runcmd($cmd);
    }
    if ($::RUNCMD_RC != 0)
    {
        my $msg = "Error processing lsnode information\n";
        log_this($msg);
        print "$msg";
        close(STANZA);
        return;
    }

    #
    #  build hash  of lsnode info
    #
    my $output   = buildNodehash(\@results);
    my %nodehash = %$output;
    my %attrhash;
    my $nodename;
    my $attributes;

    # my $test = $nodehash{"c55n03.ppd.pok.ibm.com"}{"PowerStatus"};

    while (($nodename, $attributes) = each %nodehash)
    {

        #
        # build commonly needed attributes
        #
        %attrhash = %$attributes;

        # get osname, distribution name for later
        $OS = $attrhash{"InstallOSName"};
        my $tmpname = $attrhash{"InstallDistributionName"};
        $DISTNAME = lc($tmpname);
        my $pkgarch = $attrhash{"InstallPkgArchitecture"};
        if ($pkgarch)
        {
            if ($OS eq "LINUX")
            {
                $ARCH = $pkgarch;    # save arch
            }
            else
            {                        #AIX
                $ARCH = "ppc64";
            }
        }

        #
        # first process the nodename
        #
        my $value;
        my $xcatline;
        my $attr;
        my $grplist = get_groups($nodename);
        if (!(grep /compute/, $grplist))
        {
            $grplist .= ",compute";
        }
        if (!(grep /all/, $grplist))
        {
            $grplist .= ",all";
        }
        $GRP = $grplist;    # save for setting up other tables
        my $shortnodename = get_shortname($nodename);
        $xcatline = "$shortnodename:\n";    # write node name
        $xcatline .= "    objtype=node\n";
        if ($grplist)
        {
            $xcatline .= "    groups=$grplist\n";
            $xcatline .= "    profile=compute\n";
        }
        $xcatline .= "    status=defined\n";

        if ($xcatline)
        {
            write_stanza($xcatline);
            $xcatline = "";
        }

        #now process all the attributes associated with the nodename
        #my $test = $attrhash{"InstallServer"};
        #my $test1 = $attrhash{"Mode"};

        while (($attr, $value) = each %$attributes)
        {
            #if ($attr eq "InstallServer")
            #{
             #   if ($value)
              #  {
               #     $xcatline = "    xcatmaster=$value\n";
                #    $xcatline .= "    servicenode=$value\n";

                #}
            #}
            # since we are suggesting that the xCAT MN is a different machine
            # than the CSM MS,  we will not use the Management Server value
            #if ($attr eq "ManagementServer")
            #{

            #   my $installserver = $attrhash{"InstallServer"};
            #    if ($installserver eq "")    # if no install server, use MN
            #    {
            #        if ($value)
            #        {
            #            $xcatline = "    xcatmaster=$value\n";
            #        }
            #    }
            #}
            if ($attr eq "InstallOSName")
            {
                if ($value)
                {
                    if ($value eq "AIX")
                    {
                        $xcatline = "    os=$value\n";
                        $xcatline .= "    arch=ppc64\n";
                    }
                }
            }
            if ($attr eq "InstallDistributionVersion")
            {
                if (   $value
                    && $DISTNAME)    # put together DistributeName and Version
                {
                    $xcatline = "    os=$DISTNAME";
                    $xcatline .= "$value\n";
                }
            }
            if ($attr eq "InstallPkgArchitecture")    # filled in for Linux
            {
                if ($value)
                {
                    if ($OS eq "LINUX")
                    {
                        $xcatline = "    arch=$value\n";
                        if ($value =~ /86/)
                        {
                            $xcatline .= "    netboot=pxe\n";
                        }
                        if ($value =~ /ppc/)
                        {
                            $xcatline .= "    netboot=yaboot\n";
                        }

                    }
                }
            }
            if ($attr eq "HWControlPoint")
            {
                if ($value) 
                {
                    $xcatline = "    hcp=$value\n";
                }
            }
            if ($attr eq "LParID")
            {
                if ($ARCH =~ /ppc/)
                {
                    if ($value)
                    {    # this is an LPAR
                        $xcatline = "    id=$value\n";
                        $xcatline .= "    nodetype=lpar\n";
                    }
                }
            }
            if ($attr eq "ConsoleMethod")
            {
                if ($value)
                {
                    $xcatline = "    cons=$value\n";
                }
            }
            if ($attr eq "ConsolePortNum")
            {
                if ($value)
                {
                    $xcatline = "    termport=$value\n";
                }
            }
            if ($attr eq "PowerMethod")
            {
                if ($value)
                {
                    if ($value eq "xseries")
                    {
                        $value = "ipmi";
                    }
                    $xcatline = "    power=$value\n";
                    $xcatline .= "    mgt=$value\n";
                }
            }
            if ($attr eq "InstallAdapterMacaddr")
            {
                if ($value)
                {
                    $xcatline = "    mac=$value\n";
                }
            }
            if ($attr eq "HWSerialNum")
            {
                if ($value)
                {
                    $xcatline = "    serial=$value\n";
                }
            }
            if ($attr eq "ConsoleSerialSpeed")
            {
                if ($value)
                {
                    $xcatline = "    serialspeed=$value\n";
                    $xcatline .= "    serialflow=hard\n";    # hardcoded
                }
            }
            if ($attr eq "ConsoleSerialDevice")
            {
                if ($value)
                {
                    $xcatline = "    serialport=$value\n";
                }
            }
            if ($attr eq "ConsoleServerName")
            {
                if ($value)
                {
                    $xcatline = "    termserver=$value\n";
                }
            }
            if ($xcatline)
            {
                write_stanza($xcatline);
                $xcatline = "";
            }
        }
        $xcatline = "\n";
        write_stanza($xcatline);
        $xcatline = "";
    }

    close(STANZA);
    return 0;
}

#
# runs lshwdev -l  stanza file and builds node stanza file for xCAT
#

sub getDevinfo
{

    # open the node stanza file
    my $stanzafile = "$directory/device.stanza";
    open(STANZA, ">$stanzafile")
      or die "Can't open $stanzafile for writing: $!";
    write_stanza("# <xCAT data object stanza file\n");

    log_this("Reading Device information\n");
    print "Running lshwdev -l for device info!\n";

    my $MN;
    my $OS;
    my $hwmodel;

    my @results;
    my $cmd;
    my $HWMODEL = "";

    # use of file is for debug
    my $lshwdevfile = "$directory/lshwdevdebug.output";
    if (-e $lshwdevfile)
    {    # use the imported file
        log_this("Reading $lshwdevfile information\n");
        $cmd     = "cat $lshwdevfile";
        @results = runcmd($cmd);
    }
    else
    {    # run the command
        $cmd     = "/opt/csm/bin/lshwdev -l";
        @results = runcmd($cmd);
    }
    if ($::RUNCMD_RC != 0)
    {
        my $msg = "Error processing lshwdev information\n";
        log_this($msg);
        print "$msg";
        close(STANZA);
        return;
    }

    #
    #  build hash  of lshwdev info
    #
    my $output   = buildNodehash(\@results);
    my %nodehash = %$output;
    my %attrhash;
    my $nodename;
    my $attributes;

    while (($nodename, $attributes) = each %nodehash)
    {

        #
        # build commonly needed attributes
        #
        %attrhash = %$attributes;
        $HWMODEL  = $attrhash{"HWModel"};

        #
        # first process the devicename
        #
        my $value;
        my $xcatline;
        my $attr;

        my $grplist  = get_dev_groups($nodename);
        my $shortnodename = get_shortname($nodename);
        $xcatline = "$shortnodename:\n";    # write node name
        $xcatline .= "    objtype=node\n";
        if ($grplist)
        {
            $xcatline .= "    groups=$grplist\n";
        }
        $xcatline .= "    status=defined\n";
        if ($xcatline)
        {
            write_stanza($xcatline);
            $xcatline = "";
        }

        #now process all the attributes associated with the device
        while (($attr, $value) = each %$attributes)
        {

            if ($attr eq "DeviceType")
            {
                if ($value)
                {
                    my $lcvalue = lc($value);
                    $xcatline = "    nodetype=$lcvalue\n";
                    if ($lcvalue eq "hmc") {
                      $xcatline .= "    mgt=$lcvalue\n";
                    }
                }
            }
            if ($attr eq "HWType")    # put Model and type together
            {
                if ($value)
                {
                    $xcatline = "    mtm=$HWMODEL";
                    $xcatline .= "-";
                    $xcatline .= "$value\n";
                }
            }
            if ($attr eq "HWSerialNum")
            {
                if ($value)
                {
                    $xcatline = "    serial=$value\n";
                }
            }
            if ($attr eq "PhysicalLocation")
            {
                if ($value)
                {
                    $xcatline = "    room=$value\n";
                }
            }
            if ($attr eq "Macaddr")
            {
                if ($value)
                {
                    $xcatline = "    mac=$value\n";
                }
            }
            if ($xcatline)
            {
                write_stanza($xcatline);
                $xcatline = "";
            }
        }
        $xcatline = "\n";
        write_stanza($xcatline);
        $xcatline = "";
    }
    close(STANZA);
    return 0;
}

#
# change to short hostname for xCAT
# if ip address, just return it
#
sub get_shortname
{
    my $hostname = shift();

    # if not ip address
    if ($hostname !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
    {
        my @shorthost = split(/\./, $hostname);
        return $shorthost[0];
    }
    else
    {    # return ip address
        return $hostname;
    }
}

#
# get list of groups for input hostname
#
sub get_groups
{
    my $hostname = shift();
    my $grouplist;
    my @results;
    my $cmd;

    # use of file is for debug
    my $groupfile = "$directory/nodegrpdebug.output";
    if (-e $groupfile)
    {    # use the imported file
        $cmd     = "cat $groupfile";
        @results = runcmd($cmd);
    }
    else
    {    # run the command
        $cmd     = "/opt/csm/bin/nodegrp -s $hostname";
        @results = runcmd($cmd);
    }

    foreach my $line (@results)
    {
        $grouplist .= $line;
        $grouplist .= ",";
    }
    chop $grouplist;    # get rid of last comma
    return $grouplist;
}

#
# get list of groups for input device
#
sub get_dev_groups
{
    my $devname = shift();
    my $grouplist;
    my $cmd;
    my @results;
    my $hwgroupfile = "$directory/hwdevgrpdebug.output";
    if (-e $hwgroupfile)
    {    # use the imported file
        $cmd     = "cat $hwgroupfile";
        @results = runcmd($cmd);
    }
    else
    {    # run the command
        $cmd     = "/opt/csm/bin/hwdevgrp -s $devname";
        @results = runcmd($cmd);
    }

    foreach my $line (@results)
    {
        $grouplist .= $line;
        $grouplist .= ",";
    }
    chop $grouplist;    # get rid of last comma
    return $grouplist;
}

#
# build node hash.  Build a hash of hashes,  each entry
#   nodex=> {
#            attribute=>value
#            attribute=>value
#               .
#
#

sub buildNodehash
{
    my ($info) = @_;
    my @nodeinfo = @$info;
    my %nodehash;
    my $nodename;
    foreach my $line (@nodeinfo)
    {
        $line =~ s/\s*//g;    #remove extra blanks
        my ($attr, $value) = split(/=/, $line);
        if (($attr eq "Hostname") || ($attr eq "Name"))
        {
            $nodename = $value;    # set the hash key
        }
        else
        {                          # all other lines are part of hash
            $nodehash{$nodename}{$attr} = $value;
        }
    }
    return \%nodehash;
}

#
# runs the input command and handles the errors.   Returns the output
#

sub runcmd
{
    my ($cmd) = @_;
    my $rc = 0;
    $::RUNCMD_RC = 0;
    my $outref = [];
    @$outref = `$cmd`;
    if ($?)
    {
        $rc          = $? ;
        $::RUNCMD_RC = $rc;
        if ($rc > 0)
        {
            my $msg = "$cmd returned rc=$rc @$outref\n";
            log_this($msg);
            print "$msg";
        }
    }
    chomp(@$outref);
    return @$outref;

}

sub usage
{
    print "CSM database to xCAT stanza files migration utility.\n";
    print
      "Reads the CSM database and creates xCAT stanza files that \ncan be imported using the chdef command into the xCAT database.\n";
    print "Usage:\n";
    print "\t--help - usage\n";
    print
      "\t--dir - output directory for stanza files.\n                      Default is /tmp/csm2xcat.\n";
    print "\n";
    return;
}