#!/usr/bin/env perl -w
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#####################################################
#
#  xCAT plugin package to handle the xcat2nim command.
#
#####################################################

package xCAT_plugin::xcat2nim;

BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use Sys::Hostname;
use xCAT::NodeRange;
use xCAT::Schema;
use xCAT::Utils;
use xCAT::TableUtils;
use xCAT::ServiceNodeUtils;
use xCAT::NetworkUtils;
use xCAT::DBobjUtils;
use Data::Dumper;
use Getopt::Long;
use xCAT::MsgUtils;
use Socket;

# options can be bundled up like -vV
Getopt::Long::Configure("bundling");
$Getopt::Long::ignorecase = 0;

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

=head1    xcat2nim

This program module file supports the xcat2nim command.


=cut

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

=head2    xCAT xcat2nim command

=cut

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

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

=head3  handled_commands

        Return a list of commands handled by this plugin

=cut

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

sub handled_commands
{
    return {
        xcat2nim => "xcat2nim"
    };
}

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

=head3  preprocess_request

  Check and setup for hierarchy

=cut

#-------------------------------------------------------
sub preprocess_request
{
    my $req = shift;
    my $cb  = shift;
    my %sn;

    # need for runcmd output
    $::CALLBACK = $cb;

    #exit if preprocessed
    if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }

    my $nodes = $req->{node};    # this may not be the list of nodes we need!
    my $command = $req->{command}->[0];
    $::args = $req->{arg};
    my $service = "xcat";
    my @requests;

    if ($command =~ /xcat2nim/) {

        # handle -h etc.
        #  list of nodes could be derived multiple ways!!
        my ($ret, $mynodes, $servnodes, $type) = &prexcat2nim($cb);
        if ($ret) {    # either error or -h was processed etc.
            my $rsp;
            if ($ret eq "1") {
                $rsp->{errorcode}->[0] = $ret;
                push @{ $rsp->{data} }, "Return=$ret.";
                xCAT::MsgUtils->message("E", $rsp, $cb, $ret);
            }
            return undef;
        } elsif (scalar(@{$mynodes})) {

            # set up the requests to go to the service nodes
            #   - for the nodes that were provided
            #  -  to handle node and group objects
            my $sn;
            $sn = xCAT::ServiceNodeUtils->getSNformattedhash($mynodes, $service, "MN", $type);
            foreach my $snkey (keys %$sn) {
                my $reqcopy = {%$req};
                $reqcopy->{node}                   = $sn->{$snkey};
                $reqcopy->{'_xcatdest'}            = $snkey;
                $reqcopy->{_xcatpreprocessed}->[0] = 1;
                push @requests, $reqcopy;
            }

            return \@requests;

        } elsif (scalar(@{$servnodes})) {

            # set up the requests to go to the service nodes
            #	for network objects
            foreach my $sn (@{$servnodes}) {
                my $reqcopy = {%$req};
                $reqcopy->{'_xcatdest'} = $sn;
                $reqcopy->{_xcatpreprocessed}->[0] = 1;
                push @requests, $reqcopy;
            }

            return \@requests;
        }
    }

    return undef;
}

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

=head3   process_request

        Check for xCAT command and call the appropriate subroutine.

        Returns:
                0 - OK
                1 - error

=cut

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

sub process_request
{

    $::request = shift;
    my $callback = shift;

    # need for runcmd output
    $::CALLBACK = $callback;

    my $ret;
    my $msg;
    my $rsp;

    # globals used by all subroutines.
    $::args = $::request->{arg};

    ($ret, $msg) = &x2n($callback);
    if ($ret > 0) {
        my $rsp;
        $rsp->{errorcode}->[0] = $ret;
        push @{ $rsp->{data} }, "Return=$ret.";
        xCAT::MsgUtils->message("E", $rsp, $callback, $ret);
    }
    return 0;
}

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

=head3   prexcat2nim

        Preprocessing for the xcat2nim command.

        Arguments:
        Returns:
                0 - OK - needs further processing
                1 - error - done processing this cmd
                2 - help or version - done processing this cmd
        Comments:
=cut

