#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2005,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)96   1.50.1.2   src/csm/install/makedhcp.perl, setup, csm_rfish, rfishs001b 1/8/07 20:41:20
#####################################################################
BEGIN
{
	$::CSM        = 1;
	$::MSGCAT     = 'csmInstall.cat';
	$::MSGMAPPATH =
	  $ENV{'CSM_MSGMAPS'} ? $ENV{'CSM_MSGMAPS'} : '/opt/csm/msgmaps';
	$::MSGSET = 'makedhcp';
	$::csmpm = $ENV{'CSM_PM'} ? $ENV{'CSM_PM'} : '/opt/csm/pm';
}

use strict;
use Getopt::Long;
use Socket;
use IO::Handle;

use lib $::csmpm;
use NodeUtils;
use CSMDefs;
use MessageUtils;

# functions
sub msg
{
	my $msg     = shift;
	my $sevcode = shift;
	my $msgid   = shift;
	if (!$sevcode)
	{
		$sevcode = "I";
	}
	if ($::CSM)
	{
		MessageUtils->messageFromCat($::MSGCAT, $::MSGMAPPATH, $::MSGSET,
									 $sevcode, $msgid, @_);
	}
	else
	{
		if ($msg)
		{
			print $msg;
		}

		# if this is an error message, exit after print.
		if (grep "E", $sevcode)
		{
			exit(1);
		}
	}
}

sub initialize
{
	@::VALID_ATTRS = ("Netmask", "Gateway", "Nameservers", "Network");
	%::ATTRS;
	%::NODEHASH;

	chomp($::ME      = `hostname | awk -F. '{print \$1}'`);
	chomp($::IS_ARCH = `/bin/uname -m`);
	@::DHCP;

	# content in @::DHCP has already been synced into dhcp config file.
	$::CONTENT_SYNCED = 0;

	# set this flag to 1 only after the dhcp config file is actually changed.
	$::RESTART_DHCP = 0;

	$::IS_DISTRO = NodeUtils->get_DistributionName();

	# get the definitions we need for this command
	# in this case get_OSDefs uses the attrs of the management
	# server to identify the correct definitions
	%::msOSDefs = ServerUtils->get_OSDefs();

	require xCATCSM;

}

