#!/usr/bin/perl -w
#use Data::Dumper;
use strict;
use warnings;

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

use Getopt::Long;
use File::Temp qw(tempfile);
use Fcntl ':flock';
use IPC::Open3;
use POSIX qw/WNOHANG/;
use Symbol qw/gensym/;
use Time::HiRes qw(sleep);
use xCAT::DHCP::OmapiPolicy;
use xCAT::Table;

sub usage{
    print "Usage: dhcphelper -h \n";
    print "\n";
    print "       dhcphelper -r|--rm -m|--mac <mac address> [ -a|--ip <ip address>] [ -n|--name <node name>]\n";
    print "       delete the dhcp lease of specified <mac>,<ip address> and <node name>\n";
    print "\n";

    return;
}

my $help;
my $rmop;
my $mac;
my $ip;
my $hostname;
GetOptions ("m|mac=s" => \$mac,    # numeric
            "a|ip=s"   => \$ip,      # string
            "n|name=s"   => \$hostname,      # string
            "r|rm"   => \$rmop,      # flag
            "h|help"  => \$help)   # flag
or &usage;

if($help){
    &usage;
    exit 0;
}elsif($rmop){
    require xCAT::DHCP::Backend;
    my $backend = xCAT::DHCP::Backend->new_backend();
    if (ref($backend) ne 'HASH' && $backend->name eq 'kea') {
        if ($mac && $mac !~ /:/) {
            $mac = lc($mac);
            $mac =~ s/(\w{2})/$1:/g;
            $mac =~ s/:$//;
        }

        mkdir "/tmp/xcat" unless -d "/tmp/xcat";
        open(my $dhcplockfd, ">", "/tmp/xcat/dhcplock") or die "Unable to lock DHCP state: $!";    ## no critic (InputOutput::RequireBriefOpen)
        flock($dhcplockfd, LOCK_EX);

        my $config4 = $backend->load_dhcp4_config();
        if ($config4->{error}) {
            print "Error: $config4->{error}\n";
            exit 1;
        }
        my $config6;
        my $have_dhcp6 = -e $backend->dhcp6_config_file();
        if ($have_dhcp6) {
            $config6 = $backend->load_dhcp6_config();
            if ($config6->{error}) {
                print "Error: $config6->{error}\n";
                exit 1;
            }
        }

        my @matches;
        push @matches, { hostname => $hostname } if $hostname;
        push @matches, { 'hw-address' => lc($mac) } if $mac;
        push @matches, { 'ip-address' => $ip } if $ip;

        foreach my $match (@matches) {
            $backend->delete_reservations($config4, $match);
            $backend->delete_reservations($config6, $match) if $config6;
        }

        my $result = $backend->write_dhcp4_json($backend->encode_config($config4));
        if ($result->{error}) {
            print "Error: $result->{error}\n";
            exit 1;
        }
        if ($config6) {
            my $result6 = $backend->write_dhcp6_json($backend->encode_config($config6));
            if ($result6->{error}) {
                print "Error: $result6->{error}\n";
                exit 1;
            }
        }

        my $restart = $backend->restart_services(ipv6 => $config6 ? 1 : 0);
        if ($restart->{error}) {
            print "Error: $restart->{error}\n";
            exit 1;
        }
        exit 0;
    }

    my $settings = xCAT::DHCP::OmapiPolicy->settings();
    if ($settings->{error}) {
        print "Error: $settings->{error}\n";
        exit 1;
    }

    my $passtab = xCAT::Table->new('passwd');
    my $pent = $passtab->getAttribs({ key => 'omapi', username => $settings->{key_name} }, qw(username password));
    my $passwd = $pent ? $pent->{password} : undef;
    if(!$passwd){
        print "Error: no 'omapi' entry defined in passwd table!";
        exit 1;
    }

    my $omshell_commands = xCAT::DHCP::OmapiPolicy->omshell_preamble($settings, secret => $passwd);
    $omshell_commands .= "connect\n";

    if($hostname){
        $omshell_commands .= "new host\n";
        $omshell_commands .= "set name = \"$hostname\"\n";    #Find and destroy conflict name
        $omshell_commands .= "open\n";
        $omshell_commands .= "remove\n";
        $omshell_commands .= "close\n";
    }

    if ($mac)
    {
        $omshell_commands .= "new host\n";
        $omshell_commands .= "set hardware-address = " . $mac . "\n";    #find and destroy mac conflict
        $omshell_commands .= "open\n";
        $omshell_commands .= "remove\n";
        $omshell_commands .= "close\n";
    }

    if($ip){
       $omshell_commands .= "new host\n";
       $omshell_commands .= "set ip-address = $ip\n";    #find and destroy ip conflict
       $omshell_commands .= "open\n";
       $omshell_commands .= "remove\n";
       $omshell_commands .= "close\n";
    }

    mkdir "/tmp/xcat" unless -d "/tmp/xcat";
    my ($omshell, $command_file) = tempfile('omshell.XXXXXX', DIR => '/tmp/xcat', UNLINK => 0);
    die "Unable to start omshell: $!" unless $omshell;
    print $omshell $omshell_commands;
    close($omshell);

    my $pid = fork();
    die "Unable to start omshell: $!" unless defined $pid;
    if ($pid == 0) {
        open(STDIN, '<', $command_file) or exit 127;          ## no critic (InputOutput::RequireCheckedOpen)
        open(STDOUT, '>', '/dev/null') or exit 127;           ## no critic (InputOutput::RequireCheckedOpen)
        open(STDERR, '>', '/dev/null') or exit 127;           ## no critic (InputOutput::RequireCheckedOpen)
        exec { $settings->{omshell_path} } $settings->{omshell_path};
        exit 127;
    }

    for (1 .. 100) {
        if (waitpid($pid, WNOHANG) == $pid) {
            sleep 1.0;
            unlink $command_file;
            exit 0;
        }
        sleep 0.1;
    }
    kill 'TERM', $pid;
    for (1 .. 20) {
        if (waitpid($pid, WNOHANG) == $pid) {
            unlink $command_file;
            exit 0;
        }
        sleep 0.1;
    }
    kill 'KILL', $pid;
    waitpid($pid, 0);
    unlink $command_file;
}else{
    &usage;
    exit 1;
}


#print "$mac-$ip-$hostname\n"
exit 0;
