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

=head1
  xCAT plugin package to handle mkzone,chzone,Input rmzone commands 

   Supported command:
         mkzone,chzone,rmzone - manage xcat cluster zones 

=cut

#-------------------------------------------------------
package xCAT_plugin::zone;
BEGIN
{
  $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
}

use strict;
require xCAT::Utils;
require xCAT::Zone;
require xCAT::MsgUtils;
require xCAT::Table;
use xCAT::NodeRange;
use xCAT::NodeRange qw/noderange abbreviate_noderange/;

use Getopt::Long;


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

=head3  handled_commands

Return list of commands handled by this plugin

=cut

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

sub handled_commands
{
    return {mkzone => "zone",
            chzone => "zone",
            rmzone => "zone",
            };
}



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

=head3  process_request

  Process the command, this only runs on the management node

=cut

#-------------------------------------------------------
sub process_request
{

    my $request  = shift;
    my $callback = shift;
    my $sub_req = shift;
    $::CALLBACK = $callback;
    my $command = $request->{command}->[0];
    my $rc=0;
    # the directory which will contain the zone keys
    my $keydir="/etc/xcat/sshkeys/";
    
    # check if Management Node, if not error
    unless (xCAT::Utils->isMN()) 
    {
            my $rsp = {};
            $rsp->{error}->[0] = "The $command may only be run on the Management Node.";
            xCAT::MsgUtils->message("E", $rsp, $callback, 1);
            return 1; 

    }
    # you may not run on AIX
    if (xCAT::Utils->isAIX()) {
            my $rsp = {};
            $rsp->{error}->[0] = "The $command may only be run on a Linux Cluster.";
            xCAT::MsgUtils->message("E", $rsp, $callback, 1);
            return 1; 
    }
    # test to see if any parms 
    if (scalar($request->{arg} == 0)) {
        my $rsp = {};
        $rsp->{error}->[0] =
          "No parameters input to the $command command,  see man page for syntax.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        exit 1;
    }

    my $args = $request->{arg};
    @ARGV = @{$args};    # get arguments
    # Get the zonename if it is in the input 
    my @SaveARGV = @ARGV;
    my $zonename;
    my $arg= @SaveARGV[0];
    if (!($arg =~ /-h/) && (!($arg =~ /-v/)))  {   # if not -h -v,then it must be a zone name 
     $zonename = @SaveARGV[0];  # here is the zonename, if there is one
     if ($zonename) {   #  take zonename off the argument list so it will parse correctly
        my $tmp = shift(@SaveARGV);
        @ARGV = @SaveARGV;       
     }
    }
    Getopt::Long::Configure("posix_default");
    Getopt::Long::Configure("no_gnu_compat");
    Getopt::Long::Configure("bundling");
    my %options = ();

    if (
        !GetOptions(
                    'a|noderange=s'   => \$options{'addnoderange'},
                    'r|noderange=s'   => \$options{'rmnoderange'},
                    'defaultzone|defaultzone'   => \$options{'defaultzone'}, 
                    'g|assigngrp'     => \$options{'assigngroup'},
                    'f|force'        => \$options{'force'},
                    'h|help'        => \$options{'help'},
                    'k|sshkeypath=s'   => \$options{'sshkeypath'},
                    'K|genkeys'     => \$options{'gensshkeys'},
                    's|sshbetweennodes=s'     => \$options{'sshbetweennodes'},
                    'v|version'     => \$options{'version'},
                    'V|Verbose'     => \$options{'verbose'},
        )
      )
    {

        &usage($callback,$command);
        exit 1;
    }
    # if the ARGS still have data we have invalid input
    if (@ARGV) {
            my $args=join(',', @ARGV);
            my $rsp = {};
            $rsp->{error}->[0] =
              "The input to the command: $command contained invalid arguments: $args.";
            xCAT::MsgUtils->message("E", $rsp, $callback, 1);
            exit 1; 
    }
 
    if ($options{'help'})
    {
        &usage($callback,$command);
        exit 0;
    }
    if ($options{'version'})
    {
        my $version = xCAT::Utils->Version();
        my $rsp = {};
        $rsp->{data}->[0] = $version;
        xCAT::MsgUtils->message("I", $rsp, $callback);
        exit 0;
    }
    # test to see if the zonename was input
    if (!$zonename) {
        my $rsp = {};
        $rsp->{error}->[0] =
          "zonename not specified, it is required for this command.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        exit 1;
    } else {
       $request->{zonename} = $zonename;
    }
    # if -s entered must be yes/1 or no/0
    if ($options{'sshbetweennodes'}) {
      if ($options{'sshbetweennodes'}=~ /^yes$/i || $options{'sshbetweennodes'} eq "1") {
           $options{'sshbetweennodes'}= "yes";
      } else {
        if ($options{'sshbetweennodes'}=~ /^no$/i || $options{'sshbetweennodes'} eq "0") {
           $options{'sshbetweennodes'}= "no";
        } else {
             my $rsp = {};
             $rsp->{error}->[0] =
              "The input on the -s flag $options{'sshbetweennodes'} is not valid.";
             xCAT::MsgUtils->message("E", $rsp, $callback);
             exit 1;
        }
      }
    }
    # cannot enter -K and -k
    if (($options{'sshkeypath'}) && ($options{'gensshkeys'})) {
      my $rsp = {};
      $rsp->{error}->[0] =
      "The input of -k and -K is not valid on the command : $command.";
      xCAT::MsgUtils->message("E", $rsp, $callback);
      exit 1;
    }

    # check for site.sshbetweennodes attribute, put out a warning it will not be used as long
    # as zones are defined in the zone table.
    my @entries =  xCAT::TableUtils->get_site_attribute("sshbetweennodes");    
    if ($entries[0]) {
         my $rsp = {};
         $rsp->{info}->[0] =
              "The site table sshbetweennodes attribute is set to $entries[0].  It is not used when zones are defined.  To get rid of this warning, remove the site table sshbetweennodes attribute.";
             xCAT::MsgUtils->message("I", $rsp, $callback);
    }
    #  -a and -r flags cannot be used together
    if (($options{'addnoderange'})  && ($options{'rmnoderange'})) {
        my $rsp = {};
        $rsp->{error}->[0] =
              "You may not use the -a flag to add nodes and the -r flag to remove nodes on one command.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        exit 1;
           
    }
    # save input noderange  to add nodes
    if ($options{'addnoderange'}) {
       
       # check to see if Management Node is in the noderange, if so error
       $request->{noderange}->[0] = $options{'addnoderange'};
       my @nodes = xCAT::NodeRange::noderange($request->{noderange}->[0]);
       my @mname = xCAT::Utils->noderangecontainsMn(@nodes); 
       if (@mname)
        {    # MN in the nodelist
            my $nodes=join(',', @mname);
            my $rsp = {};
            $rsp->{error}->[0] =
              "You must not run $command and include the  management node: $nodes.";
            xCAT::MsgUtils->message("E", $rsp, $callback, 1);
            exit 1; 
        }
 
 
    }
    # save input noderange  to remove nodes
    if ($options{'rmnoderange'}) {
       
       # check to see if Management Node is in the noderange, if so error
       $request->{noderange}->[0] = $options{'rmnoderange'};
       my @nodes = xCAT::NodeRange::noderange($request->{noderange}->[0]);
       my @mname = xCAT::Utils->noderangecontainsMn(@nodes); 
       if (@mname)
        {    # MN in the nodelist
            my $nodes=join(',', @mname);
            my $rsp = {};
            $rsp->{error}->[0] =
              "You must not run $command and include the  management node: $nodes.";
            xCAT::MsgUtils->message("E", $rsp, $callback, 1);
            exit 1; 
        }
 
 
    }
    if ($options{'verbose'})
    {
        $::VERBOSE = "yes";
    }

    if ($command eq "mkzone")
    {
      $rc=mkzone($request, $callback,\%options,$keydir);
    }
    if ($command eq "chzone")
    {
        $rc=chzone($request, $callback,\%options,$keydir);
    }
    if ($command eq "rmzone")
    {
         $rc=rmzone($request, $callback,\%options);
    }
    my $rsp = {};
    if ($rc ==0) {
      $rsp->{info}->[0] = "The $command ran successfully.";
      xCAT::MsgUtils->message("I", $rsp, $callback);
    } else {
      $rsp->{error}->[0] = "The $command had errors.";
      xCAT::MsgUtils->message("E", $rsp, $callback);
    }
    return $rc; 

}

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