# show usage message
sub usage
{
	my $rc = shift;
	msg(
		"Usage:  Usage: makedhcp [-n|--new] [-a|--allmac] [-nd|--nodynamic] [noderange]\n
	[-d|--delete noderange]\n
	[--add-subnet network/mask [Gateway=<gw>][Nameservers=<ns_list>]]\n
	[--add-network NIC]\n
	[--add-dynamic IPaddr_or_range [Netmask=<netmask>] [Arch=x86|ia32|ia64|ppc]\n
	[--delete-subnet network/mask]\n
	[--delete-network NIC]\n
	[--delete-dynamic IPaddr]\n
	[--mac | --uuid]\n
	[--only-lease]\n
	[--eloaderhack]\n
	[-o \"user customized options\"]\n
	\n", "I", "IMsgMakedhcpUsage"
	);
	exit($rc);
}

# get cmd line args
sub getArgs
{

	# load basic
	my @nodes;
	if (
		!GetOptions(
					'eloaderhack'           => \$::ELOADER,
					'n|new'                 => \$::NEW,
					'nd|nostage2|nodynamic' => \$::NODYNAMIC,
					'a|all|allmac'          => \$::ALL,
					'add-subnet'            => \$::ADDSUB,
					'add-network'           => \$::ADDNET,
					'add-dynamic'           => \$::ADDDY,
					'd|delete'              => \$::DELETE,
					'delete-subnet'         => \$::DELETESUB,
					'delete-network'        => \$::DELETENET,
					'delete-dynamic'        => \$::DELETEDY,
					'uuid'                  => \$::UUID,
					'mac'                   => \$::MAC,
					'h|help'                => \$::NEEDHELP,
					'v'                     => \$::VERBOSE,
					'V'                     => \$::VERBOSE,
					'only-lease'            => \$::ONLYLEASE,
					'o=s'                   => \$::OPTIONS,
					'no-dhcpd-restart'      => \$::NO_DHCPD_RESTART,
					'only-dhcpd-restart'    => \$::ONLY_DHCPD_RESTART,
				   )
	   )
	{
		&usage(1);
	}

	if ($::NEEDHELP) { &usage(0); }

	# verify arguments
	my $dt = $::DELETE + $::DELETESUB + $::DELETENET + $::DELETEDY + $::NEW +
	  $::ADDSUB + $::ADDNET + $::ADDDY;
	if ($dt > 1) { usage(1); }

	if ($::ONLY_DHCPD_RESTART)
	{
		&restartDHCP();
		exit(0);
	}

	# Handle attr=value arguments
	my @obj_argv;
	if (scalar(@ARGV) > 0)
	{
		foreach my $a (@ARGV)
		{

			# Note:  we allow a null value
			if (!$a) { next; }

			my ($attr, $value) = $a =~ /^\s*(\S+?)\s*=\s*(\S*.*)$/;

			if (!defined($attr))
			{
				push @obj_argv, $a;
				next;
			}
			my $found = 0;
			foreach my $va (@::VALID_ATTRS)
			{
				if ($attr =~ /\b$va\b/i)
				{
					$::ATTRS{$va} = $value;
					$found = 1;
					last;
				}
			}

			if (!$found)
			{
				msg("Invalid attribute : \"$attr\" \n",
					'E1', 'EMsgBAD_ATTRIBUTE', $attr);
			}
		}
	}

	# next arguments are objects such as nodes or subnets or networks, etc...
	return \@obj_argv;
}

sub getDomainName
{
	my ($dnsname, $tmpName);

	chomp($tmpName = NodeUtils->runcmd("$::DNSDOMAINNAME", -1));
	if (NetworkUtils->isIpaddr($tmpName))
	{

		# look for it in the /etc/resolv.conf file:
		my $cmd = "$::GREP search /etc/resolv.conf";
		unless (NodeUtils->runcmd("$cmd", -1))
		{
			$cmd .= " | $::AWK '{print \$2}' | $::HEAD -n 1";
			chomp($dnsname = NodeUtils->runcmd("$cmd", -1));
		}
	}
	else
	{
		$dnsname = $tmpName;
	}

	return $dnsname;
}

sub getNameServers
{
	my $nameservers = "";
	if ($::ATTRS{"Nameserver"})
	{
		return $::ATTRS{"Nameserver"};
	}
	elsif ($::ATTRS{"Nameservers"})
	{
		return $::ATTRS{"Nameservers"};
	}
	my $filename = "/etc/resolv.conf";
	if (open(RESOLV, "<$filename"))
	{
		while (<RESOLV>)
		{
			my $line = $_;
			chomp $line;

			if ($line =~ /^nameserver/i)
			{
				$nameservers .= "," if ($nameservers);
				$nameservers .= (split(' ', $line))[1];
			}
		}
		close(RESOLV);
		msg('Nameservers = \"$nameservers\".\n',
			'V', 'IMsgCDHCPNAMESRVVAL', $nameservers);
	}
	return $nameservers;
}

sub getNodeGateway
{
	my $node = shift;
	my ($gw, $gateway);

	if (!$node)
	{

		# get the gateway for dynamic ip
		$gateway = NetworkUtils->validate_ip($::ATTRS{"Gateway"});
		if ($gateway == -1)
		{
			$gateway = "0.0.0.0";
		}
		return $gateway;
	}

	# get the gateway for static node
	$gw      = $::NODEHASH{$node}{InstallAdapterGateway};
	$gateway = NetworkUtils->validate_ip($gw);
	if ($gateway == -1)
	{
		$gateway = NetworkUtils->validate_ip($::ATTRS{"Gateway"});
	}
	if ($gateway == -1)
	{
		$gateway = "0.0.0.0";
		msg(
			"Can not determine the gateway of the node \"$node\". Assuming 0.0.0.0. Please set node's InstallAdapterGateway attribute or use Gateway option if this does not work for you.\n",
			"V", "IMsgNoGateway", $node
		   );
	}

    my ($ip, $nodeMask);
    (undef, $ip, $nodeMask, undef) = get_node_network_attr($node);
    # If the node's gateway is in the different subnet than the node's ip addresses,
    # the gateway should not be in the dhcpd.conf because the gateway in different subnet
    # does not make sense.
    if (NetworkUtils->within_same_subnet($ip, $nodeMask, $gateway, $nodeMask) == $::NOK)
    {
        $gateway = "0.0.0.0";
    }

	return $gateway;
}

# get next-server of the node. This is used to identify the tftp server.
sub getNextServer
{
	my $node = shift;
	my $serverIP;

	if ($::CSM)
	{
		my $akbnode = $::NODEHASH{$node}{"InstallAdapterHostname"};
		if (!$akbnode)
		{
			$akbnode = $node;
		}
		$serverIP = NetworkUtils->get_source_ip_to_target($akbnode);
		chomp($serverIP);
	
		if ($::NODEHASH{$node}{"InstallServer"} eq "")
		{
			my (undef, $ManagementServerIP) =
				NetworkUtils->getHost($::NODEHASH{$node}{"ManagementServer"});
			if ($ManagementServerIP ne $serverIP)
			{
				msg ("For the node \"$node\", the value of the ManagementServer attribute does not match the IP address of the management server on the node's subnet (\"$serverIP\"). If the ManagementServer attribute is incorrect, change it with the chnode command.\n", "I", "IMsgManagementServerAttrNotconsistNode", $node, $serverIP);
				$serverIP = $ManagementServerIP;
			}
		}
	}
	else
	{

		# Following code are only used for xCAT.
		# If the node's IP address is in the subnet that can be
		# found in kernel routing table, then the corresponding interface's IP address is used.
		my $ip       = shift;
		my $nodeMask = shift;
		my ($nodeNet);

		my $bc       = xCATCSM->ipCalc("broadcast", $ip, $nodeMask);
		my $maskBits = xCATCSM->countbits($nodeMask);

		my @tmpIPs;
		unless (@tmpIPs = `ip addr`)
		{
			msg("Could not execute \'ip addr\' command.\n");
			exit(1);
		}

		# get the server IP that matches this nodes interface.
		foreach my $line (@tmpIPs)
		{
			if ($line =~ /inet .*\/$maskBits brd $bc/)
			{
				$line =~ s/\// /;
				$line = (split(/\s+/, $line))[2];
				$serverIP = $line;
				last;
			}
		}
	}
	return $serverIP;
}

# get the adapter name through which the target ip can be routed to.
sub getNetwork
{
	my $ip = shift;
	my ($adapterip, $network);

	if ($::CSM)
	{
		$adapterip = NetworkUtils->get_source_ip_to_target($ip);
		chomp($adapterip);

		if ($::PLTFRM eq "AIX")
		{
			my @output = NodeUtils->runcmd("$::IFCONFIG -lu", -1);
			my @ifs = split(' ', $output[0]);
			foreach my $if (@ifs)
			{
				next if ($if =~ /lo/);
				my @output =
				  NodeUtils->runcmd("$::IFCONFIG $if | $::GREP 'inet '", -1);
				foreach my $line (@output)
				{
					my @tabs = split(' ', $line);
					if ($tabs[1] eq $adapterip)
					{
						$network = $if;
						goto FOUND_NETWORK;
					}
				}
			}
		}
		else
		{

			# for Linux
			$network =
			  `$::IP addr | $::GREP 'inet ' | $::GREP $adapterip | $::AWK  '{print \$7}'`;
		}

	  FOUND_NETWORK:
		chomp($network);
		if (!$network)
		{
			msg(
				"Can not add $ip. Please make sure the target address can be reached.\n",
				"E1", "EMsgFailRouteToTarget", $ip
			   );
		}

	}
	else
	{
		chomp($network =
			`$::NETSTAT -rn | $::EGREP "^$ip\\b" | $::HEAD -n 1 | $::AWK '{print \$8}'`
		);
		if (!$network)
		{

			# the subnet to be added is not listed in kernel routing table. should
			# add it anyway, and add to default eth0 network.
			$network = "eth0";
		}
	}
	return $network;
}

# if NFSServer is set, return the NFS dirname, otherwise return null.
sub get_ns_dirname
{
	my $node = shift;

	my ($addr, $dir) = (undef, undef);

	my $ns = $::NODEHASH{$node}{"NFSServer"};
	my (undef, $ms_ip) =
	  NetworkUtils->getHost($::NODEHASH{$node}{"ManagementServer"});

	if ($ns)
	{
		($addr, $dir) = split(":", $ns);
		(undef, $addr) = NetworkUtils->getHost($addr);

		if ($addr eq $ms_ip)
		{
			$dir = $::CSMINSTDIR;
		}

		if (!$dir) { $dir = $::SERVER_DIRECTORY }
	}

	return $dir;
}

# get the root_path of the node.
sub get_node_root_path
{
	my $node = shift;
	my $rootpath;

	# only for CSM
	if (!$::CSM)
	{
		return;
	}
	my $distro = $::NODEHASH{$node}{"InstallDistributionName"};
	if ($distro =~ /SLES/)
	{
		my $version = $::NODEHASH{$node}{"InstallDistributionVersion"};
		my $arch    = $::NODEHASH{$node}{"InstallPkgArchitecture"};
		my $sl      = NodeUtils->getNodeServiceLevel($node);

		# get install dir.
		my $installdir = &get_ns_dirname($node);
		if (!$installdir)
		{

			# NFSServer attribute is not set.
			$installdir = "/csminstall";
			if (defined $ENV{'CSMINSTALL_ROOT'})
			{
				$installdir = $ENV{'CSMINSTALL_ROOT'};
			}
		}

		# special case for sles10
		if ($version eq '10')
		{
			$rootpath = "$installdir/Linux/$distro/$version/$arch/$sl/CD1";
		}
		else
		{
			$rootpath = "$installdir/Linux/$distro/$version/$arch/$sl";
		}
	}
	return $rootpath;
}

sub loadCSMVars
{

	# only for CSM
	if (!$::CSM)
	{
		return;
	}
	$::TFTPDIR  = "";
	$::DHCPCONF = $::DHCPDCONF;
	$::TDHCP    = $::DHCPCONF;
	$::DHCPVER  = "3";
	unless ($::DNSDOMAIN = &getDomainName())
	{
		msg("domain not detected.\n", "V", "IMsgNoDomain");
	}
	$::NAMESERVERS = &getNameServers();

	# do what makeNetworks and loadNetworks does in xCAT.
	my $cmd    = "$::NETSTAT -rn | $::EGREP \" U \" | $::GREP -v \'lo\$\'";
	my @output = NodeUtils->runcmd($cmd, -1);
	my $c      = 0;
	foreach my $line (@output)
	{
		my ($network, $gw, $mask);

		if ($::PLTFRM eq "AIX")
		{
			($network, $gw)   = split(/\s+/, $line);
			($network, $mask) = split("/",   $network);
			$mask = xCATCSM->bitscount($mask);
			my @net = split /\./, $network;
			my $i = scalar(@net);
			while ($i < 4)
			{
				$net[$i++] = "0";
			}
			$network = join('.', @net);
		}
		else
		{
			($network, $gw, $mask) = split(/\s+/, $line);
		}

		$::NETS[$c]  = $network;
		$::MASKS[$c] = $mask;
		$::GWS[$c]   = $gw;
		$c++;
	}

}

sub loadxCATVars
{

	# only for xCAT
	if ($::CSM)
	{
		return;
	}
	unless ($::TFTPXCATROOT = tabdb($::SITETAB, "tftpxcatroot", 1))
	{
		msg "$::MYNAME: tftpxcatroot not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::SERIALBPS = tabdb($::SITETAB, "serialbps", 1))
	{
		msg "$::MYNAME: serialbps not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::SERIALMAC = tabdb($::SITETAB, "serialmac", 1))
	{
		$::SERIALMAC = 0;
	}
	unless ($::MASTER = tabdb($::SITETAB, "master", 1))
	{
		msg "$::MYNAME: master not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::DNSDOMAIN = tabdb($::SITETAB, "domain", 1))
	{
		msg "$::MYNAME: domain not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::NISDOMAIN = tabdb($::SITETAB, "nisdomain", 1))
	{
		msg "$::MYNAME: nisdomain not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::TFTPDIR = tabdb($::SITETAB, "tftpdir", 1))
	{
		msg "$::MYNAME: tftpdir not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::OFFUTC = tabdb($::SITETAB, "offutc", 1))
	{
		msg "$::MYNAME: offutc not defined in $::SITETAB\n";
		exit 1;
	}
	unless ($::DHCPCONF = tabdb($::SITETAB, "dhcpconf", 1))
	{
		msg "$::MYNAME: dhcpconf not defined in $::SITETAB\n";
		exit 1;
	}
	$::TDHCP = $::DHCPCONF;

	if ($::DHCPVER = tabdb($::SITETAB, "dhcpver", 1))
	{
		unless ($::DHCPVER =~ /2|3/)
		{
			msg
			  "$::MYNAME: dhcp version $::DHCPVER is not supported.  Only 2 and 3 are supported\n";
			exit 1;
		}
	}
	else
	{
		msg "$::MYNAME: dhcpconf not defined in $::SITETAB\n";
		exit 1;
	}

	if ($::NAMESERVERS = tabdb($::SITETAB, "nameservers", 1))
	{
		chomp($::NAMESERVERS =
			  `egrep "^nameservers\b" $::SITETAB | awk '{print \$2}'`);
	}
	else
	{
		msg "$::MYNAME: nameservers not defined in $::SITETAB\n";
		exit 1;
	}

	# optional subdhcp.conf files
	if ($::SUBDHCP = tabdb($::SITETAB, "subdhcpd", 1))
	{
		chomp(@::SUBDHCP = `egrep "^subdhcpd" $::SITETAB | awk '{print \$2}'`);
	}
}

sub loadVars
{
	if ($::XCAT)
	{
		&loadxCATVars();

	}
	elsif ($::CSM)
	{
		&loadCSMVars();
	}
}

# write what's cached in memory back to DHCP config file, and restart dhcp server if
# the global flag has been turned on.
sub finishDHCP
{
	&del_empty_network();
	
	open(FILE, ">$::TDHCP")
	  or msg("Can not open $::TDHCP", "E1", "ECannotOpenDHCPConfig", $::TDHCP);
	foreach (@::DHCP)
	{
		print FILE $_;
	}
	close(FILE);
	$::CONTENT_SYNCED = 1;

	if ($::RESTART_DHCP)
	{
		&restartDHCP();
		$::RESTART_DHCP = 0;
	}
}

sub makeNetworks
{

	# only for xCAT
	if ($::XCAT)
	{
		system("$::XCATROOT/sbin/makenetworks");
	}
}

sub loadNetworks
{

	# only for xCAT
	if ($::XCAT)
	{
		@::NETS, @::MASKS, @::GWS, @::DNSS;
		open(FILE, "$::XCATROOT/etc/networks.tab")
		  or die "$::MYNAME can not read $::XCATROOT/etc/networks.tab";
		my $c = 0;
		my @j = ();
		while (my $line = <FILE>)
		{
			if ($line =~ /^[0-9]/)
			{
				my @junk = ();
				($::NETS[$c], @junk) = split(/\s+/, $line);
				($::MASKS[$c], $::GWS[$c], @j) = split(',', join(',', @junk));
				$::DNSS[$c] = join(',', @j);
				@j = ();
				$c++;
			}
		}
		close(FILE);
	}
}

# read node attributes into %::NODEHASH
sub read_node_attributes
{

	#only for CSM
	if (!$::CSM)
	{
		return;
	}

	my ($ref_nodelist) = @_;
	my @nodelist = @$ref_nodelist;

	my ($attr, $value);

	my $csmdefault = "/csminstall";
	if (defined $ENV{'CSMINSTALL_ROOT'})
	{
		$csmdefault = $ENV{'CSMINSTALL_ROOT'};
	}
	my $infopath = "$csmdefault/csm/config";    #get from where?

	foreach my $node (@nodelist)
	{
		my $cfg_file = "$infopath/$node.config_info";

		unless (-f $cfg_file)
		{
			msg("No config_info file for node $node\n",
				"E1", "EMsgNoNodeConfigInfo", $node);
		}

		unless (open(CFGINFO, "<$cfg_file"))
		{
			msg("Can not read config_info file $cfg_file",
				"E1", "EMsgCannotReadConfigInfo", $cfg_file);
		}

		while (<CFGINFO>)
		{
			($attr, $value) = split('=');
			chomp($attr);
			chomp($value);
			if ($attr eq "InstallAdapterMacaddr")
			{
				$::NODEHASH{$node}{"InstallAdapterMacaddr"} = $value;
			}
			if ($attr eq "UUID")
			{
				$::NODEHASH{$node}{"UUID"} = $value;
			}
			if ($attr eq "InstallAdapterGateway")
			{
				$::NODEHASH{$node}{"InstallAdapterGateway"} = $value;
			}
			if ($attr eq "InstallAdapterNetmask")
			{
				$::NODEHASH{$node}{"InstallAdapterNetmask"} = $value;
			}
			if ($attr eq "InstallAdapterName")
			{
				$::NODEHASH{$node}{"InstallAdapterName"} = $value;
			}
			if ($attr eq "InstallAdapterHostname")
			{
				$::NODEHASH{$node}{"InstallAdapterHostname"} = $value;
			}
			if ($attr eq "InstallDistributionName")
			{
				$::NODEHASH{$node}{"InstallDistributionName"} = $value;
			}
			if ($attr eq "InstallDistributionVersion")
			{
				$::NODEHASH{$node}{"InstallDistributionVersion"} = $value;
			}
			if ($attr eq "InstallPkgArchitecture")
			{
				$::NODEHASH{$node}{"InstallPkgArchitecture"} = $value;
			}
			if ($attr eq "InstallServiceLevel")
			{
				$::NODEHASH{$node}{"InstallServiceLevel"} = $value;
			}
			if ($attr eq "InstallServerAKBNode")
			{
				$::NODEHASH{$node}{"InstallServerAKBNode"} = $value;
			}
			if ($attr eq "InstallServer")
			{
				$::NODEHASH{$node}{"InstallServer"} = $value;
			}
			if ($attr eq "ManagementServer")
			{
				$::NODEHASH{$node}{"ManagementServer"} = $value;
			}
			if ($attr eq "NFSServer")
			{
				$::NODEHASH{$node}{"NFSServer"} = $value;
			}
			if ($attr eq "InstallMethod")
			{
				$::NODEHASH{$node}{"InstallMethod"} = $value;
			}
		}

		close(CFGINFO);
	}
}

=item bldUUIDStr
   Format UUID string
=cut

sub bldUUIDStr
{
	my ($uuid) = @_;

	my $lcuuid = lc($uuid);
	my $ret;

	for (my $i = 0 ; $i < length($lcuuid) ; $i += 2)
	{
		my $tmpstr = unpack("x$i A2", $lcuuid);
		$ret .= $tmpstr;
		$ret .= ":";
	}

	# get rid of last ':'
	chop($ret);

	return $ret;
}

sub getMacs
{
	if ($::XCAT)
	{
		my $macs;
		my ($n, $mac);

		# read in mac tab, then make a hash
		unless (-f "$::MACTAB")
		{
			die "$::MYNAME: $::MACTAB does not exist\n";
		}

		open(FILE, "$::MACTAB") or die "$::MYNAME: can't read $::MACTAB";
		foreach my $line (<FILE>)
		{
			if ($line =~ /^[A-Za-z]+/)
			{
				($n, $mac) = split(/\s+/, $line);
				$macs->{$n} = $mac;
			}
		}
		close(FILE);
		$::MACS = $macs;

	}
	else
	{
		my ($ref_nodes) = @_;
		my $macs;
		my ($n, $mac);

		foreach my $node (keys %::NODEHASH)
		{
			$mac = $::NODEHASH{$node}{'InstallAdapterMacaddr'};
			$macs->{$node} = $mac;
		}
		$::MACS = $macs;
	}
}

sub getUUIDs
{
	if ($::CSM)
	{
		my $uuids;
		my ($n, $uuid);

		foreach my $node (keys %::NODEHASH)
		{
			$uuid = $::NODEHASH{$node}{'UUID'};
			$uuids->{$node} = $uuid;
		}
		$::UUIDS = $uuids;
	}
	else
	{

		# for xCAT uuid support, add in the future.
	}
}

# collect node's hostname, IP address, netmask and the network it belongs to.
# returns a hash of all these attributes as key.
sub get_node_network_attr
{
	my $node = shift;
	my ($ret_host, $ip, $mask, $net);

	my $akbnode = $::NODEHASH{$node}{"InstallAdapterHostname"};
	if (!$akbnode)
	{
		$akbnode = $node;
	}
	if ($ip = gethostbyname($akbnode))
	{
		$ret_host = $akbnode;
	}
	else
	{
		msg("No IP defined for node $akbnode\n",
			"E1", "EMsgNoIPforNode", $akbnode);
	}

	$ip = inet_ntoa($ip);

	# get the subnet of the node
	if ($::CSM)
	{
		$mask = $::NODEHASH{$node}{"InstallAdapterNetmask"};
		$net = xCATCSM->ipCalc("network", $ip, $mask);
	}
	if (!$mask || $::XCAT)
	{
		foreach my $i (0 .. $#::NETS)
		{
			my $testNet = xCATCSM->ipCalc("network", $ip, $::MASKS[$i]);
			if ($testNet eq $::NETS[$i])
			{
				$net  = $::NETS[$i];
				$mask = $::MASKS[$i];
				last;
			}
		}
	}
	unless ($mask)
	{
		msg(
			"The mask of node $akbnode is not found. It will not be added into DHCP config file.\n",
			"E1", "EMsgMissingNodeMask", $akbnode
		   );
	}
	my @return_array = ($ret_host, $ip, $mask, $net);
	return (@return_array);
}

# get the boot image name that would be passed to node thru tftp.
sub get_filename
{
	my $node = shift;
	my $filename;
	my ($arch, $distro, $version, $svclevel, $akbnode, $install_method);

	if ($::ONLYLEASE)
	{

		# only to setup DHCP to assign leases, so don't pass
		# booting image to client.
		return;
	}

	# xCAT doesn't support pSeries.
	if (($::XCAT) && ($::IS_ARCH eq "ppc64"))
	{
		return;
	}

	# generate filename for dynamic node range.
	if (!$node)
	{
		if ($::IS_ARCH ne "ppc64")
		{
			$filename = "$::TFTPDIR/pxelinux.0";
		}

		#on pSeries IS and doing dynamic, not filename is needed.
		return $filename;
	}

	# generate filename for static node.
	$arch           = $::NODEHASH{$node}{"InstallPkgArchitecture"};
	$distro         = $::NODEHASH{$node}{"InstallDistributionName"};
	$version        = $::NODEHASH{$node}{"InstallDistributionVersion"};
	$svclevel       = $::NODEHASH{$node}{"InstallServiceLevel"};
	$akbnode        = $::NODEHASH{$node}{"InstallAdapterHostname"};
	$install_method = $::NODEHASH{$node}{"InstallMethod"};
	if ($arch eq "ppc64")
	{
		my $server_ip;

		if (!$akbnode)
		{
			$akbnode = $node;
		}
		$server_ip = NetworkUtils->get_source_ip_to_target($akbnode);
		chomp($server_ip);
		
		if ($::NODEHASH{$node}{"InstallServer"} eq "")
		{
			my (undef, $ManagementServerIP) =
				NetworkUtils->getHost($::NODEHASH{$node}{"ManagementServer"});
			if ($ManagementServerIP ne $server_ip)
			{
				$server_ip = $ManagementServerIP;
			}
		}

		if ($install_method eq "warewulf")
		{
			$filename =
			  "$::TFTPDIR/csm/ww-zimage-$distro$version-$svclevel-$arch";
		}
		elsif ($distro =~ /RedHat/)
		{
			$filename =
			  "$::TFTPDIR/csm/ks$version-$svclevel-$arch-zImage.initrd_$server_ip";
		}
		else
		{
			$filename =
			  "$::TFTPDIR/csm/yast$version-$svclevel-$arch-zImage.initrd_$server_ip";
		}
	}
	else
	{
		$filename = "$::TFTPDIR/pxelinux.0";
	}

	return $filename;
}

# get the pxelinux.configfile option keyword to be used by getmacs.
sub get_configfile
{
	my $cfgfile;

	if ($::ONLYLEASE)
	{

		# only to setup DHCP to assign leases, so don't
		# need the getmacs tag.
		return;
	}

	# CSM doesn't run getmacs on pSeries IS,
	# and xCAT doesn't need this tag.
	if (($::CSM) && ($::IS_ARCH ne "ppc64"))
	{
		$cfgfile = "$::TFTPDIR/pxelinux.cfg/getmacs.default";
	}

	return $cfgfile;
}

sub newConfig
{

	# makedhcp should not generate a new config file when called with the delete flags.
	if ($::DELETE || $::DELETESUB || $::DELETENET || $::DELETEDY)
	{
		return;
	}

	my @content;
	if (-f "$::TDHCP")
	{

		# read in dhcp config file to check whether the format can be recognized.
		open(FILE, "$::TDHCP") or die "$::MYNAME: Can't open $::TDHCP";
		while (<FILE>)
		{
			push @content, $_;
		}
		close(FILE);

		if (grep /IBM CSM makedhcp/, @content)
		{

			# don't need to recreate if it exists and has the right format.
			msg(
				"The existing $::DHCPDCONF file looks in the correct format. Don't create it again.\n",
				"I", "IMsgNotCreateDHCPConfig", $::DHCPDCONF
			   );
			return;
		}

		if (grep /CSM VERSION 1\.3\.3\.0/, @content)
		{

			# migrating from prior CSM versions.
			msg(undef, "I", "IMsgMigratingDHCP");

			# save the old dhcpd.conf file
			my $backTDHCP = $::TDHCP . ".precsm1.5";
			msg("Saving original $::TDHCP as $backTDHCP\n",
				"I", "IMsgSaveOldDHCPConfig", $::TDHCP, $backTDHCP);
			system("$::MOVE -f $::TDHCP $backTDHCP");

			# save the old leases file.
			my ($lease_file, $backup_lease_file);
			if ($::IS_DISTRO =~ /SLES/)
			{
				$lease_file = "/var/lib/dhcp/db/dhcpd.leases";
			}
			else
			{
				$lease_file = "/var/lib/dhcp/dhcpd.leases";
			}
			$backup_lease_file = $lease_file . ".precsm1.5";

			msg("Saving original $lease_file as $backup_lease_file\n",
				"I", "IMsgSaveOldDHCPConfig", $lease_file, $backup_lease_file);
			system("$::COPY -f $lease_file $backup_lease_file");

		}
		else
		{

			# first time CSM or xCAT users.
			my $backTDHCP;
			if ($::CSM)
			{
				$backTDHCP = $::TDHCP . ".precsm";
			}
			else
			{
				$backTDHCP = $::TDHCP . ".SAVE";
			}
			msg("Saving original $::TDHCP as $backTDHCP\n",
				"I", "IMsgSaveOldDHCPConfig", $::TDHCP, $backTDHCP);
			system("$::MOVE -f $::TDHCP $backTDHCP");
		}
	}
	if ($::XCAT)
	{
		`echo "#Automatically generated by xCAT $::VERSION\n" >$::TDHCP`;
	}
	else
	{
		`echo "#Automatically generated by IBM CSM makedhcp\n" >$::TDHCP`;
	}
	if ($::DHCPVER eq 3)
	{
		if ($::PLTFRM eq "Linux")
		{
			`echo "option space pxelinux\;
option pxelinux.magic code 208 = string\;
option pxelinux.configfile code 209 = text\;
option pxelinux.reboottime code 211 = unsigned integer 32\;
option client-guid code 97 = { unsigned integer 8, string }\;

authoritative\;
ddns-update-style none\;

option option-128 code 128 = string\;
option option-150 code 150 = string\;
option option-160 code 160 = string\;
option option-192 code 192 = string\;
option option-193 code 193 = string\;
option option-194 code 194 = string\;
option option-195 code 195 = string\;
">>$::TDHCP`;
		}
		else
		{

			# for AIX, use a different header
			`echo "leaseTimeDefault 0xffffffff
leaseExpireInterval 1 year
supportUnlistedClients yes
supportBOOTP yes
">>$::TDHCP`;
		}
	}
}

sub addNetwork
{
	my $nic = shift;
	unless (grep /} #$nic/, @::DHCP)
	{
		if ($nic eq "all")
		{
			push @::DHCP, "#shared-network $nic {\n\n";
			push @::DHCP, "#} #$nic network_end#\n\n";
		}
		else
		{
			push @::DHCP, "shared-network $nic {\n\n";
			push @::DHCP, "} #$nic network_end#\n\n";
		}
		msg("added network $nic\n", "I", "IMsgAddedNetwork", $nic);
		$::RESTART_DHCP = 1;
	}
}

sub delNetwork
{
	my $killTarget     = shift;
	my $erase          = 0;
	my $did_erase      = 0;
	my $remove_newline = 0;
	my @content;
	foreach my $line (@::DHCP)
	{
		if ($remove_newline && ($line =~ /^\n$/))
		{
			$remove_newline = 0;
			next;
		}
		if ($erase)
		{
			if ($line =~ /(#$killTarget network_end#\n)/)
			{
				$erase          = 0;
				$remove_newline = 1;
			}
			next;
		}
		if ($line =~ /#?shared-network $killTarget/)
		{
			$erase     = 1;
			$did_erase = 1;
			next;
		}
		push @content, $line;
	}
	if ($did_erase)
	{
		msg("removed network $killTarget\n",
			"I", "IMsgRemovedNetwork", $killTarget);
		$::RESTART_DHCP = 1;
	}
	else
	{
		msg("network $killTarget not removed!\n",
			"I", "IMsgNotRemoveNetwork", $killTarget);
	}
	@::DHCP = @content;
}

sub addSubnet
{
	my $net         = shift;
	my $mask        = shift;
	my $gw          = shift;
	my $ip          = shift;
	my $nic         = shift;            # for all
	my $write_opt   = shift;
	my $nameServers = $::NAMESERVERS;

	my $net = xCATCSM->ipCalc("network", $net, $mask);

	# check to see if the subnet is in there:
	if (grep /\} #$net\/$mask subnet_end#/, @::DHCP)
	{
		return;
	}

	my @ips;
	if ($::PLTFRM eq "AIX")
	{
		chomp(@ips =
			`$::IFCONFIG -a | $::GREP 'inet ' | $::GREP -v 'inet6'| $::AWK '{print \$2}'`
		);
	}
	else
	{
		chomp(@ips =
			`$::IP addr | $::GREP 'inet ' | $::AWK '{print \$2}' | $::AWK -F/ '{print \$1}'`
		);
	}
	foreach my $i (@ips)
	{
		$nameServers =~ s/$i/$ip/;
	}

	if ($::XCAT)
	{
		if (my $ns = &tabdb($::NETWORKSTAB, $net, 3))
		{
			$nameServers = $ns;
		}
	}
	else
	{
		if (!$nameServers)
		{
			$nameServers = "NA";
		}
	}

	my @header;
	if ($::PLTFRM eq "AIX")
	{
		push @header, "network $net $mask {\n";
		push @header, "\tsubnet $net {\n";
		push @header, "\n\t\toption 1\t$mask\t\t# Netmask\n";

		if ($gw)
		{
			push @header, "\t\toption 3\t$gw\t\t# Gateway\n";
		}
		if ($nameServers ne '')
		{
			my @ns = split (',', $nameServers);
			$nameServers = join (' ', @ns);
			push @header, "\t\toption 6\t$nameServers\t\t# DNS server\n";
		}
		if ($::DNSDOMAIN && ($::DNSDOMAIN !~ /NA/))
		{
			push @header,
			  "\t\toption 15\t\"$::DNSDOMAIN\"\t\t\# DNS domain name\n";
		}

		push @header, "\n\t\t###CLASS ENTRIES FOR SUBNET $net/$mask\n";
		push @header, "\t\t###END CLASS ENTRIES\n\n";
		push @header, "\t} #$net\/$mask subnet_end#\n\n";
		push @header, "} #$net\/$mask network_end#\n\n";

	}
	else    # on Linux
	{
		push @header, "\tsubnet $net netmask $mask {\n";
		push @header, "\n\t\tmax-lease-time\t\t\t43200;\n";
		push @header, "\t\tdefault-lease-time\t\t43200;\n";
		if ($gw)
		{
			push @header, "\t\toption routers\t\t\t$gw;\n";
		}
		if ($mask)
		{
			push @header, "\t\toption subnet-mask\t\t$mask;\n";
		}

		#if($::NISDOMAIN && ($::NISDOMAIN !~ /NA/)){
		if ($::NISDOMAIN)
		{
			push @header, "\t\toption nis-domain\t\t\"$::NISDOMAIN\";\n";
		}
		if ($::DNSDOMAIN && ($::DNSDOMAIN !~ /NA/))
		{
			push @header, "\t\toption domain-name\t\t\"$::DNSDOMAIN\";\n";
		}
		if ($nameServers ne '')
		{
			push @header, "\t\toption domain-name-servers\t$nameServers;\n";
		}
		if (($::OFFUTC ne 'NA') && ($::OFFUTC ne ''))
		{
			push @header, "\t\toption time-offset\t\t$::OFFUTC;\n";
		}

		# add the user customized options
		if ($write_opt)
		{
			push @header, "\n\t\t###USER DEFINED OPTIONS\n";
			my @cust_opt = split(';', $::OPTIONS);
			foreach my $option (@cust_opt)
			{

				# get rid of the preceding spaces.
				$option =~ s/^\s//;
				push @header, "\t\t$option;\n";
			}
			push @header, "\t\t###END USER DEFINED OPTIONS\n";
		}

		push @header, "\n\t\t###CLASS ENTRIES FOR SUBNET $net/$mask\n";
		push @header, "\t\t###END CLASS ENTRIES\n\n";
		push @header, "\t} #$net\/$mask subnet_end#\n\n";
	}

	# now we need to put this into @::DHCP
	my $did_add = 0;
	if ($::PLTFRM eq "AIX")
	{

		# for AIX, don't have network stanza. Put in subnet stanza directly.
		# append it at the end.
		$did_add = 1;
		push @::DHCP, @header;
	}
	else
	{
		my @newDhcp;
		foreach my $line (@::DHCP)
		{
			if (($line !~ /^\s*#/) && ($line =~ /(} #$nic network_end#)/))
			{
				push @newDhcp, @header;
				$did_add = 1;
			}
			push @newDhcp, $line;
		}
		@::DHCP = @newDhcp;
	}
	if ($did_add)
	{
		my $addsub = $net . "/" . $mask;
		msg("added subnet $addsub\n", "I", "IMsgAddedSubnet", $addsub);
		$::RESTART_DHCP = 1;
	}
}

sub delSubnet
{
	my $killSub        = shift;
	my $killMask       = shift;
	my $erase          = 0;
	my $did_erase      = 0;
	my $remove_newline = 0;
	my @content;

	my $killSub = xCATCSM->ipCalc("network", $killSub, $killMask);
	foreach my $line (@::DHCP)
	{
		if ($remove_newline && ($line =~ /^\n$/))
		{
			$remove_newline = 0;
			next;
		}
		if ($erase)
		{
			if (
				(
				    ($::PLTFRM eq "Linux")
				 && ($line =~ /(} #$killSub\/$killMask subnet_end#\n)/)
				)
				|| (   ($::PLTFRM eq "AIX")
					&& ($line =~ /(} #$killSub\/$killMask network_end#\n)/))
			   )
			{
				$erase          = 0;
				$remove_newline = 1;
			}
			next;
		}
		if (
			(
			    ($::PLTFRM eq "Linux")
			 && ($line =~ /subnet $killSub netmask $killMask/)
			)
			|| (($::PLTFRM eq "AIX") && ($line =~ /network $killSub $killMask/))
		   )
		{
			$erase     = 1;
			$did_erase = 1;
			next;
		}
		push @content, $line;
	}
	my $delsub = $killSub . "/" . $killMask;
	if ($did_erase)
	{
		msg("removed subnet $delsub\n", "I", "IMsgRemovedSubnet", $delsub);
		$::RESTART_DHCP = 1;
	}
	else
	{
		msg("subnet $delsub not removed!\n",
			"I", "IMsgNotRemoveSubnet", $delsub);
	}
	@::DHCP = @content;
}

sub del_empty_network
{
	my ($maybe_erase, $have_valid_line, $cur_net_name);
	my (@content, @tmp_net_share);
	
	foreach my $line (@::DHCP)
	{
		if ($maybe_erase)
		{
			push @tmp_net_share, $line;

			#If this is a invalid line?
			if ($line !~ /^\s*$/ && $line !~ /\s*#+/)
			{
				$have_valid_line = 1;
			}
			if ($line =~ /(#$cur_net_name network_end#\n)/)
			{
				#At the end of the current shared-network
				$maybe_erase = 0;
				if ($have_valid_line)
				{
					#Do not remove it
					foreach my $net_line (@tmp_net_share)
					{
						push @content,  $net_line;
					}
				}
				else
				{
					$::RESTART_DHCP = 1;
				}
			}
			next;
		}
		if ($line =~ /#?shared-network (\w*)/)
		{
			#Enter a shared-network
			$cur_net_name = $1;
			$maybe_erase = 1;
			@tmp_net_share = ();
			push @tmp_net_share, $line;
			$have_valid_line = 0;
			next;
		}
		push @content, $line;
	}
	
	@::DHCP = @content;
	
}


sub addDynamic
{
	my $nic        = shift;
	my $arch       = shift;
	my $serverIP   = shift;
	my $mask       = shift;
	my $beginRange = shift;
	my $endRange   = shift;
	my $write_opt  = shift;
	if ($nic eq 'all') { $nic = "eth0" }

	if (grep /#$beginRange dynamic_start#/, @::DHCP)
	{
		return 1;
	}

	unless (grep /^#?shared-network $nic/, @::DHCP)
	{
		msg("$::MYNAME: addDynamic: no $nic defined in $::TDHCP",
			"E", "EMsgMissingNIC", $nic, $::TDHCP);
		return 1;
	}

	system("$::IFCONFIG | egrep \"^$nic\"  >/dev/null 2>&1");
	my $rc = $? >> 8;
	if ($rc)
	{
		msg("$::MYNAME: addDynamic: no active $nic found on the system",
			"E", "EMsgNoActiveNIC", $nic);
		return 1;
	}
	if ($::XCAT)
	{
		system(
			  "$::IFCONFIG -a | egrep \"inet addr:$serverIP\" >/dev/null 2>&1");
		$rc = $? >> 8;
		if ($rc)
		{

			# need to add the aliased NIC
			my $os = xCATCSM->osver();
			if ($os =~ /rh/) { $os = "rh" }
			if (-f "$::XCATROOT/lib/addif.$os")
			{
				system("addif.$os $nic $arch $serverIP $mask");
			}
			else
			{
				die "$::MYNAME: $::XCATROOT/lib/addif.$os was not found";
			}
		}
	}
	my $inet = xCATCSM->ipCalc("network", $beginRange, $mask);
	&addSubnet($inet, $mask, undef, $serverIP, $nic);
	my @dy;
	push @dy, "\t\t#$beginRange dynamic_start#\n";
	push @dy, "\t\tpool {\n";
	push @dy, "\t\t\trange $beginRange $endRange;\n";

	if ($arch =~ /ia32|x86|ppc/)
	{
		my $filename = get_filename();
		if ($filename)
		{
			push @dy, "\t\t\tfilename \"$filename\";\n";
		}
		push @dy, "\t\t\tnext-server $serverIP;\n";
		my $gateway = getNodeGateway();
		push @dy, "\t\t\toption routers $gateway;\n";
		push @dy, "\t\t\toption dhcp-server-identifier $serverIP;\n";
		push @dy, "\t\t\toption option-128 E4:45:74:68:00:00;\n";
		push @dy, "\t\t\toption option-160 \"timeout=5:default=195\";\n";

		if ($::CSM)
		{
			push @dy, "\t\t\tif exists dhcp-parameter-request-list {\n";
			push @dy,
			  "\t\t\t\tappend dhcp-parameter-request-list 208,209,210,211;\n";
			push @dy, "\t\t\t}\n";
			my $cfgfile = &get_configfile();
			if ($cfgfile)
			{
				push @dy, "\t\t\tsite-option-space \"pxelinux\";\n";
				push @dy, "\t\t\toption pxelinux.magic f1:00:74:7e;\n";
				push @dy, "\t\t\toption pxelinux.configfile \"$cfgfile\";\n";
				push @dy, "\t\t\toption pxelinux.reboottime 30;\n";
			}
		}
		if ($::XCAT)
		{
			push @dy,
			  "\t\t\toption option-193 \"eepro100:::$::TFTPDIR/$::TFTPXCATROOT/stage.nbi:::console=ttyS$::SERIALMAC,$::SERIALBPS stage2 eepro100\";\n";
			push @dy,
			  "\t\t\toption option-194 \"pcnet32:::$::TFTPDIR/$::TFTPXCATROOT/stage.nbi:::console=ttyS$::SERIALMAC,$::SERIALBPS stage2 pcnet32\";\n";
			push @dy,
			  "\t\t\toption option-195 \"autodetect:::$::TFTPDIR/$::TFTPXCATROOT/stage.nbi:::console=ttyS$::SERIALMAC,$::SERIALBPS stage2 eb\";\n";
		}

	}
	elsif ($arch =~ /ia64/)
	{
		push @dy, "\t\t\tfilename \"$::TFTPDIR/elilo.efi\";\n";
		push @dy, "\t\t\tnext-server $serverIP;\n";

	}
	else
	{
		die "$::MYNAME: addDynamic: $arch is not supported";
	}

	# add the user customized options
	if ($write_opt)
	{
		push @dy, "\n\t\t\t###USER DEFINED OPTIONS\n";
		my @cust_opt = split(';', $::OPTIONS);
		foreach my $option (@cust_opt)
		{
			$option =~ s/^\s//;
			push @dy, "\t\t\t$option;\n";
		}
		push @dy, "\t\t\t###END USER DEFINED OPTIONS\n";
	}

	push @dy, "\t\t}\n";
	push @dy, "\t\t#$beginRange dynamic_end#\n\n";
	my @newD;
	my $did_add = 0;

	foreach my $line (@::DHCP)
	{
		if (($line !~ /^\s*#/) && ($line =~ /(} #$inet\/$mask subnet_end#)/))
		{
			push @newD, @dy;
			push @newD, $line;
			$did_add = 1;
			next;
		}
		push @newD, $line;
	}
	@::DHCP = @newD;
	if ($did_add)
	{
		my $dynamic = $beginRange;
		if ($endRange)
		{
			$dynamic .= "-$endRange";
		}
		msg("adding dynamic $dynamic\n", "I", "IMsgAddedDynNIC", $dynamic);
		$::RESTART_DHCP = 1;
	}
}

sub delDynamic
{
	my $killDy = shift;

	my $erase          = 0;
	my $did_erase      = 0;
	my $remove_newline = 0;
	my @content;
	foreach my $line (@::DHCP)
	{
		if ($remove_newline && $line =~ /^\n$/)
		{

			# remove an additional empty line
			$remove_newline = 0;
			next;
		}
		if ($erase)
		{
			if ($line =~ /(#$killDy dynamic_end#\n)/)
			{
				$erase          = 0;
				$remove_newline = 1;
			}
			next;
		}
		if ($line =~ /#$killDy dynamic_start/)
		{
			$erase     = 1;
			$did_erase = 1;
			next;
		}
		push @content, $line;
	}
	if ($did_erase)
	{
		msg("removed dynamic $killDy\n", "I", "IMsgRemovedDynNIC", $killDy);
		$::RESTART_DHCP = 1;
	}
	else
	{
		msg("dynamic $killDy is not removed.\n",
			"I", "IMsgNotRemoveDynNIC", $killDy);
	}
	@::DHCP = @content;
}

sub addNode
{
	if ($::UUID)
	{
		addNode_UUID(@_);
	}
	else
	{
		addNode_MAC(@_);
	}
}

sub addNode_MAC
{
	my $node      = shift;
	my $write_opt = shift;
	my @ne;
	my ($host, $ip, $nodeMask, $nodeNet, $mac);

	$mac = $::MACS->{$node};
	if (!$mac)
	{
		msg(
			"Can not add static node \"$node\" using --mac method when its InstallAdapterMacaddr attribute is null.\n",
			"E", "EMsgMissingInstAdptorMac", $node
		   );
		return;
	}

	if ($mac eq "NO_MAC")
	{
		$mac = "00:00:00:00:00:00";
	}

	($host, $ip, $nodeMask, $nodeNet) = get_node_network_attr($node);

	my $serverIP = getNextServer($node, $ip, $nodeMask);
	my $gateway;

	if ($::PLTFRM eq "AIX")
	{
		my @macs = split(':', $mac);
		$mac = join('', @macs);

		push @ne, "\t\tclient 1 $mac $ip { # host $host\n";
		push @ne, "\t\t\toption 12 \"$host\"\t# hostname for the client\n";
		push @ne, "\t\t\toption 66 $serverIP\t# the TFTP server name\n";
		my $filename = get_filename($host);
		if ($filename)
		{
			$filename = "/tftpboot" . $filename;
			push @ne, "\t\t\toption 67 \"$filename\"\t# the bootfile name\n";
		}
		my $rootpath = get_node_root_path($node);
		if ($rootpath)
		{
			push @ne,
			  "\t\t\toption 17 \"$rootpath\"\t# the default root path\n";
		}

		# add the gateway for the node.
		$gateway = getNodeGateway($node);
		if ($gateway)
		{
			push @ne, "\t\t\toption 3 $gateway\t# gateway\n";
		}

		push @ne, "\t\t}\n";
	}
	else
	{

		push @ne, "\t\thost $host {\n";

		my ($res, $serialbps, $serial, $tftpServer, $instdir, $netboot, $reth0,
			$eth0);
		if ($::XCAT)
		{
			$res        = whatismyres($node);
			$serialbps  = $::DB->{$node}{'nodehm_serialbps'};
			$serial     = $::DB->{$res}{'noderes_serial'};
			$tftpServer = $::DB->{$res}{'noderes_tftp'};
			$instdir    = $::DB->{$res}{'noderes_install_dir'};
			$netboot    = $::DB->{$node}{'nodehm_netboot'};
			$reth0      = $::DB->{$node}{'nodehm_eth0'};
			$eth0       = "eb";
		}
		else
		{
			$tftpServer = $serverIP;
			$instdir    = "/csminstall";
			$netboot    = "pxe";
			$reth0      = "";
			$eth0       = "eb";
			$netboot    = "pxe";
		}

		# now make the node entry
		push @ne, "\t\t\thardware ethernet $mac;\n";
		push @ne, "\t\t\tfixed-address $ip;\n";
		if ($netboot eq "pxe")
		{
			my $filename = get_filename($node);
			if ($filename)
			{
				push @ne, "\t\t\tfilename \"$filename\";\n";
			}
			if ($serverIP)
			{
				push @ne, "\t\t\toption dhcp-server-identifier $serverIP;\n";
			}
		}
		elsif ($netboot eq "nbgrub")
		{
			msg "NOT ADDED YET\n";
			exit;
		}
		elsif ($netboot eq "chrp")
		{
			msg "NOT ADDED YET\n";
			exit;

		}
		elsif ($netboot eq "elilo")
		{
			msg "NOT ADDED YET\n";
			exit;

		}
		elsif ($netboot eq "elilo-3.2")
		{
			msg "NOT ADDED YET\n";
			exit;

		}
		elsif ($netboot eq "eb")
		{
			msg "NOT ADDED YET\n";
			exit;

		}
		elsif ($netboot =~ /ks/)
		{
			msg "NOT ADDED YET\n";
			exit;

		}
		elsif ($netboot =~ /file:/)
		{
			my $f = (split(/=/, $netboot))[1];
			push @ne, "\t\t\tfilename \"$::TFTPDIR/$f\";\n";
		}

		# add root-path for SLES nodes.
		if ($::CSM)
		{
			my $rootpath = get_node_root_path($node);
			if ($rootpath)
			{
				push @ne, "\t\t\toption root-path \"$rootpath\";\n";
			}
		}

		# add the gateway for the node.
		$gateway = getNodeGateway($node);
		push @ne, "\t\t\toption routers $gateway;\n";

		if (my $sip = gethostbyname($tftpServer))
		{
			$sip = inet_ntoa($sip);
			push @ne, "\t\t\tnext-server $sip;\n";
		}
		elsif ($tftpServer)
		{
			push @ne, "\t\t\tnext-server $tftpServer; #NO IP!\n";
		}

		# add the user customized options
		if ($write_opt)
		{
			push @ne, "\n\t\t\t###USER DEFINED OPTIONS\n";
			my @cust_opt = split(';', $::OPTIONS);
			foreach my $option (@cust_opt)
			{
				$option =~ s/^\s//;
				push @ne, "\t\t\t$option;\n";
			}
			push @ne, "\t\t\t###END USER DEFINED OPTIONS\n";
		}

		push @ne, "\t\t}\n";
	}

	# now we need to put this into @::DHCP
	my @newDhcp;
	my $did_add          = 0;
	my $add_subnet_tried = 0;

  ADD_NODE_MAC:
	@newDhcp = ();
	foreach my $line (@::DHCP)
	{
		if (   ($line !~ /^\s*#/)
			&& ($line =~ /(} #$nodeNet\/$nodeMask subnet_end#)/))
		{
			push @newDhcp, @ne;
			$did_add = 1;
		}
		push @newDhcp, $line;
	}
	if ((!$did_add) && (!$add_subnet_tried))
	{

		# seems the node is sitting in a remote subnet.
		# need to add the subnet first.
		my $nameserver = $::NAMESERVERS;
		my $network    = &getNetwork($nodeNet);
		addSubnet($nodeNet, $nodeMask, $gateway, $nameserver, $network);
		$add_subnet_tried = 1;
		goto ADD_NODE_MAC;
	}

	@::DHCP = @newDhcp;
	if ($did_add)
	{
		msg("added node $node\n", "I", "IMsgAddedNode", $node);
		$::RESTART_DHCP = 1;
	}
}

sub addNode_UUID
{
	my $node      = shift;
	my $write_opt = shift;
	my $arch      = $::NODEHASH{$node}{InstallPkgArchitecture};
	my $mac       = $::NODEHASH{$node}{InstallAdapterMacaddr};
	my $uuid      = $::UUIDS->{$node};

	if ($arch =~ /ppc64/)
	{
		msg("UUID is not supported on pSeries node.\n",
			"E", "EMsgUUIDNotSupportedOnP");
		return;
	}

	if (!$uuid)
	{
		msg(
			"Can not add static node \"$node\" using --uuid method when its UUID attribute is null.\n",
			"E", "EMsgMissingUUID", $node
		   );
		return;
	}

	my ($host, $ip, $nodeMask, $nodeNet) = get_node_network_attr($node);

	my $serverIP = getNextServer($node, $ip, $nodeMask);

	$uuid = &bldUUIDStr($uuid);
	my $indent = "\t\t";
	my @dy;
	my $apt_hostname = $::NODEHASH{$node}{InstallAdapterHostname};
	my $gw           = $::NODEHASH{$node}{InstallAdapterGateway};
	my ($fixedhost, $fixedaddr);

	if ($apt_hostname)
	{
		($fixedhost, $fixedaddr) = NetworkUtils->getHost($apt_hostname);
		unless ($fixedaddr)
		{
			msg("Node: \"$apt_hostname\" was not found in /etc/hosts.\n",
				'W', 'IMsgNodeNotInHosts', $apt_hostname);
			next;
		}
	}
	else
	{
		$fixedhost = $node;
	}

	# Each node has a unique UUID and do not generate UUID stanza based on InstallAdapterHostname
	push @dy, $indent . "class \"$fixedhost\" { #CSM CLASS FOR $node\n";
	push @dy,
	  $indent . "\t" . "match if substring(option client-guid,1,16) = $uuid;\n";
	push @dy, $indent . "}\n";

	#get the domain part of the host name:
	my @dom;
	(undef, @dom) = split('\.', $fixedhost);
	my $domain = join('.', @dom);

	push @dy, $indent . "pool { #CSM POOL FOR $node\n";
	push @dy, $indent . "\tallow members of \"$fixedhost\";\n";
	push @dy, $indent . "\trange $ip;\n";
	if ($domain)
	{
		push @dy, $indent . "\toption domain-name \"$domain\";\n";
	}
	if ($fixedhost)
	{
		push @dy, $indent . "\toption host-name \"$fixedhost\";\n";
	}
	my $gateway = getNodeGateway($node);
	push @dy, $indent . "\toption routers $gateway;\n";

	# add the user customized options
	if ($write_opt)
	{
		push @dy, "\n$indent\t###USER DEFINED OPTIONS\n";
		my @cust_opt = split(';', $::OPTIONS);
		foreach my $option (@cust_opt)
		{
			$option =~ s/^\s//;
			push @dy, "$indent\t$option;\n";
		}
		push @dy, "$indent\t###END USER DEFINED OPTIONS\n";
	}

	push @dy, $indent . "}\n\n";

	my @newD;
	my $did_add          = 0;
	my $add_subnet_tried = 0;

  ADD_NODE_UUID:
	@newD = ();
	foreach my $line (@::DHCP)
	{
		if ($line =~ /CLASS ENTRIES FOR SUBNET\s+$nodeNet\/$nodeMask\s+/)
		{
			push @newD, $line;
			push @newD,
			  "\t\tnext-server $serverIP;   #CSM next-server for $node\n";
			my $filename = get_filename($node);
			if ($filename)
			{
				push @newD,
				  "\t\tfilename \"$filename\";   #CSM filename for $node\n";
			}
			push @newD, @dy;
			$did_add = 1;
			next;
		}
		push @newD, $line;
	}
	if ((!$did_add) && (!$add_subnet_tried))
	{

		# seems the node is sitting in a remote subnet.
		# need to add its subnet first.
		my $nameserver = $::NAMESERVERS;
		my $network    = &getNetwork($nodeNet);
		addSubnet($nodeNet, $nodeMask, $gateway, $nameserver, $network);
		$add_subnet_tried = 1;
		goto ADD_NODE_UUID;
	}

	@::DHCP = @newD;
	if ($did_add)
	{
		msg("added node $node\n", "I", "IMsgAddedNode", $node);
		$::RESTART_DHCP = 1;
	}
}

sub delNode
{

	# try to delete the node no matter the format in config file
	# is UUID or MAC.
	delNode_UUID(@_);
	delNode_MAC(@_);
}

sub delNode_MAC
{
	my $node      = shift;
	my $didRemove = 0;
	my $erase     = 0;
	my @content;

	# Remove the static stanza based on InstallAdapterHostname.
	my $killNode = $node;
	my $akbnode  = $::NODEHASH{$node}{"InstallAdapterHostname"};
	if ($akbnode)
	{
		$killNode = $akbnode;
	}

	foreach my $line (@::DHCP)
	{
		if ($erase)
		{
			if ($line =~ /}/)
			{
				$erase = 0;
			}
			next;
		}
		if (($line =~ /host $killNode {/) || ($line =~ /# host $killNode/))
		{
			$didRemove = 1;
			$erase     = 1;
			next;
		}
		push @content, $line;
	}
	if ($didRemove)
	{
		msg("removed node $killNode\n", "I", "IMsgRemovedNode", $killNode);
		$::RESTART_DHCP = 1;
	}
	@::DHCP = @content;
}

sub delNode_UUID
{
	my $killNode  = shift;
	my $didRemove = 0;
	my $erase     = 0;
	my @content;
	foreach my $line (@::DHCP)
	{
		if ($erase)
		{
			if ($line =~ /}/)
			{
				$erase = 0;
			}
			next;
		}
		if ($line =~ /CSM CLASS FOR $killNode/)
		{
			$didRemove = 1;
			$erase     = 1;
			next;
		}
		if ($line =~ /CSM POOL FOR $killNode/)
		{
			$didRemove = 1;
			$erase     = 1;
			next;
		}
		if ($line =~ /#CSM next-server for $killNode/)
		{
			next;
		}
		if ($line =~ /#CSM filename for $killNode/)
		{
			next;
		}
		push @content, $line;
	}
	if ($didRemove)
	{
		msg("removed node $killNode\n", "I", "IMsgRemovedNode", $killNode);
		$::RESTART_DHCP = 1;
	}
	@::DHCP = @content;
}

sub updateNodes
{
	my $nodes = shift;
	my @ns;
	if ($nodes eq '')
	{
		return;
	}

	# otherwise get all the macs you can
	if ($::UUID)
	{
		getUUIDs($nodes);    # stores UUIDs in $::UUIDS
	}
	else
	{
		getMacs($nodes);     # stores mactab in $::MACS
	}
	foreach my $node (@$nodes)
	{
		if ($::MACS)
		{
			unless ((@ns = grep /$node/, (keys %$::MACS)))
			{
				print STDERR "$::MYNAME: no MAC for node $node";
				next;
			}
		}
		else
		{
			unless ((@ns = grep /$node/, (keys %$::UUIDS)))
			{
				print STDERR "$::MYNAME: no UUID for node $node";
				next;
			}
		}

		foreach my $n (@ns)
		{
			unless ($::NEW)
			{
				delNode($n);
			}
			my $write_opt = 0;
			$write_opt = 1 if ($::OPTIONS);
			addNode($n, $write_opt);
		}
	}
}

sub restartDHCP
{
	return if ($::NO_DHCPD_RESTART);

	if ($::PLTFRM eq "AIX")
	{

		# if bootpd is running, need to stop it before starting dhcp.
		my $cmd = "$::PS -ef | $::GREP bootp | $::GREP -v \'grep bootp\'";
		my @output = NodeUtils->runcmd($cmd, -1);
		foreach my $line (@output)
		{
			if ($line =~ /bootpd/)
			{
				msg(undef, "I", "IMsgBootpRunning");
				exit(0);
			}
		}

		# stop the DHCP daemon if it is already running
		$cmd = "$::STOPSRC -s dhcpsd";
		NodeUtils->runcmd($cmd, -1);

		# wait for the DHCP daemon to release port 67.
		sleep(1);

		# check whether port 67 is still occupied
		$cmd = "$::NETSTAT -an | $::GREP \"\\\.67 \"";
		@output = NodeUtils->runcmd($cmd, -1);
		if (@output)
		{
			msg(undef, "I", "IMsgReleasePort");
			exit(0);
		}

		# now start the DHCP deamon
		$cmd = "$::STARTSRC -s dhcpsd";
		NodeUtils->runcmd($cmd, -1);

		$cmd = "$::LSSRC -s dhcpsd";
		@output = NodeUtils->runcmd($cmd, -1);
		my ($subsys, $group, $pid, $status) = split(' ', $output[1]);
		if (!defined($status) || $status ne 'active')
		{
			msg(undef, "I", "IMsgDHCPRestartErr");
		}
		else
		{
			msg(undef, "I", "IMsgDHCPRestartOkay");
		}
	}
	else
	{

		# for Linux

		my $os = xCATCSM->osver();
		if ($os eq "rh62")
		{
			1;
		}
		elsif ($os =~ /rh/)
		{
            # rhel5 put lease file in new place ...
            if ($os =~ /rhserver|rhclient/) {
                unless (-f "/var/lib/dhcpd/dhcpd.leases") {
                    system ("touch /var/lib/dhcpd/dhcpd.leases >/dev/null 2>&1");
                }
            }
            else {
                unless (-f "/var/lib/dhcp/dhcpd.leases") {
                    system ("touch /var/lib/dhcp/dhcpd.leases >/dev/null 2>&1");
                }
            }
			system("/sbin/chkconfig --level 345 dhcpd on");
			my $rc =
			  system("/sbin/service dhcpd restart  > /dev/null 2>&1 ") >> 8;
			if ($rc != 0)
			{
				msg("Restart DHCP server: ERROR!\n", "I", "IMsgDHCPRestartErr");
			}
			else
			{
				msg("Restart DHCP server: OK!\n", "I", "IMsgDHCPRestartOkay");
			}
		}
		elsif ($os =~ /suse7/)
		{
			unless (-f "/var/lib/dhcp/dhcpd.leases")
			{
				system("touch /var/lib/dhcp/dhcpd.leases >/dev/null @>&1");
			}
			system("cp -f /etc/rc.config /etc/rc.config.ORIG.3");
		}
		elsif ($os =~ /sles[89]|suse[8|9]|sles10/)
		{
			unless (-f "/var/lib/dhcp/dhcpd.leases")
			{
				system("touch /var/lib/dhcp/dhcpd.leases >/dev/null @>&1");
			}
			system("chkconfig -a dhcpd >/dev/null 2>&1");
			my $rc = system("/usr/sbin/rcdhcpd restart  > /dev/null 2>&1") >> 8;
			if ($rc != 0)
			{
				msg("Restart DHCP server: ERROR!\n", "I", "IMsgDHCPRestartErr");
			}
			else
			{
				msg("Restart DHCP server: OK!\n", "I", "IMsgDHCPRestartOkay");
			}

		}
	}

}

sub makeFlags
{

	# only for xCAT
	if ($::CSM)
	{
		return;
	}
	my $flags = '';
	$::ELOADER   && ($flags .= " --eloaderhack ");
	$::NEW       && ($flags .= " --new ");
	$::NODYNAMIC && ($flags .= " --nd ");
	$::ALL       && ($flags .= " --all ");
	$::DELETE    && ($flags .= " --delete ");
	$::DELETESUB && ($flags .= " --delete-sub ");
	$::DELETENET && ($flags .= " --delete-net ");
	$::DELETEDY  && ($flags .= " --delete-dynamic ");
	return $flags;
}

# the @::SUBDHCP array contains lines that look like this:
# subdhcpd server, noderange,
sub doSubDhcp
{

	# only for xCAt
	if ($::CSM)
	{
		return;
	}
	my $nodes = shift;
	my $flags = makeFlags($nodes);
	my $ns    = join(',', @$nodes);
	my @rnr;
	foreach my $sd (@::SUBDHCP)
	{
		my ($subdhcpd, $nr, @junk) = split(',', $sd);

		# get intersection of subdhcp and nodes entered.
		chomp(my $nr = `$::XCATROOT/bin/nr $nr`);
		foreach my $n (@rnr)
		{
			if (grep $n, @$nodes)
			{
				push @rnr, $n;
			}
		}

		# if there are nodes to do in this subdhcp, then go do it.
		if ($#rnr > -1)
		{
			print "Executing $::RSHC $sd $::XCATROOT/sbin/$::MYNAME ",
			  join(',', @rnr), "\n";
			system("fping -c 1 $sd >/dev/null 2>&1");
			if ($? >> 8)
			{
				msg("Can not ping $sd\n");
			}
			else
			{
				my $cmd = "$::RSHC $sd $::XCATROOT/sbin/$::MYNAME ";
				$cmd .= join(',', @rnr);
				system($cmd);
			}

		}
	}
}

#
sub add_default_network
{
	my @activeNics;
	chomp(@activeNics =
		`$::NETSTAT -rn | $::EGREP "\.*(vlan|bond|eth|myri|man|wlan)[0-9]+\\.?[0-9]*\$" | $::AWK '{print \$8}' | sort | uniq`
	);

	foreach my $nic (@activeNics)
	{
		&addNetwork($nic);
	}
	if (-f "/etc/sysconfig/dhcpd")
	{
		if ($::IS_DISTRO =~ /SLES/)
		{
			`perl -pi -e "s/^DHCPD_BINARY=.*/DHCPD_BINARY=\\'\\/usr\\/sbin\\/dhcpd.lpf\\'/g" /etc/sysconfig/dhcpd`;
		}

		system(
			'/bin/grep -i CSM_NO_CHANGE /etc/sysconfig/dhcpd > /dev/null 2>&1');
		my $rc = $? >> 8;
		if ($rc != 0)    # If keyword does not exist, modify file
		{
			if ($::IS_DISTRO =~ /SLES/)
			{
				`perl -pi -e "s/^DHCPD_INTERFACE=.*/DHCPD_INTERFACE=\\"@activeNics\\"/g" /etc/sysconfig/dhcpd`;
			}
			else
			{
				`perl -pi -e "s/^DHCPDARGS=.*/DHCPDARGS=\\"@activeNics\\"/g" /etc/sysconfig/dhcpd`;
			}
		}
	}

	# all default all#
	if ($::XCAT)
	{
		addNetwork("all");
	}
}

# add subnets for each up network
sub add_default_subnet
{
	my @ips;
	if ($::PLTFRM eq "AIX")
	{
		chomp(@ips =
			`$::IFCONFIG -a | $::GREP "inet " | $::GREP -v 127.0.0.1 | $::GREP -v inet6 | $::AWK '{print \$2 ":" \$6 ":" \$4}'`
		);
	}
	else
	{
		chomp(@ips =
			`$::IP addr | $::GREP "inet " | $::GREP -v 127.0.0.1 | $::TR '/' ' ' | $::AWK '{print \$2 ":" \$5 ":" \$3 ":" \$8}'`
		);
	}

	foreach (@ips)
	{
		my ($ip, $broadcast, $bits, $network) = split(":", $_);
		my ($net, $gw);

		if ($::PLTFRM eq "AIX")
		{
			$bits = NetworkUtils->hex2dec($bits);
			$net = xCATCSM->ipCalc("network", $ip, $bits);
		}
		else
		{

			# only add active network by default.
			system("$::IFCONFIG | egrep \"^$network\"  >/dev/null 2>&1");
			my $rc = $? >> 8;
			next if ($rc);

			$bits = xCATCSM->bitscount($bits);
			$net = xCATCSM->ipCalc("network", $ip, $bits);
			if ($::XCAT)
			{
				$gw = tabdb($::NETWORKSTAB, $net, 2);
				unless ($gw)
				{
					$gw = "0.0.0.0";
				}
			}
		}
		if ($net ne '')
		{
			addSubnet($net, $bits, $gw, $ip, $network);
		}
	}

	if ($::XCAT)
	{

		#add subnets for all
		if ((-f "$::NETWORKSTAB") and ($::ME eq $::MASTER))
		{
			my $p = @::NETS;
			foreach my $i (0 .. $p - 1)
			{
				&addSubnet(            $::NETS[$i], $::MASKS[$i], $::GWS[$i],
						   $::DNSS[$i], 'all');
			}
		}
	}
}

################################################################################
# MAIN main Main program
################################################################################
# initialize the global variables
&initialize();

# get an reference to the array of objects, such as subnets, nets, nodes, etc.
my $objs = &getArgs();

&loadVars();

if ($::XCAT)
{
	&makeNetworks();
	&loadNetworks();
}
# If dhcp configure file don't exist, delete option don't need be run
if  ((!-f "$::TDHCP") &&
	($::DELETE || $::DELETESUB || $::DELETENET || $::DELETEDY))
{
		# Set flag, avoid invoke END section.
		$::CONTENT_SYNCED = 1;
		exit 0;
}

if ((!-f "$::TDHCP") || ($::NEW))
{
	&newConfig();
}

# read the dhcpd.conf file into memory.  All of our operations will
# do this
open(FILE, "$::TDHCP") or die "$::MYNAME: Can't open $::TDHCP";

# read DHCP into memory:
while (<FILE>)
{
	push @::DHCP, $_;
}
close(FILE);

# check that the dhcpd.conf file matches what we expect it to:

unless (grep /^#xCAT|IBM CSM makedhcp/, @::DHCP)
{
	&newConfig();

	# read in DHCP again
	@::DHCP = ();
	open(FILE, "$::TDHCP") or die "$::MYNAME: Can't open $::TDHCP";
	while (<FILE>)
	{
		push @::DHCP, $_;
	}
	close(FILE);
}

if ($::DELETE)
{
	my $nodes;    # reference to the array of target nodes.

	if (scalar(@$objs))
	{
		if ($::XCAT)
		{
			my $ns = join(',', @$objs);
			chomp(@$nodes = `$::XCATROOT/bin/nr $ns`);
		}
		else
		{
			chomp(@$nodes = xCATCSM->noderange($objs, undef, undef));
		}
	}

	if ($::CSM)
	{

		# read the node attributes into %::NODEHASH
		read_node_attributes($nodes);
	}

	# make sure we get each occurance of the node
	foreach my $n (@$nodes)
	{
		&delNode($n);
	}
	finishDHCP();
	if ($::XCAT)
	{
		if ($::ME eq $::MASTER)
		{
			doSubDhcp($nodes);
		}
	}
	exit 0;
}

if ($::DELETESUB)
{
	foreach my $sub (@$objs)
	{
		my ($net, $mask) = split(/\//, $sub);
		unless ($net && $mask)
		{
			msg("Invalid net/mask argument\n", "E1", "EMsgInvalidNetMask");
		}
		unless (grep /\./, $mask)
		{

			# netmask is in the bits count form.
			$mask = xCATCSM->bitscount($mask);
		}
		delSubnet($net, $mask);
	}
	finishDHCP();
	if ($::XCAT)
	{
		if ($::ME eq $::MASTER)
		{
			doSubDhcp($objs);
		}
	}
	exit 0;

}

if ($::DELETENET)
{
	foreach (@$objs)
	{
		delNetwork($_);
	}
	finishDHCP();
	if ($::XCAT)
	{
		if ($::ME eq $::MASTER)
		{
			doSubDhcp($objs);
		}
	}
	exit 0;
}

if ($::DELETEDY)
{
	my @ranges;
	foreach my $dy (@$objs)
	{
		@ranges = split(/,/, $dy);
	}
	foreach my $range (@ranges)
	{
		delDynamic($range);
	}

	finishDHCP();
	if ($::XCAT)
	{
		if ($::ME eq $::MASTER)
		{
			doSubDhcp($objs);
		}
	}
	exit 0;
}

# Add network for all the active NICs.
if ($::PLTFRM eq "Linux")
{
	&add_default_network();
}

# The subnet corresponding to the active NICs should always be there.
&add_default_subnet();

# Add dynamic. Only used by xCAT.
if (($::NODYNAMIC eq '') && $::XCAT)
{
	my @dynamicr;
	if ($::XCAT)
	{
		chomp(@dynamicr =
			  `egrep "^dynamicr\\b" <$::SITETAB | awk '{print \$2}'`);
	}
	else
	{
		chomp(@dynamicr = ());
	}
	foreach (@dynamicr)
	{
		if ($_ =~ /NA/) { next }
		&addDynamic(split(',', $_));
	}
}

if ($::ADDSUB)
{
	foreach my $sub (@$objs)
	{
		my ($net, $mask) = split(/\//, $sub);
		unless ($net && $mask)
		{
			msg("Invalid net/mask argument\n", "E1", "EMsgInvalidNetMask");
		}
		unless (grep /\./, $mask)
		{

			# netmask is in the bits count form.
			$mask = xCATCSM->bitscount($mask);
		}
		my $gateway       = $::ATTRS{"Gateway"};
		my $nameserver    = $::NAMESERVERS;
		my $network       = &getNetwork($net);
		my $write_options = 0;
		$write_options = 1 if ($::OPTIONS);
		addSubnet($net, $mask, $gateway, $nameserver, $network, $write_options);
	}
	finishDHCP();
	exit 0;
}

if ($::ADDNET)
{
	foreach (@$objs)
	{
		addNetwork($_);
	}
	if ($::RESTART_DHCP)
	{
		msg(
			"Shared network can not be with null content, otherwise DHCP server will not start.
Please add contents to this shared network immediately.\n", "I",
			"IMsgNullNetContent"
		   );
		$::RESTART_DHCP = 0;
		finishDHCP();
	}
	exit 0;
}

if ($::ADDDY)
{
	my @ranges;

	foreach my $dy (@$objs)
	{
		my @dys = split(/,/, $dy);
		push @ranges, @dys;
	}
	foreach my $range (@ranges)
	{
		my ($startRange, $endRange) = split(/-/, $range);
		unless ($startRange)
		{
			msg("Invalid startRange and endRange.\n", "E1", "EMsgInvalidRange");
		}
		my $network = &getNetwork($startRange);
		my $arch    = $::IS_ARCH;
		if ($arch !~ /ppc/)
		{
			$arch = "ia32";
		}
		my ($mask);

		if (!$::ATTRS{"Netmask"})
		{
			msg("Netmask must be provided when adding dynamic node ranges.\n",
				"E1", "EMsgMissingNetmask");
		}
		else
		{
			$mask = $::ATTRS{"Netmask"};
		}

		my $serverIP;
		my $bc       = xCATCSM->ipCalc("broadcast", $startRange, $mask);
		my $maskBits = xCATCSM->countbits($mask);

		# determine next-server, this is used to identify tftp server.
		if ($::CSM)
		{
			$serverIP = NetworkUtils->get_source_ip_to_target($startRange);
			chomp($serverIP);
			($serverIP) = $serverIP =~ /^(\d+\.\d+\.\d+\.\d+)$/;
			if (!$serverIP)
			{
				my $net = xCATCSM->ipCalc("network", $startRange, $mask);
				msg(
					"Can not route to this network $net. Please set up routing table correctly.\n",
					"E1", "EMsgCannotRouteToSubnet"
				   );
			}
		}
		else
		{

			# For xCAT, check which interface matches the subnet of the dynamic range.
			my @tmpIPs;
			unless (@tmpIPs = `ip addr`)
			{
				die "$::MYNAME: could not execute 'ip addr' command";
			}
			foreach my $line (@tmpIPs)
			{
				if ($line =~ /inet .*\/$maskBits brd $bc/)
				{
					$line =~ s/\// /;
					$line = (split(/\s+/, $line))[2];
					$serverIP = $line;
					last;
				}
			}
		}
		my $write_opt = 0;
		$write_opt = 1 if ($::OPTIONS);
		addDynamic($network, $arch, $serverIP, $mask, $startRange, $endRange,
				   $write_opt);
	}
	finishDHCP();
	exit 0;
}

# Now all the flags have been handled. The default action is to add nodes.
my $nodes;    # reference to the array of target nodes.

if (scalar(@$objs))
{
	if ($::XCAT)
	{
		my $ns = join(',', @$objs);
		chomp(@$nodes = `$::XCATROOT/bin/nr $ns`);
	}
	else
	{
		chomp(@$nodes = xCATCSM->noderange($objs, undef, undef));
	}
}
else
{
	finishDHCP();
	exit(0);
}

# Now deal with nodes.
if ($::CSM)
{

	# read the node attributes into %::NODEHASH
	read_node_attributes($nodes);
}
updateNodes($nodes);
finishDHCP();

if ($::XCAT)
{
	if ($::ME eq $::MASTER)
	{
		doSubDhcp($nodes);
	}
}

END
{

	# catch the obnormal exits.
	if ((@::DHCP) && (!$::CONTENT_SYNCED))
	{
		finishDHCP();
	}
}
