#!/usr/bin/env perl

#---------------------------------------------------------
# Configure Ethnet BNT switches
#---------------------------------------------------------

BEGIN
{
  $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
  $::XCATDIR  = $ENV{'XCATDIR'}  ? $ENV{'XCATDIR'}  : '/etc/xcat';
}
use lib "$::XCATROOT/lib/perl";


use strict;
use Socket;
use Getopt::Long;
use Expect;
use Net::Ping;
use xCAT::Usage;
use xCAT::NodeRange;
use xCAT::NetworkUtils;
use xCAT::Utils;
use xCAT::Table;
use xCAT::MsgUtils;

Getopt::Long::Configure("bundling");
$Getopt::Long::ignorecase = 0;

#global variables
my @nodes;
my @filternodes;


#---------------------------------------------------------
#Main

# parse the options
if (
    !GetOptions(
                'h|help'     => \$::HELP,
                'switches=s' => \$::SWITCH,  
                'port=s'     => \$::PORT,  
                'vlan=s'     => \$::VLAN,
                'user=s'     => \$::USER,
                'password=s' => \$::PASSWORD,
                'snmp'       => \$::SNMP,
                'ssh'        => \$::SSH,
                'license=s'  => \$::LICENSE,
                'ntp'        => \$::NTP,
                'all'        => \$::ALL,
    )
  )
{
    &usage;
    exit(1);
}

# display the usage if -h or --help is specified
if ($::HELP)
{
    &usage;
    exit(0);
}

if ($::SWITCH) {
    my @filternodes = xCAT::NodeRange::noderange( $::SWITCH );
    if (nodesmissed) {
        my $nodenotdefined = join(',', nodesmissed);
        xCAT::MsgUtils->message("I","The following nodes are not defined in xCAT DB: $nodenotdefined");
    }
    # check switch type
    my $switchestab =  xCAT::Table->new('switches');
    my $switches_hash = $switchestab->getNodesAttribs(\@filternodes,['switchtype']);
    foreach my $fsw (@filternodes)  {
        if (($switches_hash->{$fsw}->[0]->{switchtype}) =~ /cumulus/) {
            push @nodes, $fsw;
        } else {
            xCAT::MsgUtils->message("E","The $fsw is not cumulus switch, will not config");
        }
    }
    unless (@nodes) {
        xCAT::MsgUtils->message("E","No Valid Switch to process");
        exit(1);
    }
} else {
    xCAT::MsgUtils->message("E","Invalid flag, please provide switches with --switches");
    &usage;
    exit(1);
}

my $switches = join(",",@nodes);
my $cmd;
my $vlan;
my $port;
my $sub_req;
my $rc;

if (($::SSH) || ($::ALL))
{
    config_ssh();
}

if (($::LICENSE) || ($::ALL))
{
    install_license();
}

if (($::SNMP) || ($::ALL))
{
    config_snmp();
}
if (($::NTP) || ($::ALL))
{
    config_ntp();
}
if ($::VLAN)
{
    #config_vlan();
}
sub config_ssh {
    my $password = "CumulusLinux!";
    my $userid = "cumulus";
    my $timeout = 10;
    my $keyfile = "/root/.ssh/id_rsa.pub";
    my $rootkey = `cat /root/.ssh/id_rsa.pub`;
    my $cmd;
    my @config_switches;

    foreach my $switch (@nodes) {
        #remove old host key from /root/.ssh/known_hosts
        $cmd = `ssh-keygen -R $switch`;

        my ($exp, $errstr) = cumulus_connect($switch, $userid, $password, $timeout);
        if (!defined $exp) {
            print ("connect failed $errstr\n");
            next;
        }

        my $ret;
        my $err;

        ($ret, $err) = cumulus_exec($exp, "mkdir -p /root/.ssh");
        ($ret, $err) = cumulus_exec($exp, "chmod 700 /root/.ssh");
        ($ret, $err) = cumulus_exec($exp, "echo \"$rootkey\" >/root/.ssh/authorized_keys");
        ($ret, $err) = cumulus_exec($exp, "chmod 644 /root/.ssh/authorized_keys");

        $exp->hard_close();
        push (@config_switches, $switch);
    }

    if (@config_switches) {
        #update switch status
        my $csw = join(",",@config_switches);
        $cmd = "chdef $csw status=ssh_configed ";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        print "ssh configured for $csw\n";
    }
}