=head3   

   Parses and runs  mkzone 
   Input 
     request
     callback
     Input  arguments from the GetOpts
     zone ssh key dir

=cut

#-------------------------------------------------------
sub mkzone 
{
    my ($request, $callback,$options,$keydir) = @_;
    my $rc=0;
    # already checked but lets do it again,  need a zonename, it is the only required parm
    if (!($request->{zonename})) {

        my $rsp = {};
        $rsp->{error}->[0] =
          "zonename not specified The zonename is required.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }
    # test for -g, if no noderange this is an error 
    if (( ! defined($$options{'addnoderange'})) && ($$options{'assigngroup'})) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " The -g flag requires a noderange ( -a).";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }  
    # test for -r,  not valid 
    if ($$options{'rmnoderange'}) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " The -r flag Is not valid for mkzone. Use chzone.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }  
    # check to see if the input zone already exists
    if (xCAT::Zone->iszonedefined($request->{zonename})) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " zonename: $request->{zonename} already defined, use chzone or rmzone to change or remove it.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }
    
    # Create path to generated ssh keys
    # keydir comes in set to /etc/xcat/sshkeys
    $keydir .= $request->{zonename}; 
    $keydir .= "/.ssh"; 
    

    # add new zones to the zone table 
    $rc=addtozonetable($request, $callback,$options,$keydir);
    if ($rc == 0) {  # zone table setup is ok
      # test for a noderange, if(-a) not supplied nothing to do
      if (defined($$options{'addnoderange'})) {
        $rc=addnodestozone($request, $callback,$options,$keydir);
      }  
      if ($rc == 0) {  # zone table setup is ok
        # generate root ssh keys
        $rc=gensshkeys($request, $callback,$options,$keydir);
        if ($rc != 0) {
          return 1;
        }
      }
    }


    return $rc;

}
#-------------------------------------------------------

