From abea7eceee69b2deb4f2a5a8a5e29e2d6f42312a Mon Sep 17 00:00:00 2001 From: daniceexi Date: Thu, 19 Dec 2013 04:53:49 -0500 Subject: [PATCH] Rewirte the proxydhcp.c with proxydhcp-xcat in perl; Added site.installnic to control the nics setting for windows; Added servicenode.proxydhcp and noderes.proxydhcp to control the starting of proxydhcp-xcat daemon and makedhcp againsts node for windows deployment --- perl-xCAT/xCAT/Schema.pm | 22 +- xCAT-server/lib/perl/xCAT/Template.pm | 37 ++- xCAT-server/lib/xcat/plugins/AAsn.pm | 27 ++- xCAT-server/lib/xcat/plugins/dhcp.pm | 13 +- xCAT-server/sbin/proxydhcp-xcat | 331 ++++++++++++++++++++++++++ 5 files changed, 419 insertions(+), 11 deletions(-) create mode 100755 xCAT-server/sbin/proxydhcp-xcat diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index 61ae40ebf..a5c05e2d5 100755 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -595,7 +595,7 @@ nodepos => { }, }, noderes => { - cols => [qw(node servicenode netboot tftpserver tftpdir nfsserver monserver nfsdir installnic primarynic discoverynics cmdinterface xcatmaster current_osimage next_osimage nimserver routenames nameservers comments disable)], + cols => [qw(node servicenode netboot tftpserver tftpdir nfsserver monserver nfsdir installnic primarynic discoverynics cmdinterface xcatmaster current_osimage next_osimage nimserver routenames nameservers proxydhcp comments disable)], keys => [qw(node)], tablespace =>'XCATTBS16K', table_desc => 'Resources and settings to use when installing nodes.', @@ -618,6 +618,7 @@ noderes => { nimserver => 'Not used for now. The NIM server for this node (as known by this node).', routenames => 'A comma separated list of route names that refer to rows in the routes table. These are the routes that should be defined on this node when it is deployed.', nameservers => 'An optional node/group specific override for name server list. Most people want to stick to site or network defined nameserver configuration.', + proxydhcp => 'To specify whether the node supports proxydhcp protocol. Valid values: yes or 1, no or 0. Default value is yes.', comments => 'Any user-written notes.', disable => "Set to 'yes' or '1' to comment out this row.", }, @@ -839,7 +840,7 @@ ppchcp => { }, }, servicenode => { - cols => [qw(node nameserver dhcpserver tftpserver nfsserver conserver monserver ldapserver ntpserver ftpserver nimserver ipforward dhcpinterfaces comments disable)], + cols => [qw(node nameserver dhcpserver tftpserver nfsserver conserver monserver ldapserver ntpserver ftpserver nimserver ipforward dhcpinterfaces proxydhcp comments disable)], keys => [qw(node)], tablespace =>'XCATTBS16K', table_desc => 'List of all Service Nodes and services that will be set up on the Service Node.', @@ -857,6 +858,7 @@ servicenode => { nimserver => 'Not used. Do we set up a NIM server on this service node? Valid values:yes or 1, no or 0. If no or 0, it does not change the current state of the service.', ipforward => 'Do we set up ip forwarding on this service node? Valid values:yes or 1, no or 0. If no or 0, it does not change the current state of the service.', dhcpinterfaces => 'The network interfaces DHCP server should listen on for the target node. This attribute can be used for management node and service nodes. If defined, it will override the values defined in site.dhcpinterfaces. This is a comma separated list of device names. !remote! indicates a non-local network for relay DHCP. For example: !remote!,eth0,eth1', + proxydhcp => 'Do we set up proxydhcp service on this node? valid values: yes or 1, no or 0. If yes, the proxydhcp daemon will be enabled on this node.', comments => 'Any user-written notes.', disable => "Set to 'yes' or '1' to comment out this row.", @@ -1081,9 +1083,10 @@ site => { " xcatmaxbatchconnections: Number of concurrent xCAT connections allowed from the nodes.\n". " Value must be less than xcatmaxconnections. Default is 50.\n". " xcatdport: The port used by the xcatd daemon for client/server communication.\n\n". - " xcatiport: The port used by xcatd to receive install status updates from nodes.\n\n", - " xcatsslversion: The ssl version by xcatd. Default is SSLv3.\n\n", - " xcatsslciphers: The ssl cipher by xcatd. Default is 3DES.\n\n", + " xcatiport: The port used by xcatd to receive install status updates from nodes.\n\n". + " xcatsslversion: The ssl version by xcatd. Default is SSLv3.\n\n". + " xcatsslciphers: The ssl cipher by xcatd. Default is 3DES.\n\n". + " setinstallnic: Set the network configuration for installnic to be static.\n\n", value => 'The value of the attribute specified in the "key" column.', comments => 'Any user-written notes.', disable => "Set to 'yes' or '1' to comment out this row.", @@ -1660,6 +1663,11 @@ my @nodeattrs = ( tabentry => 'noderes.monserver', access_tabentry => 'noderes.node=attr:node', }, + {attr_name => 'supportproxydhcp', + tabentry => 'noderes.proxydhcp', + access_tabentry => 'noderes.node=attr:node', + }, + {attr_name => 'kernel', tabentry => 'bootparams.kernel', access_tabentry => 'bootparams.node=attr:node', @@ -1728,6 +1736,10 @@ my @nodeattrs = ( {attr_name => 'setupipforward', tabentry => 'servicenode.ipforward', access_tabentry => 'servicenode.node=attr:node', + }, + {attr_name => 'setupproxydhcp', + tabentry => 'servicenode.proxydhcp', + access_tabentry => 'servicenode.node=attr:node', }, # - moserver not used yet # {attr_name => 'setupmonserver', diff --git a/xCAT-server/lib/perl/xCAT/Template.pm b/xCAT-server/lib/perl/xCAT/Template.pm index c12f44e79..51584ece1 100644 --- a/xCAT-server/lib/perl/xCAT/Template.pm +++ b/xCAT-server/lib/perl/xCAT/Template.pm @@ -358,6 +358,28 @@ sub windows_net_cfg { my $component_end = ''; my $interfaces_cfg = ''; + + # get the installnic + my $nrtab = xCAT::Table->new('noderes',-create=>0); + my $installnic; + if ($nrtab) { + my $nrent = $nrtab->getNodeAttribs($node,['installnic', 'primarynic']); + if ($nrent) { + if (defined ($nrent->{'installnic'})) { + $installnic = $nrent->{'installnic'}; + } elsif (defined ($nrent->{'primarynic'})) { + $installnic = $nrent->{'primarynic'}; + } + } + } + + # get the site.setinstallnic + my @ents = xCAT::TableUtils->get_site_attribute("setinstallnic"); + my $setinstallnic; + if ($ents[0] =~ /1|yes|y/i) { + $setinstallnic = 1; + } + my $nicstab = xCAT::Table->new('nics',-create=>0); my $hasif; if ($nicstab) { @@ -370,6 +392,15 @@ sub windows_net_cfg { my ($nicname, $ips) = split(/!/, $_); unless ($nicname) { next; } if ($nicname =~ /^bmc/) { next; } # do nothing for bmc interface + my $dosetgw = 0; + if ($nicname eq $installnic) { + if ($setinstallnic) { + # set to static with gateway + $dosetgw = 1; + } else {# else: do nothing means using dhcp + next; + } + } # else: do not set gateway, since gateway only set for installnic if ($ips) { $interface_cfg .= 'falsefalse'; $interface_cfg .= "$nicname"; @@ -383,6 +414,9 @@ sub windows_net_cfg { next; } if ($gw) { $gateway = $gw; } + if ($gateway eq '') { + $gateway = xCAT::NetworkUtils->my_ip_facing($ip); + } $interface_cfg .= ''.$ip."/$netmask".''; } if ($num eq 1) { @@ -396,9 +430,10 @@ sub windows_net_cfg { $interface_cfg .= 'truetrue'; $interface_cfg .= "$nicname"; } + # add the default gateway - if ($gateway) { + if ($gateway && $dosetgw) { $interface_cfg .= '1'.$gateway.'0/0'; } $interface_cfg .= ''; diff --git a/xCAT-server/lib/xcat/plugins/AAsn.pm b/xCAT-server/lib/xcat/plugins/AAsn.pm index f6dcede30..bb80c11ff 100755 --- a/xCAT-server/lib/xcat/plugins/AAsn.pm +++ b/xCAT-server/lib/xcat/plugins/AAsn.pm @@ -128,7 +128,11 @@ sub init_plugin } } - + if ($servicelist->{"proxydhcp"} == 1) { + &setup_proxydhcp(1); + } else { + &setup_proxydhcp(0); + } } # end Linux only # # setup these services for AIX or Linux @@ -1455,5 +1459,26 @@ sub enable_TFTPhpa return 0; } +# enable or disable proxydhcp service +sub setup_proxydhcp { + my $flag = shift; + + my $pid; + # read the pid of proxydhcp + if (open (PIDF, "; + } + close(PIDF); + + # kill it first + if ($pid) { + kill 2, $pid; + } + + # start the proxydhcp daemon if it's set to enable + if ($flag) { + system ("/opt/xcat/sbin/proxydhcp-xcat -d"); + } +} 1; diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index 33fcb002c..1332b7c8f 100755 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -102,9 +102,14 @@ sub check_uefi_support { # check whether the proxydhcp has been enabled. sub proxydhcp { - my @output = xCAT::Utils->runcmd("ps -C proxydhcp", -1); + my $nrent = shift; + + if ($nrent && defined $nrent->{'proxydhcp'} && $nrent->{'proxydhcp'} =~ /0|no|n/i) { + return 0; + } + my @output = xCAT::Utils->runcmd("ps -C proxydhcp-xcat", -1); if (@output) { - if (grep /proxydhcp/, @output) { + if (grep /proxydhcp-xcat/, @output) { return 1; } } @@ -587,7 +592,7 @@ sub addnode } else { # If proxydhcp daemon is enabled for windows deployment, do vendor-class-identifier of "PXEClient" to bump it over to proxydhcp.c if (($douefi == 2 and $chainent->{currstate} =~ /^install/) or $chainent->{currstate} =~ /^winshell/) { - if (proxydhcp()){ #proxy dhcp required in uefi invocation + if (proxydhcp($nrent)){ #proxy dhcp required in uefi invocation $lstatements = 'if option client-architecture = 00:00 or option client-architecture = 00:07 or option client-architecture = 00:09 { filename = \"\"; option vendor-class-identifier \"PXEClient\"; } else { filename = \"\"; }'.$lstatements; #If proxydhcp daemon is enable, use it. } else { $lstatements = 'if option user-class-identifier = \"xNBA\" and option client-architecture = 00:00 { always-broadcast on; filename = \"http://'.$nxtsrv.'/tftpboot/xcat/xnba/nodes/'.$node.'\"; } else if option client-architecture = 00:07 or option client-architecture = 00:09 { filename = \"\"; option vendor-class-identifier \"PXEClient\"; } else if option client-architecture = 00:00 { filename = \"xcat/xnba.kpxe\"; } else { filename = \"\"; }'.$lstatements; #Only PXE compliant clients should ever receive xNBA @@ -1691,7 +1696,7 @@ sub process_request } else { $chainents = undef; } - $nrhash = $nrtab->getNodesAttribs($req->{node}, ['tftpserver','netboot']); + $nrhash = $nrtab->getNodesAttribs($req->{node}, ['tftpserver','netboot','proxydhcp']); my $nodetypetab; $nodetypetab = xCAT::Table->new('nodetype',-create=>0); if ($nodetypetab) { diff --git a/xCAT-server/sbin/proxydhcp-xcat b/xCAT-server/sbin/proxydhcp-xcat new file mode 100755 index 000000000..89efeab0e --- /dev/null +++ b/xCAT-server/sbin/proxydhcp-xcat @@ -0,0 +1,331 @@ +#! /usr/bin/perl +# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html +# This program will start a proxydhcp daemon to handle 4011 request + +use Sys::Syslog; +use IO::Socket::INET; +use IO::Select; +use Getopt::Long; + +my $quit = 0; +my $doreload = 0; +my %nodecfg; +my $bootpmagic = pack("C*", 0x63, 0x82, 0x53, 0x63); + +# set signal handler to set flag to reload configuration file +$SIG{USR1} = sub { + $doreload = 1; +}; +$SIG{TERM} = $SIG{INT} = sub { + $quit = 1; +}; + +my $verbose; +my $tobedaemon; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); +GetOptions( + 'V' => \$verbose, + 'd' => \$tobedaemon, +); + +if ($tobedaemon) { + daemonize(); +} + +# open syslog +openlog("proxydhcp", "nofatal", "local4"); + +my $socket; +my $retry = 5; +while ($retry > 0) { + $socket = IO::Socket::INET->new(LocalPort => 4011, + Proto => 'udp', + Domain => AF_INET); + if ($socket) { + last; + } else { + sleep 1; + } + $retry--; +} +unless ($socket) { + syslog ("info", "Unable to open socket on port 4011."); + closelog(); + exit; +} + +# regist my pid to /var/run/xcat/proxydhcp.pid +if (open (PIDF, ">/var/run/xcat/proxydhcp-xcat.pid")) { + print PIDF $$; + close(PIDF); +} else { + syslog ("info", "Cannot open /var/run/xcat/proxydhcp.pid."); + closelog(); + exit; +} + + +my $select = new IO::Select; +$select->add($socket); +load_cfg(); +until ($quit) { + until ($select->can_read(5)) { #Wait for data + if ($doreload) { + load_cfg(); + syslog ("info", "Reload configuration file in select."); + } + if ($quit) { last; }; + yield; + } + + if ($doreload) { + load_cfg(); + syslog ("info", "Reload configuration file before recv."); + } + + my $data; + my $caddr = $socket->recv($data,1500); + + my ($cport, $cnip) = sockaddr_in($caddr); + my $snip = my_ip_facing($cnip); + unless ($snip) { + syslog ("info", "Cannot find the server ip of proxydhcp daemon"); + next; + } + + if (length ($data) < 320) { + next; + } + + my @package = unpack("C*", $data); + my @replypkg; + + if (pack("C*", $package[0xec], $package[0xed], $package[0xee], $package[0xef]) != $bootpmagic) { + next; + } + + # get the node name of client + my $nodename = gethostbyaddr($cnip, AF_INET); + + # get the winpepath + my $winpepath = ""; + if ($nodename) { + if (defined $nodecfg{$nodename}) { + $winpepath = $nodecfg{$nodename}; + if ($verbose) {syslog ("info", "Find configure for $nodename= $nodecfg{$nodename} in configuration file")}; + } else { + $nodename =~ s/\..*//; + if (defined $nodecfg{$nodename}) { + $winpepath = $nodecfg{$nodename}; + if ($verbose) {syslog ("info", "Find configure for $nodename= $nodecfg{$nodename} in configuration file")}; + } + } + } + + # get the Vendor class identifier + my $strp = 0xf0; + my $archp = 0; + while ($strp < $#package) { + if ($package[$strp] eq 60) { + $archp = $strp + 0x11; + last; + } else { + $strp += $package[$strp+1] + 2; + } + } + + # get the winpe boot loader path + my $winboot = $winpepath."Boot/pxeboot.0"; + if ($archp) { + my $clienttype = substr($data, $archp, 5); + if ($clienttype == "00000") { + #if ("$package[$archp]$package[$archp+1]$package[$archp+2]$package[$archp+3]$package[$archp+4]" == "00000") { + $winboot = $winpepath."Boot/pxeboot.0"; + } elsif ($clienttype == "00007") { + $winboot = $winpepath."Boot/bootmgfw.efi"; + } + } + + syslog ("info", "The boot loader path for node $nodename is $winboot"); + + # set message type + $replypkg[0] = 2; + + # set the hardware type + $replypkg[1] = $package[1]; + + # set the hardware address length + $replypkg[2] = $package[2]; + + # set the hops + $replypkg[3] = $package[3]; + + # set the transaction ID + @replypkg = (@replypkg, @package[4 .. 7]); + + # set elapsed time + $replypkg[8] = 0; + $replypkg[9] = 0; + + # set bootp flag + $replypkg[0xa] = 0; + $replypkg[0xb] = 0; + + # set client ip + @replypkg = (@replypkg, @package[0xc .. 0xf]); + + # set Your (client IP) + @replypkg = (@replypkg, 0, 0, 0, 0); + + #set Next server IP (set my IP) + @replypkg = (@replypkg, unpack("C*", $snip)); + + # set dhcp relay agent ip + @replypkg = (@replypkg, 0, 0, 0, 0); + + # set client hardware address + @replypkg = (@replypkg, @package[0x1c .. 0x2b]); + + # set server host name + foreach (0x2c .. 0x6b) { + $replypkg[$_] = 0; + } + + # set the boot file name + @replypkg = (@replypkg, unpack("C*", $winboot)); + my $lenth = length ($winboot); + foreach (0x6c + $lenth .. 0xeb) { + $replypkg[$_] = 0; + } + + # add magic cookie + #my @xx = unpack("C*", $bootpmagic); + #@replypkg = (@replypkg, @xx); + @replypkg = (@replypkg, unpack("C*", $bootpmagic)); + + # set dhcp msg type + $replypkg[0xf0] = 0x35; # option number + $replypkg[0xf1] = 0x1; # msg length + $replypkg[0xf2] = 0x5; # dhcp ack + + # set dhcp server identifer + $replypkg[0xf3] = 0x36; # option number + $replypkg[0xf4] = 0x4; # msg length + @replypkg = (@replypkg, unpack("C*", $snip)); + + # set the bcd path + my $winbcd = $winpepath."Boot/BCD"; + $replypkg[0xf9] = 0xfc; # option number + $replypkg[0xfa] = length($winbcd) + 1; # msg length + @replypkg = (@replypkg, unpack("C*", $winbcd)); + $replypkg[0xfa + length($winbcd) + 1] = 0; + $replypkg[0xfa + length($winbcd) + 2] = 0xff; + + $socket->send(pack("C*", @replypkg), 0, $caddr); + + syslog ("info", "The BCD path for node $nodename is $winbcd"); + + # debug package detail + if (0) { + my $msg; + my $firstline = 1; + my $num = 0; + foreach (@replypkg) { + my $v = sprintf("%2x ", $_); + $msg .= $v; + if (($num - 5)%8 eq 0) { + $msg .= " "; + } + if (($num - 5)%16 eq 0) { + syslog ("info", $msg); + print $msg."\n"; + $msg = ""; + } + $num++; + } + print $msg."\n"; + } +} + +closelog(); + +# daemonize the service +sub daemonize +{ + chdir('/'); + umask 0022; + my $pid = fork; + + if ($pid) { + exit; + } + open STDOUT, '>/dev/null'; + open STDERR, '>/dev/null'; + $0='proxydhcp-xcat'; + $progname = \$0; +} + +# load configuration from /var/lib/xcat/proxydhcp.cfg to %nodecfg +sub load_cfg +{ + $doreload = 0; + if (! -r "/var/lib/xcat/proxydhcp.cfg") { + return; + } + + if (! open (CFG, ") { + $mycfg .= $_; + } + + my $p = 0; + while (1) { + + my $name = substr($mycfg, $p, 50); + $p += 50; + my $value = substr($mycfg, $p, 150); + $p += 150; + + $name =~ s/\0//g; + $value =~ s/\0//g; + if ($name) { + $nodecfg{$name} = $value; + } else { + return; + } + } + + + close(CFG); +} + +# get the ip in xCAT MN/SN which facing target ip +sub my_ip_facing +{ + my $peer = shift; + unless ($peer) { return undef; } + my $noden = unpack("N", $peer); + my @nets = split /\n/, `/sbin/ip addr`; + foreach (@nets) + { + my @elems = split /\s+/; + unless (/^\s*inet\s/) + { + next; + } + (my $curnet, my $maskbits) = split /\//, $elems[2]; + my $curmask = 2**$maskbits - 1 << (32 - $maskbits); + my $curn = unpack("N", inet_aton($curnet)); + if (($noden & $curmask) == ($curn & $curmask)) + { + return inet_aton($curnet); + } + } + return undef; +}