#-----------------------------------------------------------------------------
sub prexcat2nim
{
    my $callback = shift;

    my @nodelist     = ();    # pass back list of nodes - if applicable
    my @servicenodes = ();    # pass back list of service nodes - if applicable

    if (defined($::args) && @{$::args}) {
        @ARGV = @{$::args};
    } else {
        &xcat2nim_usage($callback);
        return 1;
    }

    Getopt::Long::Configure("no_pass_through");
    if (
        !GetOptions(
            'all|a'            => \$::opt_a,
            'b|backupSN'       => \$::BACKUP,
            'f|force'          => \$::FORCE,
            'help|h|?'         => \$::opt_h,
            'list|l'           => \$::opt_l,
            'update|u'         => \$::opt_u,
            'remove|r'         => \$::opt_r,
            'managementnode|M' => \$::MN,
            'o=s'              => \$::opt_o,
            'p|primarySN'      => \$::PRIMARY,
            't=s'              => \$::opt_t,
            's=s'              => \$::SERVERS,
            'verbose|V'        => \$::opt_V,
            'version|v'        => \$::opt_v,
        )
      )
    {
        &xcat2nim_usage($callback);
        return 1;
    }

    # Option -h for Help
    if ($::opt_h)
    {
        &xcat2nim_usage($callback);
        return 2;
    }

    # Option -v for version
    if ($::opt_v)
    {
        my $rsp;
        my $version = xCAT::Utils->Version();
        $rsp->{data}->[0] = "xcat2nim - $version";
        xCAT::MsgUtils->message("I", $rsp, $callback);
        return 2;    # - just exit
    }

    # process the command line
    my $rc = &processArgs($callback);

    my $type;
    if ($::PRIMARY && $::BACKUP) {

        # setting both is the same as all
        $type = "all";
    } elsif ($::PRIMARY) {
        $type = "primary";
    } elsif ($::BACKUP) {
        $type = "backup";
    } else {
        $type = "all";
    }

    # figure out what nodes are involved - if any
    #	- so we can send the request to the correct service nodes
    my %objhash = xCAT::DBobjUtils->getobjdefs(\%::objtype);
    my $donet   = 0;
    foreach my $o (@::clobjnames) {
        if ($::objtype{$o} eq 'node') {
            push(@nodelist, $o);
        }
        if ($::objtype{$o} eq 'group') {
            my $memberlist = xCAT::DBobjUtils->getGroupMembers($o, \%objhash);
            my @members = split(',', $memberlist);
            if (@members) {
                foreach my $n (@members) {
                    push(@nodelist, $n);
                }
            }
        }
        if ($::objtype{$o} eq 'network') {
            if ($::FORCE || $::opt_u || $::opt_r) {
                my $rsp;
                $rsp->{data}->[0] = "The -f, -r and -u options are not supported for network objects.\n";
                xCAT::MsgUtils->message("E", $rsp, $callback);
                &xcat2nim_usage($callback);
                return 2;
            }
            $donet = 1;
        }
    }

    # NIM network defs need to be created on the NIM primary
    my $nimprime = xCAT::InstUtils->getnimprime();
    chomp $nimprime;
    @servicenodes = ($nimprime);

    if (0) {    # only do networks on the management node (NIM primary) for now
        if ($donet) {
            if ($::MN) {

                # only do management node
                @servicenodes = ("$nimprime");
            } elsif ($::SERVERS) {
                @servicenodes = split(',', $::SERVERS);
            } else {

                # do MN and all servers
                @servicenodes = xCAT::ServiceNodeUtils->getAllSN();
                push(@servicenodes, $nimprime);
            }
        }

        if (scalar(@nodelist)) {

            # make sure the nodes are resolvable
            #  - if not then exit
            foreach my $n (@nodelist) {
                my $packed_ip = gethostbyname($n);
                if (!$packed_ip) {
                    my $rsp;
                    $rsp->{data}->[0] = "Could not resolve node $n.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }
            }
        }
    }

    return (0, \@nodelist, \@servicenodes, $type);
}

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

=head3   processArgs

        Process the command line.

		Also - Process any input files provided on cmd line.

        Arguments:

        Returns:
                0 - OK
                1 - just print usage
				2 - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub processArgs
{
    my $callback = shift;

    my $gotattrs = 0;

    if (defined($::args) && @{$::args}) {
        @ARGV = @{$::args};
    } else {
        return 3;
    }

    my %ObjTypeHash;
    @::clobjnames = ();
    @::clobjtypes = ();

    # parse the options
    Getopt::Long::Configure("no_pass_through");
    if (
        !GetOptions(
            'all|a'            => \$::opt_a,
            'b|backupSN'       => \$::BACKUP,
            'f|force'          => \$::FORCE,
            'help|h|?'         => \$::opt_h,
            'list|l'           => \$::opt_l,
            'update|u'         => \$::opt_u,
            'remove|r'         => \$::opt_r,
            'managementnode|M' => \$::MN,
            'o=s'              => \$::opt_o,
            'p|primarySN'      => \$::PRIMARY,
            's=s'              => \$::SERVERS,
            't=s'              => \$::opt_t,
            'verbose|V'        => \$::opt_V,
            'version|v'        => \$::opt_v,
        )
      )
    {

        # return 2;
    }

    # can get object names in many ways - easier to keep track
    $::objectsfrom_args = 0;
    $::objectsfrom_opto = 0;
    $::objectsfrom_optt = 0;
    $::objectsfrom_opta = 0;

    #
    # process @ARGV
    #

    #  - put attr=val operands in ATTRS hash
    while (my $a = shift(@ARGV))
    {

        if (!($a =~ /=/))
        {

            # the first arg could be a noderange or a list of args
            if (($::opt_t) && ($::opt_t ne 'node'))
            {

                # if we know the type isn't "node" then set the object list
                @::clobjnames = split(',', $a);
                $::objectsfrom_args = 1;
            }
            elsif (!$::opt_t || ($::opt_t eq 'node'))
            {

                # if the type was not provided or it is "node"
                #	then set noderange
                @::noderange        = &noderange($a, 0);
                @::clobjnames       = @::noderange;
                $::objectsfrom_args = 1;
            }

        }
        else
        {

            # if it has an "=" sign its an attr=val - we hope
            #   - this will handle "attr= "
            my ($attr, $value) = $a =~ /^\s*(\S+?)\s*=\s*(\S*.*)$/;
            if (!defined($attr) || !defined($value))
            {
                my $rsp;
                $rsp->{data}->[0] = "Incorrect \'attr=val\' pair - $a\n";
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 3;
            }

            $gotattrs = 1;

            # put attr=val in hash
            $::ATTRS{$attr} = $value;

        }
    }

    # Option -V for verbose output
    if (defined($::opt_V))
    {
        $::verbose = 1;
        $::VERBOSE = 1;
    }

    #
    #  determine the object types
    #

    # could have comma seperated list of types
    if ($::opt_t)
    {
        my @tmptypes;

        if ($::opt_t =~ /,/)
        {

            # can't have mult types when using attr=val
            if ($gotattrs)
            {
                my $rsp;
                $rsp->{data}->[0] =
"Cannot combine multiple types with \'att=val\' pairs on the command line.\n";
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 3;
            }
            else
            {
                @tmptypes = split(',', $::opt_t);
            }
        }
        else
        {
            push(@tmptypes, $::opt_t);
        }

        # check for valid types
        my @xdeftypes;
        foreach my $k (keys %{xCAT::Schema::defspec})
        {
            push(@xdeftypes, $k);
        }

        foreach my $t (@tmptypes)
        {
            if (!grep(/$t/, @xdeftypes))
            {
                my $rsp;
                $rsp->{data}->[0] =
                  "$::msgstr Type \'$t\' is not a valid xCAT object type.\n";
                $rsp->{data}->[1] = "Skipping to the next type.\n";
                xCAT::MsgUtils->message("I", $rsp, $callback);
            }
            else
            {
                chomp $t;
                push(@::clobjtypes, $t);
            }
        }
    }

    # must have object type(s) - default if not provided
    if (!@::clobjtypes && !$::opt_a)
    {

        # make the default type = 'node' if not specified
        push(@::clobjtypes, 'node');

        #my $rsp;
        #$rsp->{data}->[0] = "Assuming an object type of \'node\'.\n";
        #xCAT::MsgUtils->message("I", $rsp, $callback);
    }

    #
    #  determine the object names
    #

    # -  get object names from the -o option or the noderange
    #		- this assumes the noderange was not provided as an arg!!!
    if ($::opt_o)
    {

        $::objectsfrom_opto = 1;

        # make a list
        if ($::opt_o =~ /,/)
        {
            @::clobjnames = split(',', $::opt_o);
        }
        else
        {
            push(@::clobjnames, $::opt_o);
        }

        if (!$::opt_t || ($::opt_t eq 'node')) {

            # if the type is "node"
            @::noderange = &noderange($::opt_o, 0);
            @::clobjnames = @::noderange;
        }
    }

    # if there is no opt_o & no noderange then try to find all the objects
    #	of the given types.
    if ($::opt_t
        && !($::opt_o
            || $::opt_a
            || @::noderange
            || @::clobjnames))
    {
        my @tmplist;

        $::objectsfrom_optt = 1;

        # could have multiple type
        foreach my $t (@::clobjtypes)
        {

            #  look up all objects of this type in the DB ???
            @tmplist = xCAT::DBobjUtils->getObjectsOfType($t);

            unless (@tmplist)
            {
                my $rsp;
                $rsp->{data}->[0] =
                  "Could not get objects of type \'$t\'.\n";
                $rsp->{data}->[1] = "Skipping to the next type.\n";
                xCAT::MsgUtils->message("I", $rsp, $callback);
                next;
            }

            # add objname and type to hash and global list
            foreach my $o (@tmplist)
            {
                push(@::clobjnames, $o);
                $ObjTypeHash{$o} = $t;
            }
        }
    }


    # can't have -a with other obj sources
    if ($::opt_a
        && ($::opt_o || @::noderange))
    {

        my $rsp;
        $rsp->{data}->[0] =
          "Cannot use \'-a\' with \'-o\' or a noderange.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 3;
    }

    #  if -a then get a list of all DB objects
    my %AllObjTypeHash;
    if ($::opt_a)
    {
        my @tmplist;

        $::objectsfrom_opta = 1;

        # for every type of data object get the list of defined objects
        foreach my $t (keys %{xCAT::Schema::defspec})
        {
            if (($t eq 'node') || ($t eq 'group') || ($t eq 'network')) {

                my @tmplist;
                @tmplist = xCAT::DBobjUtils->getObjectsOfType($t);

                # add objname and type to hash and global list
                if (scalar(@tmplist) > 0)
                {
                    foreach my $o (@tmplist)
                    {
                        push(@::clobjnames, $o);
                        $AllObjTypeHash{$o} = $t;
                    }
                }
            }
        }
    }

    # must have object name(s) -
    if (!@::clobjnames)
    {
        return 3;
    }

    # create hash with object names and types
    foreach my $o (@::clobjnames)
    {
        if ($::objectsfrom_opta) {

            # if the object names came from the "-a" option then
            %::objtype = %AllObjTypeHash;

        } elsif ($::objectsfrom_optt) {

            # if the names came from the opt_t option
            %::objtype = %ObjTypeHash;

        } elsif ($::objectsfrom_args || $::objectsfrom_opto) {

            # from the opt_o or as an argument
            #  - there can only be one type
            $::objtype{$o} = $::clobjtypes[0];
        }
    }

    return 0;
}

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

=head3   x2n

        Support for the xcat2nim command.

        Arguments:
        Returns:
                0 - OK
                1 - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub x2n
{
    my $callback = shift;

    my $rc    = 0;
    my $error = 0;

    # get this systems name as known by xCAT management node
    $::Sname = xCAT::InstUtils->myxCATname();
    chomp $::Sname;

    $::msgstr = "$::Sname: ";

    my $nimprime = xCAT::InstUtils->getnimprime();
    chomp $nimprime;

    # process the command line
    $rc = &processArgs($callback);
    if ($rc != 0)
    {

        # rc: 0 - ok, 1 - return, 2 - help, 3 - error
        if ($rc != 1)
        {
            &xcat2nim_usage($callback);
        }
        return ($rc - 1);
    }

    # get all the attrs for these definitions
    %::objhash = xCAT::DBobjUtils->getobjdefs(\%::objtype);
    if (!%::objhash)
    {
        my $rsp;
        $rsp->{data}->[0] = "Could not get xCAT object definitions.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    my @nodes;
    my @networks;
    my @groups;
    foreach my $obj (keys %::objhash) {
        if ($::objtype{$obj} eq 'node') {
            push(@nodes, $obj);
        } elsif ($::objtype{$obj} eq 'network') {
            if ($::objhash{$obj}{'gateway'} eq '<xcatmaster>') {
                $::objhash{$obj}{'gateway'} = xCAT::NetworkUtils->my_ip_in_subnet($::objhash{$obj}{'net'}, $::objhash{$obj}{'mask'});
                if (!$::objhash{$obj}{'gateway'}) {
                    my $rsp;
                    $rsp->{data}->[0] = "Could not get gateway for network $obj, ...skipping\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    next;
                }
            }
            push(@networks, $obj);
        } elsif ($::objtype{$obj} eq 'group') {
            push(@groups, $obj);
        }
    }

    #  NIM machine definitions
    if (scalar(@nodes)) {
        foreach my $objname (@nodes) {

            # does this node belong to this server?
            my $nimmaster = $nimprime;
            if ($::objhash{$objname}{servicenode}) {
                my $sn2;
                ($nimmaster, $sn2) = split(/,/, $::objhash{$objname}{servicenode});
            }

            if (!xCAT::InstUtils->is_me($nimmaster)) {
                next;
            }

            if ($::opt_l || $::opt_r) {

                if (&rm_or_list_nim_object($objname, $::objtype{$objname}, $callback)) {

                    # the routine failed
                    $error++;
                }
            } else {
                if (mkclientdef($objname, $callback)) {

                    # could not create client definition
                    $error++;
                }
            }
        }    # end for each node
    }

    # NIM network definitions
    if (scalar(@networks)) {
        if ($::opt_l) {

            # list network def
            if (&listNIMnetwork($callback, \@networks, \%::objhash)) {

                # could not create client definition
                $error++;
            }
        } elsif ($::opt_r) {

            # remove network def

            #if (&rmNIMnetwork($callback, \@networks) ) {
            #    # could not create client definition
            #    $error++;
            #}

            my $rsp;
            push @{ $rsp->{data} }, "$::msgstr The remove option is not supported for NIM network definitions.\n";
            xCAT::MsgUtils->message("E", $rsp, $callback);
            $error++;

        } else {
            if (&mkNIMnetwork($callback, \@networks, \%::objhash)) {

                # could not create client definition
                $error++;
            }
        }
    }

    # NIM group definitions
    if (scalar(@groups)) {
        foreach my $objname (@groups) {

            # make sure grouptype is set  ???
            $::objhash{$objname}{'grouptype'} = 'static';
            my $grptab = xCAT::Table->new('nodegroup');

            #dynamic groups and static groups in nodegroup table
            my @grplist = @{ $grptab->getAllEntries() };
            foreach my $grpdef_ref (@grplist) {
                my %grpdef = %$grpdef_ref;
                if (($grpdef{'groupname'} eq $objname) && ($grpdef{'grouptype'} eq 'dynamic')) {
                    $::objhash{$objname}{'grouptype'} = 'dynamic';
                    last;
                }
            }
            $grptab->close;

            if ($::opt_l || $::opt_r) {
                if (&rm_or_list_nim_object($objname, $::objtype{$objname}, $callback)) {

                    # the routine failed
                    $error++;
                }
            } else {
                if (&mkgrpdef($objname, $callback)) {

                    # could not create group definition
                    $error++;
                }
            }
            next;
        }
    }

    if ($error)
    {
        my $rsp;
        $rsp->{data}->[0] =
          "$::msgstr One or more errors occured.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }
    else
    {
        return 0;
    }
    return 0;
}

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

=head3   mkclientdef

		Create a NIM client definition.
        Arguments:
        Returns:
                0 - OK
                1 - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub mkclientdef
{
    my $node     = shift;
    my $callback = shift;

    my $cabletype = undef;
    my $ifattr    = undef;

    my %finalattrs;
    my $shorthost;
    my $net_name;
    my $adaptertype;
    my $nim_args;
    my $nim_type;

    # this code runs on service nodes - which are NIM masters

    # check to see if the client is already defined
    #	- sets $::client_exists
    if (&check_nim_client($node, $::Sname, $callback)) {

        # the routine failed
        return 1;
    }

    # need short host name for NIM client defs
    ($shorthost = $node) =~ s/\..*$//;

    #  NIM has a limit of 39 characters for a machine name
    my $len = length($shorthost);
    if ($len > 39) {
        my $rsp;
        push @{ $rsp->{data} }, "$::msgstr Could not define \'$shorthost\'. A NIM machine name can be no longer then 39 characters.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    # don't update an existing def unless they say so!
    if ($::client_exists && !$::opt_u) {

        if ($::FORCE) {

            # get rid of the old definition
            my $rmcmd = "/usr/sbin/nim -Fo reset $shorthost;/usr/sbin/nim -Fo deallocate -a subclass=all $shorthost;/usr/sbin/nim -Fo remove $shorthost";
            my $output = xCAT::Utils->runcmd("$rmcmd", -1);
            if ($::RUNCMD_RC != 0) {
                my $rsp;
                push @{ $rsp->{data} }, "$::msgstr Could not remove the existing NIM object named \'$shorthost\'.\n";
                if ($::VERBOSE) {
                    push @{ $rsp->{data} }, "$output";
                }
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            }

        } else {    # no force
            my $rsp;
            $rsp->{data}->[0] = "$::msgstr The NIM client machine \'$shorthost\' already exists.  Use the \'-f\' option to remove and recreate or the \'-u\' option to update an existing definition.\n";
            xCAT::MsgUtils->message("I", $rsp, $callback);
            return 1;
        }

    }

    # either create or update the def

    # process the args and add defaults etc.
    foreach my $attr (keys %::ATTRS) {

        if ($attr =~ /^if/) {

            $ifattr = "-a $attr=\'$::ATTRS{$attr}\'";

        } elsif ($attr =~ /^cable_type/) {
            $cabletype = "-a cable_type1=\'$::ATTRS{$attr}\'";

        } else {

            # add to
            $finalattrs{$attr} = $::ATTRS{$attr};
        }
    }

    # req a value for cable_type

    if (!$cabletype) {
        $cabletype = "-a cable_type1=N/A ";
    }

    # req a value for "if1" interface def
    if (!$ifattr) {

        # then try to create the attr - required
        if ($::objhash{$node}{'netname'}) {
            $net_name = $::objhash{$node}{'netname'};
        } else {
            $net_name = "find_net";
        }

        if (!$::objhash{$node}{'mac'})
        {
            my $rsp;
            $rsp->{data}->[0] = "$::msgstr Missing the MAC for node \'$node\'.\n";
            xCAT::MsgUtils->message("E", $rsp, $callback);
            return 1;
        } else {
            $::objhash{$node}{'mac'} =~ s/://g;
        }

        my $mac_or_local_link_addr;
        if (xCAT::NetworkUtils->getipaddr($shorthost) =~ /:/)    #ipv6 node
        {
            $mac_or_local_link_addr = xCAT::NetworkUtils->linklocaladdr($::objhash{$node}{'mac'});
            $adaptertype = "ent6";
        } else {
            $mac_or_local_link_addr = $::objhash{$node}{'mac'};

            # only support Ethernet for management interfaces
            $adaptertype = "ent";
        }

        $ifattr = "-a if1=\'$net_name $shorthost $mac_or_local_link_addr $adaptertype\'";
    }

    # only support standalone for now - will get this from node def in future
    $nim_type = "-t standalone";

    $nim_args = "$ifattr ";
    $nim_args .= "$cabletype";

    # add the rest of the attr=val to the command line
    foreach my $a (keys %finalattrs) {
        $nim_args .= " -a $a=\'$finalattrs{$a}\'";
    }

    # put together the correct NIM command
    my $cmd;

    if ($::client_exists && $::opt_u) {
        $cmd = "nim -F -o change $nim_args $shorthost";
    } else {
        $cmd = "nim -o define $nim_type $nim_args $shorthost";
    }

    my $nimcmd;
    $nimcmd = qq~$cmd 2>&1~;

    # run the cmd
    my $output = xCAT::Utils->runcmd("$nimcmd", -1);
    if ($::RUNCMD_RC != 0)
    {
        my $rsp;
        $rsp->{data}->[0] = "$::msgstr Could not create a NIM definition for \'$node\'.\n";
        if ($::verbose)
        {
            $rsp->{data}->[1] = "$output";
        }
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    return 0;
}

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

=head3   mkgrpdef

        Create a NIM group definition.

        Arguments:
        Returns:
                0 - OK
                1 - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub mkgrpdef
{
    my $group    = shift;
    my $callback = shift;

    # get members of xCAT group
    my $memberlist = xCAT::DBobjUtils->getGroupMembers($group, \%::objhash);
    my @members = split(',', $memberlist);

    # get list of nim groups defined locally
    $cmd = qq~lsnim -c groups | cut -f1 -d' ' 2>/dev/null~;
    my @GroupList = xCAT::Utils->runcmd("$cmd", -1);
    if ($::RUNCMD_RC != 0)
    {
        my $rsp;
        $rsp->{data}->[0] = "$::msgstr Could not get a list of NIM group definitions.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    # get list of nim machine defined locally
    $mcmd = qq~lsnim -c machines | cut -f1 -d' ' 2>/dev/null~;
    my @nimnodes = xCAT::Utils->runcmd("$mcmd", -1);
    if ($::RUNCMD_RC != 0)
    {
        my $rsp;
        $rsp->{data}->[0] = "$::msgstr Could not get a list of NIM group definit
ions.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    # don't update an existing def unless we're told
    if ((grep(/$group/, @GroupList)) && !$::opt_u) {
        if ($::FORCE) {

            # get rid of the old definition
            my $rmcmd = "/usr/sbin/nim -Fo remove $group";
            my $output = xCAT::Utils->runcmd("$rmcmd", -1);
            if ($::RUNCMD_RC != 0) {
                my $rsp;
                push @{ $rsp->{data} }, "$::msgstr Could not remove the existing NIM group named \'$group\'.\n";
                if ($::VERBOSE) {
                    push @{ $rsp->{data} }, "$output";
                }
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            }

        } else {    # no force
            my $rsp;
            $rsp->{data}->[0] = "$::msgstr The NIM group \'$group\' already exists.  Use the \'-f\' option to remove and recreate or the \'-u\' option to update an existing definition.\n";
            xCAT::MsgUtils->message("I", $rsp, $callback);
            return 1;
        }
    }

    # either create or update the group def on this master

    # any list with more than 1024 members is an error
    #   - NIM can't handle that
    if ($#members > 1024) {
        my $rsp;
        $rsp->{data}->[0] = "$::msgstr Cannot create a NIM group definition with more than 1024 members.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        next;
    }

    #
    #  The list may become quite long and not fit on one cmd line
    #  so we do it one at a time for now - need to revisit this
    #      (like do blocks at a time)  - TODO
    #
    my $justadd = 0;    # after the first define we just need to add
    foreach my $memb (@members) {

        # if this node is not defined locally then don't add it to the list
        if (!grep(/$memb/, @nimnodes)) {
            next;
        }

        my $shorthost;
        ($shorthost = $memb) =~ s/\..*$//;

        # do we change or create
        my $cmd;
        if (((grep(/$group/, @GroupList)) && $::opt_u) || $justadd) {
            $cmd = "nim -o change -a add_member=$shorthost $group 2>&1";
        } else {
            $cmd = "nim -o define -t mac_group -a add_member=$shorthost $group 2>&1";
            $justadd++;
        }

        my $output = xCAT::Utils->runcmd("$cmd", -1);

        if ($::RUNCMD_RC != 0)
        {
            my $rsp;
            $rsp->{data}->[0] = "$::msgstr Error running command \'$cmd\'.\n";
            if ($::verbose)
            {
                $rsp->{data}->[1] = "$output";
            }
            xCAT::MsgUtils->message("E", $rsp, $callback);
            return 1;
        }
    }
    return 0;
}

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

=head3   rm_or_list_nim_object

         List a NIM object definition.

        Argument:
        Returns:
                0 - OK
                1 - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub rm_or_list_nim_object
{
    my ($object, $type, $callback) = @_;

    if ($type eq 'node') {

        if ($::opt_l) {

            my $cmd;
            $cmd = qq~lsnim -l $object 2>/dev/null~;

            my $outref = xCAT::Utils->runcmd("$cmd", -1);
            if ($::RUNCMD_RC != 0)
            {
                my $rsp;
                $rsp->{data}->[0] = "$::msgstr Could not get the NIM definition for $object.\n";
                if ($::verbose)
                {
                    $rsp->{data}->[1] = "$outref";
                }
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            } else {

                #  display to NIM output
                my $rsp;
                $rsp->{data}->[0] = "$outref";
                xCAT::MsgUtils->message("I", $rsp, $callback);
                return 0;
            }

        } elsif ($::opt_r) {

            # remove the object
            my $cmd;

            if ($::FORCE) {
                $cmd = qq~nim -Fo reset $object;nim -Fo deallocate -a subclass=all $object;nim -Fo remove $object 2>/dev/null~;
            } else {
                $cmd = qq~nim -Fo remove $object 2>/dev/null~;
            }

            my $outref = xCAT::Utils->runcmd("$cmd", -1);
            if ($::RUNCMD_RC != 0)
            {
                my $rsp;
                $rsp->{data}->[0] = "$::msgstr Could not remove the NIM definition for \'$object\'.\n";
                if ($::verbose)
                {
                    $rsp->{data}->[1] = "$outref";
                }
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            }
        }
    }

    if ($type eq 'group') {

        my $cmd;
        if ($::opt_l) {
            $cmd = qq~lsnim -l $object 2>/dev/null~;

            my $outref = xCAT::Utils->runcmd("$cmd", -1);
            if ($::RUNCMD_RC != 0)
            {
                my $rsp;
                $rsp->{data}->[0] = "$::msgstr Could not list the NIM definition for \'$object\'.\n";
                if ($::verbose)
                {
                    $rsp->{data}->[1] = "$outref";
                }
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            } else {

                #  display NIM output
                my $rsp;
                $rsp->{data}->[0] = "$outref";
                xCAT::MsgUtils->message("I", $rsp, $callback);
                return 0;
            }
        } elsif ($::opt_r) {
            $cmd = qq~nim -Fo remove $object 2>/dev/null~;

            my $outref = xCAT::Utils->runcmd("$cmd", -1);
            if ($::RUNCMD_RC != 0)
            {
                my $rsp;
                $rsp->{data}->[0] = "$::msgstr Could not remove the NIM definition for \'$object\'.\n";
                if ($::verbose)
                {
                    $rsp->{data}->[1] = "$outref";
                }
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            }
        }
    }
    return 0;
}

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

=head3   getNIMmaster

        Get the name of the NIM master for a node.

        Arguments:
        Returns:
                name
                undef - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub getNIMmaster
{
    my ($node) = @_;
    my $NimMaster;
    my $master = undef;

    # get the server name
    if ($::objhash{$node}{nimmaster}) {
        $NimMaster = $::objhash{$node}{nimmaster};

    } elsif ($::objhash{$node}{servicenode}) {

        # the servicenode attr is set for this node
        $NimMaster = $::objhash{$node}{servicenode};

    } elsif ($::objhash{$node}{xcatmaster}) {
        $NimMaster = $::objhash{$node}{xcatmaster};

    } else {
        my $nimprime = xCAT::InstUtils->getnimprime();
        chomp $nimprime;
        $NimMaster = $nimprime;
    }

    # assume short hostnames for now???
    if ($NimMaster) {
        ($master = $NimMaster) =~ s/\..*$//;
    }
    return $master;
}

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

=head3   check_nim_client

        See if an xCAT node has already been defined as a NIM client.

        Arguments:
        Returns:
                0 - OK
                1 - error
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub check_nim_client
{
    my ($node, $servnode, $callback) = @_;
    my ($cmd, @ClientList);

    $cmd = qq~lsnim -c machines | cut -f1 -d' ' 2>/dev/null~;
    @ClientList = xCAT::Utils->runcmd("$cmd", -1);
    if ($::RUNCMD_RC != 0)
    {
        my $rsp;
        $rsp->{data}->[0] = "$::msgstr Could not get a list of NIM client definitions from \'$servnode\'.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    $::client_exists = grep(/^$node$/, @ClientList) ? 1 : 0;

    return 0;
}



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

=head3  xcat2nim_usage

        Arguments:
        Returns:
        Globals:

        Error:

        Example:

        Comments:
=cut

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

sub xcat2nim_usage
{
    my $callback = shift;
    my $rsp;
    push @{ $rsp->{data} }, "\nUsage: xcat2nim - Use this command to create and manage AIX NIM definitions based on xCAT object definitions.\n";
    push @{ $rsp->{data} }, "  xcat2nim [-h|--help ]\n";
    push @{ $rsp->{data} }, "  xcat2nim [-V|--verbose] [-l|--list] [-r|--remove] [-u|--update]\n    [-f|--force] [-t object-types] [-o object-names] [-a|--allobjects]\n    [-p|--primarySN] [-b|--backupSN] [noderange] [attr=val [attr=val...]]\n";
    xCAT::MsgUtils->message("I", $rsp, $callback);
    return 0;
}

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

=head3 mkNIMnetwork
		Create NIM network definitions corresponding to xCAT network
		definitions. This routine runs on the NIMprime and AIX SNs

    Arguments:

    Returns:


    Comments:

    ex. &mkNIMnetwork($callback, \@networks, \%nethash);

=cut

#-------------------------------------------------------------------------------
sub mkNIMnetwork
{
    my $callback = shift;
    my $xnets    = shift;
    my $xnhash   = shift;

    my @xnetworks = @{$xnets};
    my %xnethash;    # hash of xCAT network definitions
    if ($xnhash) {
        %xnethash = %{$xnhash};
    }

    #
    # get all the nim network names and attrs defined on this server
    #
    my $cmd = qq~/usr/sbin/lsnim -c networks | /usr/bin/cut -f1 -d' ' 2>/dev/null~;
    my @networks = xCAT::Utils->runcmd("$cmd", -1);

    # for each NIM network - get the attrs
    my %NIMnets;
    foreach my $netwk (@networks) {
        my $cmd = qq~/usr/sbin/lsnim -Z -a net_addr -a snm $netwk 2>/dev/null~;
        my @result = xCAT::Utils->runcmd("$cmd", -1);
        if ($::RUNCMD_RC != 0) {
            my $rsp;
            push @{ $rsp->{data} }, "$::msgstr Could not run lsnim command: \'$cmd\'.\n";
            xCAT::MsgUtils->message("E", $rsp, $callback);
            return 1;
        }

        foreach my $l (@result) {

            # skip comment lines
            next if ($l =~ /^\s*#/);

            my ($nimname, $net_addr, $snm) = split(':', $l);
            $NIMnets{$netwk}{'net_addr'} = $net_addr;
            $NIMnets{$netwk}{'snm'}      = $snm;
        }
    }

    #
    # for each xCAT network - see if the net we need is defined
    #
    foreach my $net (@xnetworks) {

        # see if it's already defined - or equivalent is defined
        # split  mask
        my ($nm1, $nm2, $nm3, $nm4) = split('\.', $xnethash{$net}{mask});

        # split net addr
        my ($nn1, $nn2, $nn3, $nn4) = split('\.', $xnethash{$net}{net});

        # foreach nim network name
        foreach my $netwk (@networks) {

            # split definition mask
            my ($dm1, $dm2, $dm3, $dm4) = split('\.', $NIMnets{$netwk}{'snm'});

            # split definition net addr
            my ($dn1, $dn2, $dn3, $dn4) = split('\.', $NIMnets{$netwk}{'net_addr'});

            # check for the same netmask and network address
            if (($nn1 == $dn1) && ($nn2 == $dn2) && ($nn3 == $dn3) && ($nn4 == $dn4)) {
                if (($nm1 == $dm1) && ($nm2 == $dm2) && ($nm3 == $dm3) && ($nm4 == $dm4)) {
                    $foundmatch = 1;
                }
            }
        }

        # if not defined then define it!
        if (!$foundmatch) {

            # create new nim network def
            # use the same network name as xCAT uses
            my $cmd;
            $cmd = qq~/usr/sbin/nim -o define -t ent -a net_addr=$xnethash{$net}{net} -a snm=$xnethash{$net}{mask} -a routing1='default $xnethash{$net}{gateway}' $net 2>/dev/null~;

            my $output1 = xCAT::Utils->runcmd("$cmd", -1);
            if ($::RUNCMD_RC != 0) {
                my $rsp;
                push @{ $rsp->{data} }, "$::msgstr Could not run \'$cmd\'.\n";
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            }

            # get all the possible IPs for the node I'm running on
            my $ifgcmd = "ifconfig -a | grep 'inet'";
            my @result = xCAT::Utils->runcmd($ifgcmd, 0);
            if ($::RUNCMD_RC != 0) {
                my $rsp;
                push @{ $rsp->{data} }, "$::msgstr Could not run \'$ifgcmd\'.\n";
                xCAT::MsgUtils->message("E", $rsp, $callback);
                return 1;
            }


            my $samesubnet = 0;
            my ($inet, $myIP, $str);
            foreach my $int (@result) {
                ($inet, $myIP, $str) = split(" ", $int);
                chomp $myIP;
                $myIP =~ s/\/.*//;    # ipv6 address 4000::99/64
                $myIP =~ s/\%.*//;    # ipv6 address ::1%1/128

                # if the ip address is in the subnet
                if (xCAT::NetworkUtils->ishostinsubnet($myIP, $xnethash{$net}{mask}, $xnethash{$net}{net})) {

                    # to create the nim network object within the same subnet
                    $samesubnet = 1;
                    last;
                }
            }

            if ($samesubnet == 1)
            {
                #
                # create an interface def (if*) for the master
                #
                # first get the if* and cable_type* attrs
                #  - the -A option gets the next avail index for this attr
                my $ifcmd = qq~/usr/sbin/lsnim -A if master 2>/dev/null~;
                my $ifindex = xCAT::Utils->runcmd("$ifcmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "Could not run \'$ifcmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }

                my $ctcmd = qq~/usr/sbin/lsnim -A cable_type master 2>/dev/null~;
                my $ctindex = xCAT::Utils->runcmd("$ctcmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "$::msgstr Could not run \'$ctcmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }

                #
                # get the local adapter hostname for this network
                my $adapterhostname = xCAT::NetworkUtils->gethostname($myIP);

                # define the new interface
                my $chcmd = qq~/usr/sbin/nim -o change -a if$ifindex='$net $adapterhostname 0' -a cable_type$ctindex=N/A master 2>/dev/null~;

                my $output2 = xCAT::Utils->runcmd("$chcmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "$::msgstr Could not run \'$chcmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }
            }
            else
            {
                # cross subnet
                # create static routes between the networks

                # get master_net - always if1
                my $hncmd = qq~/usr/sbin/lsnim -a if1 -Z master 2>/dev/null~;
                my @ifone = xCAT::Utils->runcmd("$hncmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "$::msgstr Could not run \'$hncmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }

                my ($junk1, $masternet, $adapterhost);
                foreach my $l (@ifone) {

                    # skip comment lines
                    next if ($l =~ /^\s*#/);
                    ($junk1, $masternet, $adapterhost) = split(':', $l);

                }

                # get the next index for the routing attr
                my $ncmd = qq~/usr/sbin/lsnim -A routing $masternet 2>/dev/null~;
                my $rtindex = xCAT::Utils->runcmd("$ncmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "$::msgstr Could not run \'$ncmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }

                # get the gateway of master_net
                my $gcmd = qq~/usr/sbin/lsnim -a routing1 -Z $masternet 2>/dev/null~;
                my @gws = xCAT::Utils->runcmd("$gcmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "$::msgstr Could not run \'$gcmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }

                my ($junk, $dft, $gw);
                foreach my $l (@gws) {

                    # skip comment lines
                    next if ($l =~ /^\s*#/);
                    ($junk, $dft, $gw) = split(':', $l);
                }

                my $masternetgw;
                if ($dft =~ /default/)
                {
                    $masternetgw = $gw;
                }
                else
                {
                    # use the master IP as default gateway
                    # get the ip of the nim primary interface
                    my $gwIP = xCAT::NetworkUtils->getipaddr($adapterhost);
                    chomp $gwIP;
                    $masternetgw = $gwIP;
                }

                # create static routes between the networks
                my $rtgcmd = qq~/usr/sbin/nim -o change -a routing$rtindex='$net $masternetgw $xnethash{$net}{gateway}' $masternet 2>/dev/null~;
                my $output3 = xCAT::Utils->runcmd("$rtgcmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "$::msgstr Could not run \'$rtgcmd\'.\n";
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                    return 1;
                }

            }
        }    # end - define new nim network

    }    # end - for each network
    return 0;
}

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

=head3 listNIMnetwork
        List NIM network definitions corresponding to xCAT network
        definitions. This routine runs on the NIMprime and AIX SNs

    Arguments:

    Returns:

    Comments:

    ex. &listNIMnetwork($callback, \@networks, \%nethash);

=cut

#-------------------------------------------------------------------------------
sub listNIMnetwork
{
    my $callback = shift;
    my $xnets    = shift;
    my $xnhash   = shift;

    my %xnethash;    # hash of xCAT network definitions
    if ($xnhash) {
        %xnethash = %{$xnhash};
    }

    my @xcatnetworks = @{$xnets};

    #
    # get all the nim network names
    #
    my $cmd = qq~/usr/sbin/lsnim -c networks | /usr/bin/cut -f1 -d' ' 2>/dev/null~;
    my @nimnetworks = xCAT::Utils->runcmd("$cmd", -1);
    if ($::RUNCMD_RC != 0) {
        my $rsp;
        push @{ $rsp->{data} }, "$::msgstr Could not run lsnim command: \'$cmd\'.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    # for each NIM network - get the attrs
    my %NIMnets;
    foreach my $netwk (@nimnetworks) {
        my $cmd = qq~/usr/sbin/lsnim -Z -a net_addr -a snm $netwk 2>/dev/null~;
        my @result = xCAT::Utils->runcmd("$cmd", -1);
        if ($::RUNCMD_RC != 0) {
            my $rsp;
            push @{ $rsp->{data} }, "$::msgstr Could not run lsnim command: \'$cmd\'.\n";
            xCAT::MsgUtils->message("E", $rsp, $callback);
            return 1;
        }

        foreach my $l (@result) {

            # skip comment lines
            next if ($l =~ /^\s*#/);

            my ($nimname, $net_addr, $snm) = split(':', $l);
            $NIMnets{$netwk}{'net_addr'} = $net_addr;
            $NIMnets{$netwk}{'snm'}      = $snm;
        }
    }

    # for each xCAT network check for an equivalent NIM network
    #	- if a match then display the NIM def
    #	- display output of "lsnim -l <netname>"

    foreach my $xcatnet (@xcatnetworks) {

        # split  mask
        my ($nm1, $nm2, $nm3, $nm4) = split('\.', $xnethash{$xcatnet}{mask});

        # split net addr
        my ($nn1, $nn2, $nn3, $nn4) = split('\.', $xnethash{$xcatnet}{net});

        foreach my $nimnet (@nimnetworks) {

            # split definition mask
            my ($dm1, $dm2, $dm3, $dm4) = split('\.', $NIMnets{$nimnet}{'snm'});

            # split definition net addr
            my ($dn1, $dn2, $dn3, $dn4) = split('\.', $NIMnets{$nimnet}{'net_addr'});

            # check for the same netmask and network address
            if (($nn1 == $dn1) && ($nn2 == $dn2) && ($nn3 == $dn3) && ($nn4 == $dn4)) {
                if (($nm1 == $dm1) && ($nm2 == $dm2) && ($nm3 == $dm3) && ($nm4 == $dm4)) {

                    # found match so display NIM net def
                    my $cmd = qq~/usr/sbin/lsnim -l $nimnet 2>/dev/null~;
                    my $output = xCAT::Utils->runcmd("$cmd", -1);
                    if ($::RUNCMD_RC != 0) {

                        #		my $rsp;
                        #		push @{$rsp->{data}}, "Could not run lsnim command: \'$cmd\'.\n";
                        #		xCAT::MsgUtils->message("E", $rsp, $callback);
                        next;
                    } else {
                        my $rsp;
                        push @{ $rsp->{data} }, "Note: The following is a match for \nxCAT network named \'$xcatnet\'\n";
                        push @{ $rsp->{data} }, "$output\n";
                        xCAT::MsgUtils->message("I", $rsp, $callback);
                    }
                }
            }
        }    # end for each nim net
    }    # end foreach xcat net

    return 0;
}

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

=head3 rmNIMnetwork
        Remove NIM network definitions corresponding to xCAT network
        definitions. This routine runs on the NIMprime and AIX SNs

    Arguments:

    Returns:


    Comments:

    ex. &rmNIMnetwork($callback, \@networks);

=cut

#-------------------------------------------------------------------------------
sub rmNIMnetwork
{
    my $callback = shift;
    my $xnets    = shift;




############################################
    #
    #    This will not work - you must first remove the master if* definition
    #		and the route defs AND any objects that may reference the network
    #		- it is not worth the effort - maybe a future
    #		enhancement - I'll leave the code here for now.
#########################################################

    my @xcatnetworks = @{$xnets};

    #
    # get all the nim network names
    #
    my $cmd = qq~/usr/sbin/lsnim -c networks | /usr/bin/cut -f1 -d' ' 2>/dev/null~;
    my @nimnetworks = xCAT::Utils->runcmd("$cmd", -1);
    if ($::RUNCMD_RC != 0) {
        my $rsp;
        push @{ $rsp->{data} }, "Could not run lsnim command: \'$cmd\'.\n";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }

    foreach my $xcatnet (@xcatnetworks) {
        chomp $xcatnet;

        foreach my $nimnet (@nimnetworks) {
            chomp $nimnet;

            if ($xcatnet eq $nimnet) {

                # get rid of the old definition
                my $rmcmd = "/usr/sbin/nim -Fo remove $nimnet";
                my $output = xCAT::Utils->runcmd("$rmcmd", -1);
                if ($::RUNCMD_RC != 0) {
                    my $rsp;
                    push @{ $rsp->{data} }, "Could not remove the existing NIM network named \'$nimnet\'.\n";
                    if ($::VERBOSE) {
                        push @{ $rsp->{data} }, "$output";
                    }
                    xCAT::MsgUtils->message("E", $rsp, $callback);
                } else {
                    if ($::VERBOSE) {
                        my $rsp;
                        push @{ $rsp->{data} }, "Removed the NIM network definition called \'$nimnet\'\n";
                        xCAT::MsgUtils->message("I", $rsp, $callback);
                    }
                }
            }
        }
    }

    return 0;
}

1;