=head3   

   Parses and runs chzone 
   Input 
     request
     callback
     Input  arguments from the GetOpts


=cut

#-------------------------------------------------------
sub chzone 
{
    my ($request, $callback,$options,$keydir) = @_;
    my $rc=0;
    # Create default  path to generated ssh keys
    # keydir comes in set to /etc/xcat/sshkeys
    $keydir .= $request->{zonename}; 
    $keydir .= "/.ssh"; 
    my $zonename=$request->{zonename};
    # already checked but lets do it again,  need a zonename
    if (!($request->{zonename})) {

        my $rsp = {};
        $rsp->{error}->[0] =
          "zonename not specified The zonename is required.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }
    # see if they asked to do anything
    if ((!($$options{'sshkeypath'})) && (!($$options{'gensshkeys'})) && 
       (!( $$options{'addnoderange'})) && (!( $$options{'rmnoderange'})) &&
       (!( $$options{'defaultzone'})) &&
        (!($$options{'assigngroup'} )) && (!($$options{'sshbetweennodes'}))) {
        my $rsp = {};
        $rsp->{info}->[0] =
          "chzone was run but nothing to do.";
        xCAT::MsgUtils->message("I", $rsp, $callback);
        return 0;
    }
    # test for -g, if no noderange (-r or -a) this is an error 
    if ((( ! defined($$options{'addnoderange'}))&& ( ! defined($$options{'rmnoderange'}))) && ($$options{'assigngroup'})) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " The -g flag requires a noderange using the -a or -r option.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }

    #  if -r remove nodes from zone, check to see that they are a member of the zone
    #  if not a member of the zone,  error out and do nothing
    if ($$options{'rmnoderange'}){
      my @nodes = xCAT::NodeRange::noderange($request->{noderange}->[0]);
     
      foreach my $node (@nodes) {
        my $nodezonename=xCAT::Zone->getmyzonename($node);
        if ($nodezonename ne $zonename) {
            my $rsp = {};
            $rsp->{error}->[0] =
             " $node does not belong to the zone:$zonename. Rerun the chzone -r  command with only nodes in the noderange that are currently assigned to the zone.";
            xCAT::MsgUtils->message("E", $rsp, $callback);
            return 1;
        }  
       
      }  
    }  
    # get the zone ssh key directory. We don't have a good zone without it.
    my $sshrootkeydir = xCAT::Zone->getzonekeydir($zonename);
    if ($sshrootkeydir == 1) { # error return
       #if we have been requested to regenerated the ssh keys continue
       if (($$options{'sshkeypath'}) || ($$options{'gensshkeys'})) { 
           my $rsp = {};
           $rsp->{info}->[0] =
           " sshkeydir attribute not defined for $zonename. The zone sshkeydir will be regenerated.";
           xCAT::MsgUtils->message("I", $rsp, $callback);
       } else {  # sshkeydir is missing  and they did not request to regenerate,   that is an error
           my $rsp = {};
           $rsp->{error}->[0] =
           " sshkeydir attribute not defined for $zonename. The zone sshkeydir must be regenerated. Rerun this command with -k or -K options";
           xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
       }
    } else {  # we got a sshkeydir from the database, use it
        $keydir=$sshrootkeydir;
    }
    # do we regenerate keys (-k or -K)
    if (($$options{'sshkeypath'}) || ($$options{'gensshkeys'})) {
       $rc=gensshkeys($request, $callback,$options,$keydir);
       if ($rc != 0) {
         return 1;
       }
    }
    
    # update the zone table 
    $rc=updatezonetable($request, $callback,$options,$keydir);
    if ($rc == 0) {  # zone table setup is ok
      # update the nodelist table
      if (defined($$options{'addnoderange'})) {
        $rc=addnodestozone($request, $callback,$options,$keydir);
      } else {  # note -a and -r are not allowed on one chzone
          if (defined($$options{'rmnoderange'})) {
            $rc=rmnodesfromzone($request, $callback,$options,$keydir);
          }
      }
    }


    return $rc;
}
#-------------------------------------------------------

=head3   

   Parses and runs rmzone 
   Input 
     request
     callback
     Input  arguments from the GetOpts
      

=cut

#-------------------------------------------------------
sub rmzone 
{
    my ($request, $callback,$options) = @_;
    
    # already checked but lets do it again,  need a zonename, it is the only required parm
    if (!($request->{zonename})) {

        my $rsp = {};
        $rsp->{error}->[0] =
          "zonename not specified The zonename is required.";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }
    # see if they input invalid flags 
    if (($$options{'sshkeypath'}) || ($$options{'gensshkeys'}) || 
       ( $$options{'addnoderange'}) || ( $$options{'rmnoderange'}) ||
       ( $$options{'defaultzone'}) || 
       ($$options{'sshbetweennodes'})) {
        
        my $rsp = {};
        $rsp->{error}->[0] =
          "The following flags are not valid input for the rmzone command: -k,-K,-a,-r,-f,-s  ";
        xCAT::MsgUtils->message("E", $rsp, $callback);
        return 1;
    }
    # check to see if the input zone already exists
    # cannot remove it if it is not defined
    my $zonename=$request->{zonename};
    if (!(xCAT::Zone->iszonedefined($zonename))) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " zonename: $zonename is not defined. You cannot remove it.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }
    # is this zone is the default zone you must force the delete
    my $defaultzone =xCAT::Zone->getdefaultzone($callback);
    if (($defaultzone eq $zonename) && (!($$options{'force'}))) {
       my $rsp = {};     
       $rsp->{error}->[0] =
        " You are removing the default zone: $zonename.  You must define another default zone before deleting or use the -f flag to force the removal.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }

    # get the zone ssh key directory
    my $sshrootkeydir = xCAT::Zone->getzonekeydir($zonename);
    if ($sshrootkeydir == 1) { # error return
       my $rsp = {};
       $rsp->{info}->[0] =
        " sshkeydir attribute not defined for $zonename. Cannot remove it.";
       xCAT::MsgUtils->message("I", $rsp, $callback);
    } else {  # remove the keys  unless it is /root/.ssh
       my $roothome = xCAT::Utils->getHomeDir("root");
       $roothome .="\/.ssh";
       if ($sshrootkeydir eq $roothome) {  # will not delete /root/.ssh
           my $rsp = {};
           $rsp->{info}->[0] =
             "  $zonename sshkeydir is $roothome. This will not be deleted.";
             xCAT::MsgUtils->message("I", $rsp, $callback);
       } else {  # not roothome/.ssh
         # check to see if id_rsa.pub is there. I don't want to remove the
         # wrong directory
         # if id_rsa.pub exists remove the files
         # then remove the directory
         if ( -e "$sshrootkeydir/id_rsa.pub") {
           my $cmd= "rm -rf $sshrootkeydir";    
           xCAT::Utils->runcmd($cmd,0);
           if ($::RUNCMD_RC != 0)
           {
              my $rsp = {};
              $rsp->{error}->[0] = "Command: $cmd failed";
              xCAT::MsgUtils->message("E", $rsp, $callback);
           }
           my ($zonedir,$ssh)= split(/\.ssh/, $sshrootkeydir);
           $cmd= "rmdir $zonedir";    
           xCAT::Utils->runcmd($cmd,0);
           if ($::RUNCMD_RC != 0)
           {
              my $rsp = {};
              $rsp->{error}->[0] = "Command: $cmd failed";
              xCAT::MsgUtils->message("E", $rsp, $callback);
           }
          } else {  #  no id_rsa.pub key will not remove the files
              my $rsp = {};
              $rsp->{info}->[0] = "$sshrootkeydir did not contain an id_rsa.pub key, will not remove files";
              xCAT::MsgUtils->message("I", $rsp, $callback);
          }
       }
    
    }

    # open zone table and remove this entry
    my $tab = xCAT::Table->new("zone");
    if (!defined($tab)) {  
       my $rsp = {};
       $rsp->{error}->[0] =
        " Failure opening the zone table.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }
    # remove the table entry
    $tab->delEntries({zonename=>$zonename});

    # remove zonename and possibly group name (-g flag) from  any nodes defined in this zone
    my $rc=rmnodesfromzone($request, $callback,$options,"ALL");

    return $rc;



}
#-------------------------------------------------------------------------------

=head3
      usage

        puts out zone command usage message

        Arguments:
          None

        Returns:

        Globals:


        Error:
                None


=cut

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

sub usage
{
    my ($callback, $command) = @_;
    my $usagemsg1="";
    my $usagemsg2="";
    if ($command eq "mkzone") {
       $usagemsg1  = " mkzone -h \n mkzone -v \n";
       $usagemsg2  = " mkzone <zonename> [-V] [--defaultzone] [-k <full path to the ssh RSA private key>] \n        [-a <noderange>] [-g] [-f] [-s <yes/no>]";
    } else {
       if ($command eq "chzone") {
           $usagemsg1  = " chzone -h \n chzone -v \n";
           $usagemsg2  = " chzone <zonename> [-V] [--defaultzone] [-k <full path to the ssh RSA private key>] \n      [-K] [-a <noderange>] [-r <noderange>] [-g] [-s <yes/no>]";
       } else {
            if ($command eq "rmzone") {
               $usagemsg1  = " rmzone -h \n rmzone -v \n";
               $usagemsg2  = " rmzone <zonename> [-g]";
            }
       }
    }
    my $usagemsg .= $usagemsg1 .=  $usagemsg2 ;
    if ($callback)
    {
        my $rsp = {};
        $rsp->{data}->[0] = $usagemsg;
        xCAT::MsgUtils->message("I", $rsp, $callback);
    }
    else
    {
        xCAT::MsgUtils->message("I", $usagemsg);
    }
    return;
}
#-------------------------------------------------------

=head3   

   generate the ssh keys and store them in /etc/xcat/sshkeys/<zonename>/.ssh 


=cut

#-------------------------------------------------------
sub gensshkeys 
{
    my ($request, $callback,$options,$keydir) = @_;
    my $rc=0;
    # generate root ssh keys
    # Did they input a path to existing RSA keys
    my $rsakey;
    my $zonename=$request->{zonename};
    if ($$options{'sshkeypath'}) {
        # check to see if RSA keys exists
        $rsakey= $$options{'sshkeypath'} .= "/id_rsa";
        if (!(-e $rsakey)){   # if it does not exist error out
           my $rsp = {};
           $rsp->{error}->[0] =
             "Input $rsakey does not exist.  Cannot generate the ssh root keys for the zone.";
           xCAT::MsgUtils->message("E", $rsp, $callback);
           return 1;
        }
    } 
    
    $rc =xCAT::Zone->genSSHRootKeys($callback,$keydir, $zonename,$rsakey);
    if ($rc !=0) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " Failure generating the ssh root keys for the zone.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }

    return  $rc;

}
#-------------------------------------------------------

=head3   
    addtozonetable
    Add the new zone to the zone table, check if already there and 
    error - use either chzone or -f to override default 



=cut

#-------------------------------------------------------
sub addtozonetable 
{
    my ($request, $callback,$options,$keydir) = @_;
    my $rc=0;
    my $zoneentry;
    my $tab = xCAT::Table->new("zone");
    if ($tab)
    {
     # read a record from the zone table, if it is empty then add
     #  the xcatdefault entry
     my @zones = $tab->getAllAttribs('zonename');
     if (!(@zones)) {  # table empty
       my %xcatdefaultzone;
       $xcatdefaultzone{defaultzone} ="yes";
       $xcatdefaultzone{sshbetweennodes} ="yes";
       my $roothome = xCAT::Utils->getHomeDir("root");
       $roothome .="\/.ssh";
       $xcatdefaultzone{sshkeydir} =$roothome;
       $tab->setAttribs({zonename => "xcatdefault"}, \%xcatdefaultzone);
     }

     # now add the users zone
     my %tb_cols;
     $tb_cols{sshkeydir} = $keydir;  # key directory
     # set sshbetweennodes attribute from -s flag or default to yes
     if ( $$options{'sshbetweennodes'}) {
        $tb_cols{sshbetweennodes} = $$options{'sshbetweennodes'};         
     } else {
        $tb_cols{sshbetweennodes} = "yes";         
     }
     my $zonename=$request->{zonename};
     if ( $$options{'defaultzone'}) {  # set the default
       # check to see if a default already defined
       my $curdefaultzone = xCAT::Zone->getdefaultzone($callback);
       if (!(defined ($curdefaultzone))) {  # no default defined
           $tb_cols{defaultzone} ="yes";
           $tab->setAttribs({zonename => $zonename}, \%tb_cols);
           $tab->commit();
           $tab->close();
       } else { # already a default
          if ($$options{'force'}) {  # force the default
            $tb_cols{defaultzone} ="yes";
            $tab->setAttribs({zonename => $zonename}, \%tb_cols);
            # now change the old default zone to not be the default
            my %tb1_cols;
            $tb1_cols{defaultzone} ="no";
            $tab->setAttribs({zonename => $curdefaultzone}, \%tb1_cols);
            $tab->commit();
            $tab->close();
          } else {  # no force this is an error
             my $rsp = {};
             $rsp->{error}->[0] =
             " Failure setting default zone. The defaultzone $curdefaultzone already exists. Use the -f flag if you want to override the current default zone.";
             xCAT::MsgUtils->message("E", $rsp, $callback);
             return 1;
          }
       }
     } else { # not a default zone
       $tb_cols{defaultzone} ="no";
       $tab->setAttribs({zonename => $zonename}, \%tb_cols);
       $tab->commit();
       $tab->close();
     }
    } else {
       my $rsp = {};
       $rsp->{error}->[0] =
        " Failure opening the zone table.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }

     
    return  $rc;

}
#-------------------------------------------------------

=head3   
    updatezonetable
    change either the sshbetweennodes or defaultzone  attribute
    or generate new keys ( -k -K)


=cut

#-------------------------------------------------------
sub updatezonetable 
{
    my ($request, $callback,$options,$keydir) = @_;
    my $zoneentry; 
    my $zonename=$request->{zonename};
    # check for changes
    if (($$options{'sshbetweennodes'}) || ( $$options{'defaultzone'}) ||
       ($$options{'sshkeypath'}) || ($$options{'gensshkeys'})) { 

      my $tab = xCAT::Table->new("zone");
      if($tab) {

       # now add the users changes 
       my %tb_cols;
       # generated keys ( -k or -K)
       if (($$options{'sshkeypath'}) || ($$options{'gensshkeys'})) {
         $tb_cols{sshkeydir} = $keydir;  # key directory
       }
       # set sshbetweennodes attribute from -s flag 
       if ( $$options{'sshbetweennodes'}) {
        $tb_cols{sshbetweennodes} = $$options{'sshbetweennodes'};         
       }
       # if --defaultzone
       if ( $$options{'defaultzone'}) {  # set the default
         # check to see if a default already defined
         my $curdefaultzone = xCAT::Zone->getdefaultzone($callback);
         if (!(defined ($curdefaultzone))) {  # no default defined
           $tb_cols{defaultzone} ="yes";
           $tab->setAttribs({zonename => $zonename}, \%tb_cols);
           $tab->commit();
           $tab->close();
         } else { # already a default
            if ($$options{'force'}) {  # force the default
              $tb_cols{defaultzone} ="yes";
              $tab->setAttribs({zonename => $zonename}, \%tb_cols);
              # now change the old default zone to not be the default
              my %tb1_cols;
              $tb1_cols{defaultzone} ="no";
              $tab->setAttribs({zonename => $curdefaultzone}, \%tb1_cols);
              $tab->commit();
              $tab->close();
            } else {  # no force this is an error
               my $rsp = {};
               $rsp->{error}->[0] =
               " Failure setting default zone. The defaultzone $curdefaultzone already exists. Use the -f flag if you want to override the current default zone.";
               xCAT::MsgUtils->message("E", $rsp, $callback);
               return 1;
            }
         }
       } else { # not a default zone change, just commit the other changes
         $tab->setAttribs({zonename => $zonename}, \%tb_cols);
         $tab->commit();
         $tab->close();
       }
      } else {
         my $rsp = {};
         $rsp->{error}->[0] =
        " Failure opening the zone table.";
         xCAT::MsgUtils->message("E", $rsp, $callback);
         return 1;
      }
  }

     
  return  0;

}
#-------------------------------------------------------

=head3   
    addnodestozone 
    Add the new zonename attribute to any nodes in the noderange ( if a noderange specified) 
    Add zonename group to nodes in the noderange if -g flag. 



=cut

#-------------------------------------------------------
sub addnodestozone 
{
    my ($request, $callback,$options,$keydir) = @_;
    my $rc=0;
    my $zonename=$request->{zonename};
    # if -g add zonename group also
    my @nodes = xCAT::NodeRange::noderange($request->{noderange}->[0]);
    # check to see if noderange expanded
    if (!(scalar @nodes)) {
       my $rsp = {};
       $rsp->{error}->[0] =
        " The noderange $request->{noderange}->[0] is not valid. The nodes are not defined.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    } 
    my $tab = xCAT::Table->new("nodelist");
    if ($tab)
    {
      # if -g then add the zonename to the group attribute on each node
      if ($$options{'assigngroup'}){
         foreach my $node (@nodes) {
             xCAT::TableUtils->updatenodegroups($node,$tab,$zonename);
         }
      }
      # set the nodelist zonename attribute to the zonename for all nodes in the range
      $tab-> setNodesAttribs(\@nodes, { zonename => $zonename });
      $tab->commit();
      $tab->close();
    } else {
       my $rsp = {};
       $rsp->{error}->[0] =
        " Failure opening the nodelist table.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }
    return  $rc;

}
#-------------------------------------------------------

=head3   
    rmnodesfromzone 
    removes the zonename from all nodes with their zonename the input zone or
    the noderange supplied on the -r flag
    if -g, removes zonename group from all nodes defined with their zonename the input zone. 
    Note if $ALL is input it removes all nodes from the zone, 
     otherwise  $request->{noderange} points to the noderange


=cut

#-------------------------------------------------------
sub rmnodesfromzone 
{
    my ($request, $callback,$options,$ALL) = @_;
    my $zonename=$request->{zonename};
    my $tab = xCAT::Table->new("nodelist");
    if ($tab)
    {
      # read all the nodes with zonename
      my @nodes;
      if ($ALL) {  # do all nodes
        @nodes = xCAT::Zone->getnodesinzone($callback,$zonename);
      } else {  # the nodes in the noderange ( -r )
        @nodes = xCAT::NodeRange::noderange($request->{noderange}->[0]);
        # check to see if noderange expanded
        if (!(scalar @nodes)) {
           my $rsp = {};
           $rsp->{error}->[0] =
           " The noderange $request->{noderange}->[0] is not valid. The nodes are not defined.";
           xCAT::MsgUtils->message("E", $rsp, $callback);
           return 1;
        } 
      }
      # if -g then remove the zonename  group attribute on each node
      if ($$options{'assigngroup'}){
         foreach my $node (@nodes) {
             xCAT::TableUtils->rmnodegroups($node,$tab,$zonename);
         }
      }
      # set the nodelist zonename to nothing
      my $nozonename=""; 
      $tab-> setNodesAttribs(\@nodes, { zonename => $nozonename  });
      $tab->commit();
      $tab->close();
    } else {
       my $rsp = {};
       $rsp->{error}->[0] =
        " Failure opening the nodelist table.";
       xCAT::MsgUtils->message("E", $rsp, $callback);
       return 1;
    }
    return  0;

}


1;