sub cumulus_connect {
     my $server   = shift;
     my $userid   = shift;
     my $password = shift;
     my $timeout  = shift;

     my $ssh      = Expect->new;
     my $command     = 'ssh';
     my @parameters  = ($userid . "@" . $server);

     $ssh->debug(0);
     $ssh->log_stdout(0);    # suppress stdout output..
     $ssh->slave->stty(qw(sane -echo));

     unless ($ssh->spawn($command, @parameters))
     {
         my $err = $!;
         $ssh->soft_close();
         my $rsp;
         return(undef, "unable to run command $command $err\n");
     }

     $ssh->expect($timeout,
                   [ "-re", qr/WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED/, sub {die "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"; } ],
                   [ "-re", qr/\(yes\/no\)\?\s*$/, sub { $ssh->send("yes\n");  exp_continue; } ],
                   [ "-re", qr/ password:/,        sub {$ssh->send("$password\n"); exp_continue; } ],
                   [ "-re", qr/:~\$/,              sub { $ssh->send("sudo su\n"); exp_continue; } ],
                   [ "-re", qr/ password for cumulus:/, sub { $ssh->send("$password\n"); exp_continue; } ],
                   [ "-re", qr/.*\/home\/cumulus#/, sub { $ssh->clear_accum(); } ],
                   [ timeout => sub { die "No login.\n"; } ]
                  );
     $ssh->clear_accum();
     return ($ssh);
}

sub cumulus_exec {
     my $exp = shift;
     my $cmd = shift;
     my $timeout    = shift;
     my $prompt =  shift;

     $timeout = 10 unless defined $timeout;
     $prompt = qr/.*\/home\/cumulus#/ unless defined $prompt;


     $exp->clear_accum();
     $exp->send("$cmd\n");
     my ($mpos, $merr, $mstr, $mbmatch, $mamatch) = $exp->expect(6,  "-re", $prompt);

     if (defined $merr) {
         return(undef,$merr);
     }
     return($mbmatch);
}

# for cumulus switch, need to set the license file
sub install_license {
    my @config_switches;
    print "install_license\n";
    my $license_file;
    my $file_name = "/root/license.txt";

    if ($::LICENSE) {
        $license_file = $::LICENSE;
    }

    print "file = $license_file\n";
    if (!(-e $license_file) ) {
        print "$license_file is not exist\n";
    }

    foreach my $switch (@nodes) {
        my $cmd = "xdcp $switch $license_file $file_name";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        if ($::RUNCMD_RC != 0) {
            xCAT::MsgUtils->message("E","Failed to xscp $license_file to $switch");
            next;
        }

        $cmd = "xdsh $switch '/usr/cumulus/bin/cl-license -i $file_name' ";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        if ($::RUNCMD_RC != 0) {
            xCAT::MsgUtils->message("E","Failed to $cmd to $switch");
            next;
        }
        push (@config_switches, $switch);
    }
    if (@config_switches) {
        my $csw = join(",",@config_switches);
        print "license is installed on $csw\n";
    }

}


#setup secure SNMP v3 
sub config_snmp {
    my $snmp_user;
    my $snmp_passwd;
    my @config_switches;
    my $cmd;

    print "start to config_snmp\n";
    if ($::USER) {
        $snmp_user = $::USER;
    } else {
        $snmp_user = "xcatadmin";
    }
    if ($::PASSWORD) {
        $snmp_passwd = $::PASSWORD;
    } else {
        $snmp_passwd = "xcatpassw0rd";
    }

    my $file = "temp.txt";
    open(FILE , ">$file")
            or die "cannot open file $file\n";
    print FILE "#xCAT modify following line\n";
    print FILE "agentAddress udp:161,udp6:[::1]:161\n";
    print FILE "rocommunity public default\n";
    print FILE "rocommunity public  default    -V systemonly\n";
    print FILE "createUser $snmp_user SHA $snmp_passwd\n";
    print FILE "rwuser $snmp_user\n";

    foreach my $switch (@nodes) {
        #check if xdsh works
        $cmd = "xdsh $switch date";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        if ($::RUNCMD_RC != 0) {
            xCAT::MsgUtils->message("E","xdsh command to $switch failed");
            next;
        }
        my $cmd_line = "sed -i 's/^agentAddress/#agentAddress/g' /etc/snmp/snmpd.conf";
        $cmd = "xdsh $switch $cmd_line";
        $rc= xCAT::Utils->runcmd($cmd, 0);

        $cmd = "xdcp $switch $file";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        $cmd = "xdsh $switch 'cat $file  >> /etc/snmp/snmpd.conf;rm -fr $file;systemctl restart snmpd;systemctl enable snmpd' ";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        if ($::RUNCMD_RC != 0) {
            xCAT::MsgUtils->message("E","Failed to update snmpd.conf for $switch");
            next;
        }
        push (@config_switches, $switch);
    }
    close FILE;
    $cmd = `rm -rf $file`;
    if (@config_switches) {
        #update switch status
        my $csw = join(",",@config_switches);
        $cmd = "chdef $csw status=snmp_configed snmpversion=3 snmpauth=sha snmpusername=$snmp_user snmppassword=$snmp_passwd";
        $rc= xCAT::Utils->runcmd($cmd, 0);
    }

}

sub config_ntp {
    my @config_switches;
    my $cmd;

    my $master = `hostname -i`;

    my $file = "temp.txt";
    open(FILE , ">$file")
            or die "cannot open file $file\n";
    print FILE "#This file is created by xCAT \n";
    print FILE "driftfile /var/lib/ntp/drift\n";
    print FILE "disable auth\n";
    print FILE "restrict 127.0.0.1\n";
    print FILE "server $master iburst\n";
    print FILE "interface listen eth0\n";

    foreach my $switch (@nodes) {
        #check if xdsh works
        $cmd = "xdsh $switch date";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        if ($::RUNCMD_RC != 0) {
            xCAT::MsgUtils->message("E","xdsh command to $switch failed");
            next;
        }
        my $cmd_line = "echo 'US/Eastern'>/etc/timezone;dpkg-reconfigure --frontend noninteractive tzdata";
        print "$cmd_line\n";
        $cmd = "xdsh $switch $cmd_line";
        if ($::RUNCMD_RC != 0) {
            print "Failed to update ntp timezone\n";
            xCAT::MsgUtils->message("E","Failed to update ntp timezone for $switch");
            next;
        }
        print "$cmd\n";
        $cmd = "xdcp $switch $file";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        $cmd = "xdsh $switch 'cp /etc/ntp.conf /etc/ntp.conf.orig;cp $file /etc/ntp.conf;rm -fr $file;systemctl restart ntp;systemctl enable ntp' ";
        $rc= xCAT::Utils->runcmd($cmd, 0);
        if ($::RUNCMD_RC != 0) {
            xCAT::MsgUtils->message("E","Failed to update ntp for $switch");
            next;
        }
        push (@config_switches, $switch);
    }
    close FILE;
    $cmd = `rm -rf $file`;

    if (@config_switches) {
        #update switch status
        my $csw = join(",",@config_switches);
        $cmd = "chdef $csw status=ntp_configed";
        $rc= xCAT::Utils->runcmd($cmd, 0);
    }

    
}

sub config_vlan {
    if ($::PORT) {
        $port = $::PORT;
    } else {
        &usage;
        exit(1);
    }
    $vlan = $::VLAN;
    print "Tagging VLAN=$vlan for $switches port $port\n";
    #create vlan, tagged vlan
    #$cmd = `xdsh $switches --devicetype EthSwitch::BNT "enable;configure terminal;vlan $vlan;exit;interface port $port;switchport mode trunk;switchport trunk allowed vlan $vlan;write memory;exit;exit"`;

}


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

=head3    usage

        Displays message for -h option

=cut

#---------------------------------------------------------
sub usage
{
    print "Usage:
    configBNT [-?│-h│--help] 
    configBNT [--switches switchnames] [--all] 
    configBNT [--switches switchnames] [--ssh] 
    configBNT [--switches switchnames] [--license filename ] 
    configBNT [--switches switchnames] [--snmp] [--user snmp_user] [--password snmp_password] [--group snmp_group]
    configBNT [--switches switchnames] [--ntp] 
    configBNT [--switches switchnames] [--port port] [--vlan vlan]
    \n";
}


