| #!/usr/bin/perl -w |
| |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| ############################################################################## |
| # $Id: xCAT.pm 1953 2008-12-12 14:23:17Z arkurth $ |
| ############################################################################## |
| |
| =head1 NAME |
| |
| VCL::Provisioning::xCAT - VCL module to support the xCAT provisioning engine |
| |
| =head1 SYNOPSIS |
| |
| Needs to be written |
| |
| =head1 DESCRIPTION |
| |
| This module provides VCL support for xCAT (Extreme Cluster Administration |
| Toolkit). xCAT is a scalable distributed computing management and |
| provisioning tool that provides a unified interface for hardware control, |
| discovery, and OS diskful/diskfree deployment. |
| http://xcat.sourceforge.net |
| |
| =cut |
| |
| ############################################################################## |
| package VCL::Module::Provisioning::xCAT; |
| |
| # Specify the lib path using FindBin |
| use FindBin; |
| use lib "$FindBin::Bin/../../.."; |
| |
| # Configure inheritance |
| use base qw(VCL::Module::Provisioning); |
| |
| # Specify the version of this module |
| our $VERSION = '2.00'; |
| |
| # Specify the version of Perl to use |
| use 5.008000; |
| |
| use strict; |
| use warnings; |
| use diagnostics; |
| |
| use VCL::utils; |
| use Fcntl qw(:DEFAULT :flock); |
| use File::Copy; |
| |
| ############################################################################## |
| |
| =head1 CLASS ATTRIBUTES |
| |
| =cut |
| |
| =head2 $XCAT_ROOT |
| |
| Data type : scalar |
| Description : $XCAT_ROOT stores the location of the xCAT binary files. xCAT |
| should set the XCATROOT environment variable. This is used if |
| it is set. If XCATROOT is not set, /opt/xcat is used. |
| |
| =cut |
| |
| # Class attributes to store xCAT configuration details |
| my $XCAT_ROOT; |
| |
| ############################################################################## |
| |
| =head1 OBJECT METHODS |
| |
| =cut |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 initialize |
| |
| Parameters : |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub initialize { |
| my $self = shift; |
| |
| # Check the XCAT_ROOT environment variable, it should be defined |
| if (defined($ENV{XCATROOT}) && $ENV{XCATROOT}) { |
| $XCAT_ROOT = $ENV{XCATROOT}; |
| } |
| elsif (defined($ENV{XCATROOT})) { |
| notify($ERRORS{'WARNING'}, 0, "XCATROOT environment variable is not defined, using /opt/xcat"); |
| $XCAT_ROOT = '/opt/xcat'; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "XCATROOT environment variable is not set, using /opt/xcat"); |
| $XCAT_ROOT = '/opt/xcat'; |
| } |
| |
| # Remove trailing / from $XCAT_ROOT if exists |
| $XCAT_ROOT =~ s/\/$//; |
| |
| # Make sure the xCAT root path is valid |
| if (!-d $XCAT_ROOT) { |
| notify($ERRORS{'WARNING'}, 0, "unable to initialize xCAT module, $XCAT_ROOT directory does not exist"); |
| return 0; |
| } |
| |
| # Check to make sure one of the expected executables is where it should be |
| if (!-x "$XCAT_ROOT/bin/rpower") { |
| notify($ERRORS{'WARNING'}, 0, "unable to initialize xCAT module, expected executable was not found: $XCAT_ROOT/bin/rpower"); |
| return 0; |
| } |
| notify($ERRORS{'DEBUG'}, 0, "xCAT root path found: $XCAT_ROOT"); |
| |
| notify($ERRORS{'DEBUG'}, 0, "xCAT module initialized"); |
| return 1; |
| } ## end sub initialize |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 load |
| |
| Parameters : hash |
| Returns : 1(success) or 0(failure) |
| Description : loads node with provided image |
| |
| =cut |
| |
| sub load { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Get the data |
| my $reservation_id = $self->data->get_reservation_id(); |
| my $image_name = $self->data->get_image_name(); |
| my $image_os_name = $self->data->get_image_os_name(); |
| my $image_project = $self->data->get_image_project(); |
| my $image_reload_time = $self->data->get_image_reload_time(); |
| my $imagemeta_postoption = $self->data->get_imagemeta_postoption(); |
| my $image_architecture = $self->data->get_image_architecture(); |
| my $computer_id = $self->data->get_computer_id(); |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| my $computer_ip_address = $self->data->get_computer_ip_address(); |
| |
| notify($ERRORS{'OK'}, 0, "nodename not set") |
| if (!defined($computer_node_name)); |
| notify($ERRORS{'OK'}, 0, "imagename not set") |
| if (!defined($image_name)); |
| notify($ERRORS{'OK'}, 0, "project not set") |
| if (!defined($image_project)); |
| notify($ERRORS{'OK'}, 0, "estimated reload time not set") |
| if (!defined($image_reload_time)); |
| notify($ERRORS{'OK'}, 0, "osname not set") |
| if (!defined($image_os_name)); |
| notify($ERRORS{'OK'}, 0, "computerid not set") |
| if (!defined($computer_id)); |
| notify($ERRORS{'OK'}, 0, "reservationid not set") |
| if (!defined($reservation_id)); |
| notify($ERRORS{'OK'}, 0, "architecture not set") |
| if (!defined($image_architecture)); |
| |
| # Initialize some timer variables |
| # Do this here in case goto passes over the declaration |
| my $sshd_start_time; |
| my $sshd_end_time; |
| |
| insertloadlog($reservation_id, $computer_id, "startload", "$computer_node_name $image_name"); |
| |
| #make sure the following services are running on management node |
| # dhcpd named xcatd |
| # start them if they are not actively running |
| $image_project = "vcl" if (!defined($image_project)); |
| |
| $image_architecture = "x86" if (!defined($image_architecture)); |
| |
| # Run xCAT's assign2project utility |
| if (_assign2project($computer_node_name, $image_project)) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name _assign2project return successful"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "$computer_node_name could not _assign2project to $image_project"); |
| return 0; |
| } |
| |
| # Make sure dhcpd is started on management node |
| if (!(_checknstartservice("dhcpd"))) { |
| notify($ERRORS{'CRITICAL'}, 0, "dhcpd is not running or failed to restart"); |
| } |
| |
| # Make sure named is started on management node |
| if (!(_checknstartservice("named"))) { |
| notify($ERRORS{'CRITICAL'}, 0, "named is not running or failed to restart"); |
| } |
| |
| # Make sure xcatd is started on management node |
| if (!(_checknstartservice("xcatd"))) { |
| notify($ERRORS{'CRITICAL'}, 0, "xcatd is not running or failed to restart"); |
| } |
| |
| # Make sure atftpd is started on management node |
| if (!(_checknstartservice("atftpd"))) { |
| notify($ERRORS{'CRITICAL'}, 0, "atftpd is not running or failed to restart"); |
| } |
| |
| # Insert a computerloadlog record and edit nodetype.tab |
| insertloadlog($reservation_id, $computer_id, "editnodetype", "updating nodetype file"); |
| if ($self->_edit_nodetype($computer_node_name, $image_name, $image_os_name, $image_architecture)) { |
| notify($ERRORS{'OK'}, 0, "nodetype updated for $computer_node_name with $image_name"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not edit nodetype for $computer_node_name with $image_name"); |
| } |
| |
| # Begin reinstallation using xCAT's rinstall |
| # Loop and continue checking |
| |
| # Set flags and counters |
| my $rinstall_attempts = 0; |
| my $rpower_fixes = 0; |
| my $bootstatus = 0; |
| my $wait_loops = 0; |
| my @status; |
| |
| # Check to see if management node throttle is configured |
| if ($THROTTLE) { |
| notify($ERRORS{'DEBUG'}, 0, "throttle is set to $THROTTLE"); |
| |
| my $lckloadfile = "/tmp/nodeloading.lockfile"; |
| notify($ERRORS{'DEBUG'}, 0, "attempting to open node loading lockfile for throttling: $lckloadfile"); |
| if (sysopen(SEM, $lckloadfile, O_RDONLY | O_CREAT)) { |
| notify($ERRORS{'DEBUG'}, 0, "opened lockfile, attempting to obtain lock"); |
| |
| if (flock(SEM, LOCK_EX)) { |
| notify($ERRORS{'DEBUG'}, 0, "obtained exclusive lock on $lckloadfile, checking for concurrent loads"); |
| my $maxload = 1; |
| while ($maxload) { |
| notify($ERRORS{'DEBUG'}, 0, "running 'nodeset all stat' to determine number of nodes currently being loaded"); |
| if (open(NODESET, "$XCAT_ROOT/bin/nodeset all stat \| grep install 2>&1 | ")) { |
| my @nodesetout = <NODESET>; |
| close(NODESET); |
| my $ld = @nodesetout; |
| notify($ERRORS{'DEBUG'}, 0, "current number of nodes loading: $ld"); |
| |
| if ($ld < $THROTTLE) { |
| notify($ERRORS{'OK'}, 0, "current nodes loading is less than throttle, ok to proceed"); |
| $maxload = 0; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "current nodes loading=$ld, throttle=$THROTTLE, must wait, sleeping for 10 seconds"); |
| sleep 10; |
| } |
| } ## end if (open(NODESET, "$XCAT_ROOT/bin/nodeset all stat \| grep install 2>&1 | "... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to run 'nodeset all stat' to determine number of nodes currently being loaded"); |
| } |
| } ## end while ($maxload) |
| } ## end if (flock(SEM, LOCK_EX)) |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to obtain exclusive lock on $lckloadfile"); |
| } |
| |
| notify($ERRORS{'OK'}, 0, "releasing exclusive lock on $lckloadfile, proceeding to install"); |
| close(SEM); |
| |
| } ## end if (sysopen(SEM, $lckloadfile, O_RDONLY | ... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to open node loading lockfile"); |
| } |
| |
| } ## end if ($THROTTLE) |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "throttle is NOT set"); |
| } |
| |
| XCATRINSTALL: |
| |
| # Reset sshd wait start time, used only for diagnostic purposes |
| $sshd_start_time = 0; |
| |
| # Make use of semaphore files to control the flow |
| # xCAT's rinstall does not handle locking of files |
| my $lckfile = "/tmp/rinstall.lockfile"; |
| notify($ERRORS{'DEBUG'}, 0, "attempting to open rinstall lockfile: $lckfile"); |
| if (sysopen(SEM, $lckfile, O_RDONLY | O_CREAT)) { |
| notify($ERRORS{'DEBUG'}, 0, "opened lockfile, attempting to obtain lock"); |
| |
| if (flock(SEM, LOCK_EX)) { |
| notify($ERRORS{'DEBUG'}, 0, "obtained exclusive lock on $lckfile"); |
| |
| # Safe to run rinstall command |
| insertloadlog($reservation_id, $computer_id, "rinstall", "starting install process"); |
| notify($ERRORS{'OK'}, 0, "executing rinstall $computer_node_name"); |
| if (open(RINSTALL, "$XCAT_ROOT/bin/rinstall $computer_node_name 2>&1 |")) { |
| $rinstall_attempts++; |
| notify($ERRORS{'OK'}, 0, "beginning rinstall attempt $rinstall_attempts"); |
| while (<RINSTALL>) { |
| chomp($_); |
| |
| #notify($ERRORS{'OK'},0,"$_"); |
| if ($_ =~ /not in bay/) { |
| notify($ERRORS{'WARNING'}, 0, "rpower not in bay issue, will attempt to correct, calling rinv"); |
| if (_fix_rpower($computer_node_name)) { |
| |
| #try xcatrinstall again |
| close(RINSTALL); |
| close(SEM); # remove lock |
| # loop control |
| if ($rpower_fixes < 10) { |
| $rpower_fixes++; |
| sleep 1; |
| goto XCATRINSTALL; |
| } |
| else { |
| notify($ERRORS{'CRITCAL'}, 0, "rpower failed $rpower_fixes times on $computer_node_name"); |
| return 0; |
| } |
| } ## end if (_fix_rpower($computer_node_name)) |
| } ## end if ($_ =~ /not in bay/) |
| if ($_ =~ /Invalid login|does not exist/) { |
| notify($ERRORS{'CRITCAL'}, 0, "failed to initate rinstall on $computer_node_name - $_"); |
| close(RINSTALL); |
| close(SEM); |
| insertloadlog($reservation_id, $computer_id, "failed", "failed to start load process on $computer_node_name"); |
| return 0; |
| } |
| |
| } #while RINSTALL |
| close(RINSTALL); |
| |
| notify($ERRORS{'OK'}, 0, "releasing exclusive lock on $lckfile"); |
| close(SEM); |
| } ## end if (open(RINSTALL, "$XCAT_ROOT/bin/rinstall $computer_node_name 2>&1 |"... |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not execute $XCAT_ROOT/bin/rinstall $computer_node_name $!"); |
| close(SEM); |
| return 0; |
| } |
| } ## end if (flock(SEM, LOCK_EX)) |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to obtain exclusive lock on $lckfile, error: $!, returning"); |
| return; |
| } |
| } ## end if (sysopen(SEM, $lckfile, O_RDONLY | O_CREAT... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to open node loading lockfile, error: $!, returning"); |
| return; |
| } |
| |
| # Check progress, locate MAC and IP address for this node, monitor /var/log/messages for communication from node |
| # dhcp req/ack, xcat calls, etc |
| my ($eth0MACaddress, $privateIP); |
| if (open(MACTAB, "$XCAT_ROOT/etc/mac.tab")) { |
| my @mactab = <MACTAB>; |
| close(MACTAB); |
| foreach my $line (@mactab) { |
| if ($line =~ /(^$computer_node_name(-eth[0-9])?)(\s+)([:0-9a-f]*)/) { |
| $eth0MACaddress = $4; |
| notify($ERRORS{'OK'}, 0, "MAC address for $computer_node_name collected $eth0MACaddress"); |
| } |
| } |
| } ## end if (open(MACTAB, "$XCAT_ROOT/etc/mac.tab")) |
| if (!defined($eth0MACaddress)) { |
| notify($ERRORS{'WARNING'}, 0, "MAC address not found for $computer_node_name , possible issue with regex"); |
| } |
| |
| #should also store/pull private address from the database |
| if (open(HOSTS, "/etc/hosts")) { |
| my @hosts = <HOSTS>; |
| close(HOSTS); |
| foreach my $line (@hosts) { |
| if ($line =~ /([0-9]*.[0-9]*.[0-9]*.[0-9]*)\s+($computer_node_name)/) { |
| $privateIP = $1; |
| notify($ERRORS{'OK'}, 0, "PrivateIP address for $computer_node_name collected $privateIP"); |
| last; |
| } |
| } |
| } ## end if (open(HOSTS, "/etc/hosts")) |
| if (!defined($privateIP)) { |
| notify($ERRORS{'WARNING'}, 0, "private IP address not found for $computer_node_name, possible issue with regex"); |
| } |
| my ($s1, $s2, $s3, $s4, $s5) = 0; |
| my $sloop = 0; |
| |
| #insertloadlog($reservation_id,$computer_id,"info","SUCCESS initiated install process"); |
| #sleep for boot process to happen takes anywhere from 60-90 seconds |
| notify($ERRORS{'OK'}, 0, "sleeping 65 to allow bootstrap of $computer_node_name"); |
| sleep 65; |
| my @TAILLOG; |
| my $t; |
| |
| if ($eth0MACaddress && $privateIP) { |
| @TAILLOG = 0; |
| $t = 0; |
| if (open(TAIL, "</var/log/messages")) { |
| seek TAIL, -1, 2; # |
| for (;;) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name ROUND 1 checks loop $sloop of 45"); |
| while (<TAIL>) { |
| if (!$s1) { |
| if ($_ =~ /dhcpd: DHCPDISCOVER from $eth0MACaddress/) { |
| $s1 = 1; |
| notify($ERRORS{'OK'}, 0, "$computer_node_name STAGE 1 set DHCPDISCOVER from $eth0MACaddress"); |
| insertloadlog($reservation_id, $computer_id, "xcatstage1", "SUCCESS stage1 detected dhcp request for node"); |
| } |
| } |
| if (!$s2) { |
| if ($_ =~ /dhcpd: DHCPACK on $privateIP to $eth0MACaddress/) { |
| $s2 = 1; |
| notify($ERRORS{'OK'}, 0, "$computer_node_name STAGE 2 set DHCPACK on $privateIP to $eth0MACaddress"); |
| insertloadlog($reservation_id, $computer_id, "xcatstage2", "SUCCESS stage2 detected dhcp ack for node"); |
| } |
| } |
| if (!$s3) { |
| if ($_ =~ /Serving \/tftpboot\/pxelinux.0 to $privateIP:/) { |
| $s3 = 1; |
| chomp($_); |
| notify($ERRORS{'OK'}, 0, "$computer_node_name STAGE 3 set $_"); |
| insertloadlog($reservation_id, $computer_id, "xcatstage3", "SUCCESS stage3 node received pxe"); |
| } |
| } |
| if (!$s4) { |
| if ($_ =~ /Serving \/tftpboot\/xcat\/(rhfc|linux_image|image|rhas.)\/x86\/install.gz to $privateIP:/) { |
| $s4 = 1; |
| chomp($_); |
| notify($ERRORS{'OK'}, 0, "$computer_node_name STAGE 4 set $_"); |
| insertloadlog($reservation_id, $computer_id, "xcatstage4", "SUCCESS stage4 node received pxe install instructions"); |
| } |
| } |
| |
| #stage5 is where images and rhas(KS) are different |
| if (!$s5) { |
| |
| #here we look for rpc.mountd |
| if ($_ =~ /authenticated mount request from $computer_node_name:(\d+) for/) { |
| $s5 = 1; |
| chomp($_); |
| notify($ERRORS{'OK'}, 0, "$computer_node_name STAGE 5 set $_"); |
| insertloadlog($reservation_id, $computer_id, "xcatstage5", "SUCCESS stage5 node started installing via partimage"); |
| } |
| |
| #in case we miss the above statement |
| if ($image_os_name =~ /^(rhel|rhfc|fc|esx)/) { |
| if ($_ =~ /xcat: xcatd: $computer_node_name installing/) { |
| $s5 = 1; |
| chomp($_); |
| notify($ERRORS{'OK'}, 0, "$computer_node_name STAGE 5 set $_"); |
| insertloadlog($reservation_id, $computer_id, "xcatstage5", "SUCCESS stage5 node started installing via kickstart"); |
| } |
| } |
| } ## end if (!$s5) |
| } #while |
| #either stages are set or we loop or we rinstall again |
| #check s5 and counter for loop control |
| if ($s5) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name ROUND1 stages are set proceeding to next round"); |
| close(TAIL); |
| goto ROUND2; |
| } |
| elsif ($sloop > 45) { |
| insertloadlog($reservation_id, $computer_id, "WARNING", "potential problem started $rinstall_attempts install attempt"); |
| |
| #hrmm this is taking too long |
| #have we been here before? if less than 3 attempts continue on the 3rd try fail |
| #whats the problem, chck known locations |
| # /tftpboot/xcat/image/x86 |
| # look for tmpl file (in does_image_exist routine) |
| # does the machine need to reboot, premission to reboot issue |
| if (_check_pxe_grub_files($image_name)) { |
| notify($ERRORS{'OK'}, 0, "checkpxe_grub_file checked"); |
| } |
| |
| if ($rinstall_attempts < 3) { |
| close(TAIL); |
| insertloadlog($reservation_id, $computer_id, "repeat", "starting install process"); |
| goto XCATRINSTALL; |
| } |
| else { |
| |
| #fail this one and let whoever called me get another machine |
| notify($ERRORS{'CRITICAL'}, 0, "rinstall made $rinstall_attempts in ROUND1 on $computer_node_name with no success, admin needs to check it out"); |
| insertloadlog($reservation_id, $computer_id, "failed", "FAILED problem made $rinstall_attempts install attempts failing reservation"); |
| if (_nodeset_option($computer_node_name, "boot")) { |
| notify($ERRORS{'OK'}, 0, "due to failure reseting state of blade to boot"); |
| } |
| close(TAIL); |
| return 0; |
| } ## end else [ if ($rinstall_attempts < 3) |
| } ## end elsif ($sloop > 45) [ if ($s5) |
| else { |
| |
| #keep checking the messages log |
| $sloop++; |
| sleep 7; |
| seek TAIL, 0, 1; |
| } |
| } #for loop |
| } #if Tail |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could open /var/log/messages to $!"); |
| } |
| } ## end if ($eth0MACaddress && $privateIP) |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "eth0MACaddress $eth0MACaddress && privateIP $privateIP are not set not able to use these checks"); |
| insertloadlog($reservation_id, $computer_id, "failed", "FAILED could not locate private IP and MAC addresses in XCAT files failing reservation"); |
| return 0; |
| } |
| |
| ROUND2: |
| |
| #begin second round of checks reset $sX |
| ($s1, $s2, $s3, $s4, $s5) = 0; |
| $sloop = 0; |
| |
| # start time for loading |
| my $R2starttime = convert_to_epoch_seconds(); |
| |
| #during loading we need to wait based on some precentage of the estimated reload time (50%?) |
| #times range from 4-10 minutes perhaps longer for a large image |
| my $TM2waittime = int($image_reload_time / 2); |
| insertloadlog($reservation_id, $computer_id, "xcatround2", "starting ROUND2 checks - waiting for boot flag"); |
| |
| notify($ERRORS{'OK'}, 0, "Round 2 TM2waittime set to $TM2waittime on $computer_node_name"); |
| if (open(TAIL, "</var/log/messages")) { |
| seek TAIL, -1, 2; |
| my $gettingclose = 0; |
| for (;;) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name round2 log checks 30sec loop count is $sloop of $image_reload_time TM2waittime= $TM2waittime"); |
| while (<TAIL>) { |
| if (!$s1) { |
| if ($_ =~ /xcat: xcatd: set boot request from $computer_node_name/) { |
| |
| insertloadlog($reservation_id, $computer_id, "bootstate", "node in boot state completed imaging process - proceeding to next round"); |
| $s1 = 1; |
| notify($ERRORS{'OK'}, 0, "Round 2 STAGE 1 set $computer_node_name in boot state"); |
| } |
| |
| #is it even near completion only checking rhel installs |
| #not really useful for linux_images |
| if ($image_os_name =~ /^(rhel|rhfc|fc|esx)/) { |
| if (!$gettingclose) { |
| if ($_ =~ /rpc.mountd: authenticated mount request from $computer_node_name:(\d+) for \/install\/post/) { |
| $gettingclose = 1; |
| notify($ERRORS{'OK'}, 0, "Round 2 STAGE 1 install nearing completion on node $computer_node_name"); |
| } |
| } |
| else { |
| if (!$s4) { |
| if ($sloop == $image_reload_time) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name Round 2 getting close, loop eq $image_reload_time, substracting 6 from loop count"); |
| $sloop = ($sloop - 8); |
| $s4 = 1; #loop control, don't set this we loop forever |
| notify($ERRORS{'WARNING'}, 0, "ert estimated reload time may be too low\n $computer_node_name\nimagename $image_name\n current ert = $image_reload_time"); |
| } |
| } |
| } ## end else [ if (!$gettingclose) |
| } ## end if ($image_os_name =~ /^(rhel|rhfc|fc|esx)/) |
| } ## end if (!$s1) |
| } #while |
| if ($s1) { |
| |
| #good, move on |
| close(TAIL); |
| goto ROUND3; |
| } |
| else { |
| if ($sloop > $image_reload_time) { |
| notify($ERRORS{'OK'}, 0, "exceeded TM2waittime of $TM2waittime minutes sloop= $sloop ert= $image_reload_time"); |
| |
| # check delta from when we started actual loading till now |
| my $rtime = convert_to_epoch_seconds(); |
| my $delta = $rtime - $R2starttime; |
| if ($delta < ($image_reload_time * 60)) { |
| |
| #ok delta is actually less then ert, we don't need to stop it yet. |
| notify($ERRORS{'OK'}, 0, "loading delta is less than ert, not stopping yet delta is $delta/60 "); |
| sleep 35; |
| $sloop = ($sloop - 8); #decrement loop control |
| seek TAIL, 0, 1; |
| |
| } |
| elsif ($rinstall_attempts < 2) { |
| notify($ERRORS{'WARNING'}, 0, "starting rinstall again"); |
| insertloadlog($reservation_id, $computer_id, "WARNING", "potential problem restarting rinstall current attemp $rinstall_attempts"); |
| close(TAIL); |
| insertloadlog($reservation_id, $computer_id, "repeat", "starting install process"); |
| goto XCATRINSTALL; |
| } |
| else { |
| |
| #fail this one and let whoever called me get another machine |
| notify($ERRORS{'CRITICAL'}, 0, "rinstall made $rinstall_attempts in ROUND2 on $computer_node_name with no success, admin needs to check it out"); |
| insertloadlog($reservation_id, $computer_id, "failed", "rinstall made $rinstall_attempts failing request"); |
| close(TAIL); |
| return 0; |
| } |
| } ## end if ($sloop > $image_reload_time) |
| else { |
| sleep 35; |
| $sloop++; #loop control |
| insertloadlog($reservation_id, $computer_id, "info", "node in load process waiting for signal"); |
| seek TAIL, 0, 1; |
| |
| #goto TAILMESSAGES2; |
| } |
| } ## end else [ if ($s1) |
| } #for |
| } ## end if (open(TAIL, "</var/log/messages")) |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could open /var/log/messages to $!"); |
| return 0; |
| } |
| |
| ROUND3: |
| |
| my $nodeset_status; |
| |
| # Round 3 checks, machine has been installed we wait here for boot process which could include sysprep |
| # we are checking for the boot state in the OS status |
| insertloadlog($reservation_id, $computer_id, "xcatround3", "starting round 3 checks - finishing post configuration"); |
| $wait_loops = 0; |
| while (!$bootstatus) { |
| my $nodeset_status = _nodeset($computer_node_name); |
| |
| if ($nodeset_status =~ /boot/) { |
| $bootstatus = 1; |
| notify($ERRORS{'OK'}, 0, "$computer_node_name has been reinstalled with $image_name"); |
| notify($ERRORS{'OK'}, 0, "xcat has set the boot flag"); |
| if ($image_os_name =~ /win|wxp|2003/) { |
| notify($ERRORS{'OK'}, 0, "waiting 3 minutes to allow OS to reboot and initialize machine"); |
| sleep 180; |
| } |
| |
| #elsif($osname =~ /^(rhel|rh3image|fc|rhfc|rh4image)/){ |
| elsif ($image_os_name =~ /^(rh[0-9]image|rhel[0-9]|fc[0-9]image|rhfc[0-9]|rhas[0-9]|esx[0-9]+)/) { |
| notify($ERRORS{'OK'}, 0, "waiting 65 sec to allow OS to reboot and initialize machine"); |
| sleep 65; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "waiting 3 minutes to allow OS to reboot and initialize machine"); |
| sleep 180; |
| } |
| my ($readycount, $ready) = 0; |
| READYFLAG: |
| |
| #check /var/log/messages file for READY |
| |
| if (open(TAIL, "</var/log/messages")) { |
| seek TAIL, -1, 2; |
| for (;;) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name checking for READY FLAG loop count is $readycount of 10"); |
| while (<TAIL>) { |
| if ($_ =~ /READY|ready|Starting firstboot: succeeded/) { |
| $ready = 1 if ($_ =~ /$computer_node_name/); |
| } |
| if ($image_os_name =~ /^(rh|fc|esx)/) { |
| if ($_ =~ /$computer_node_name|$computer_node_name kernel/) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name booting up"); |
| sleep 5; |
| $ready = 1; |
| close(TAIL); |
| goto SSHDATTEMPT; |
| } |
| } |
| } #while |
| |
| if ($readycount > 10) { |
| notify($ERRORS{'OK'}, 0, "taking longer than expected, readycount==$readycount moving to next set of checks"); |
| $ready = 1; |
| close(TAIL); |
| goto SSHDATTEMPT; |
| } |
| if ($readycount > 2) { |
| |
| #check ssh status just in case we missed the flag |
| my $sshd = _sshd_status($computer_node_name, $image_name); |
| if ($sshd eq "on") { |
| $ready = 1; |
| notify($ERRORS{'OK'}, 0, "we may have missed start flag going next stage"); |
| close(TAIL); |
| goto SSHDATTEMPT; |
| } |
| } ## end if ($readycount > 2) |
| if (!$ready) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name not ready yet, sleeping for 40 seconds"); |
| sleep 40; |
| seek TAIL, 0, 1; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "/var/log/messages reports $computer_node_name is ready"); |
| insertloadlog($reservation_id, $computer_id, "xcatREADY", "detected ready signal from node - proceeding"); |
| close(TAIL); |
| goto SSHDATTEMPT; |
| } |
| |
| #placing out side of if statements for loop control |
| $readycount++; |
| } #for |
| } ## end if (open(TAIL, "</var/log/messages")) |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not open messages at READYFLAG $!"); |
| } |
| notify($ERRORS{'OK'}, 0, "proceeding for sync sshd active"); |
| } ## end if ($nodeset_status =~ /boot/) |
| else { |
| |
| # check for strange states |
| |
| } |
| } ## end while (!$bootstatus) |
| |
| # we need to wait for sshd to become active |
| my $sshd_attempts = 0; |
| SSHDATTEMPT: |
| my $sshdstatus = 0; |
| $wait_loops = 0; |
| $sshd_attempts++; |
| my $sshd_status = "off"; |
| |
| # Set the sshd start time to now if it hasn't been set already |
| # This is used to report how long sshd took to become active |
| $sshd_start_time = time() if !$sshd_start_time; |
| |
| while (!$sshdstatus) { |
| my $sshd_status = _sshd_status($computer_node_name, $image_name); |
| if ($sshd_status eq "on") { |
| |
| # Set the sshd end time to now to capture how long it took sshd to become active |
| $sshd_end_time = time(); |
| my $sshd_duration = $sshd_end_time - $sshd_start_time; |
| |
| $sshdstatus = 1; |
| notify($ERRORS{'OK'}, 0, "$computer_node_name sshd has become active, took $sshd_duration secs, ok to proceed to sync ssh keys"); |
| insertloadlog($reservation_id, $computer_id, "info", "synchronizing keys"); |
| } ## end if ($sshd_status eq "on") |
| else { |
| |
| #either sshd is off or N/A, we wait |
| if ($wait_loops >= 7) { |
| if ($sshd_attempts < 3) { |
| goto SSHDATTEMPT; |
| } |
| else { |
| |
| # Waited long enough for sshd to become active |
| |
| # Set the sshd end time to now to capture how long process waited for sshd to become active |
| $sshd_end_time = time(); |
| my $sshd_duration = $sshd_end_time - $sshd_start_time; |
| |
| notify($ERRORS{'WARNING'}, 0, "$computer_node_name waited acceptable amount of time for sshd to become active, $sshd_duration secs"); |
| |
| #need to check power, maybe reboot it. for now fail it |
| #try to reinstall it once |
| if ($rinstall_attempts < 2) { |
| notify($ERRORS{'WARNING'}, 0, "$computer_node_name starting rinstall again"); |
| insertloadlog($reservation_id, $computer_id, "repeat", "starting install process"); |
| close(TAIL); |
| goto XCATRINSTALL; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "$computer_node_name: sshd never became active after 2 rinstall attempts"); |
| insertloadlog($reservation_id, $computer_id, "failed", "exceeded maximum install attempts"); |
| return 0; |
| } |
| } ## end else [ if ($sshd_attempts < 3) |
| } ## end if ($wait_loops >= 7) |
| else { |
| $wait_loops++; |
| |
| # to give post config a chance |
| notify($ERRORS{'OK'}, 0, "going to sleep 15 seconds, waiting for post config to finish"); |
| sleep 15; |
| } |
| } # else |
| } #while |
| |
| # Clear ssh public keys from /root/.ssh/known_hosts |
| my $known_hosts = "/root/.ssh/known_hosts"; |
| my @file; |
| if (open(FILE, $known_hosts)) { |
| @file = <FILE>; |
| close FILE; |
| |
| foreach my $line (@file) { |
| if ($line =~ s/$computer_node_name.*\n//) { |
| notify($ERRORS{'OK'}, 0, "removing $computer_node_name ssh public key from $known_hosts"); |
| } |
| } |
| |
| if (open(FILE, ">$known_hosts")) { |
| print FILE @file; |
| close FILE; |
| } |
| } ## end if (open(FILE, $known_hosts)) |
| else { |
| notify($ERRORS{'OK'}, 0, "could not open $known_hosts for editing the $computer_node_name public ssh key"); |
| } |
| |
| # Synchronize ssh keys using xCAT's makesshgkh |
| my $makessygkh_attempts = 0; |
| MAKESSH: |
| notify($ERRORS{'OK'}, 0, " resting 1sec before executing makesshgkh"); |
| sleep 1; |
| if (open(MAKESSHGKH, "$XCAT_ROOT/sbin/makesshgkh $computer_node_name |")) { |
| $makessygkh_attempts++; |
| notify($ERRORS{'OK'}, 0, " makesshgkh attempt $makessygkh_attempts "); |
| while (<MAKESSHGKH>) { |
| chomp($_); |
| if ($_ =~ /Scanning keys/) { |
| notify($ERRORS{'OK'}, 0, "$_"); |
| } |
| } |
| close MAKESSHGKH; |
| my $keysync = 0; |
| my $keysynccheck = 0; |
| |
| while (!$keysync) { |
| $keysynccheck++; |
| my $sshd = _sshd_status($computer_node_name, $image_name); |
| if ($sshd =~ /on/) { |
| $keysync = 1; |
| notify($ERRORS{'OK'}, 0, "keys synced"); |
| insertloadlog($reservation_id, $computer_id, "info", "SUCCESS keys synchronized"); |
| last; |
| } |
| if ($keysynccheck > 3) { |
| if ($makessygkh_attempts < 1) { |
| notify($ERRORS{'OK'}, 0, "keysynccheck exceeded 5 minutes, there might be a problem running makesshgkh again"); |
| goto MAKESSH; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "makesshgkh exceeded 2 attempts to create new ssh keys there appears to be a problem with $computer_node_name moving on"); |
| |
| #move on- |
| $keysync = 1; |
| last; |
| } |
| } ## end if ($keysynccheck > 3) |
| notify($ERRORS{'OK'}, 0, "waiting for ssh keys to be updated"); |
| sleep 5; |
| } ## end while (!$keysync) |
| } ## end if (open(MAKESSHGKH, "$XCAT_ROOT/sbin/makesshgkh $computer_node_name |"... |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not execute $XCAT_ROOT/sbin/makesshgkh $computer_node_name $!"); |
| } |
| |
| # Perform post load tasks |
| |
| # Windows specific routines |
| if ($image_os_name =~ /winxp|wxp|win2003/) { |
| |
| insertloadlog($reservation_id, $computer_id, "info", "randomizing system level passwords"); |
| |
| #change passwords for root and administrator account |
| #skip changing root password for imageprep loads |
| if (changewindowspasswd($computer_node_name, "root")) { |
| notify($ERRORS{'OK'}, 0, "Successfully changed password, account $computer_node_name,root"); |
| } |
| |
| if (changewindowspasswd($computer_node_name, "administrator")) { |
| notify($ERRORS{'OK'}, 0, "Successfully changed password, account $computer_node_name,administrator"); |
| } |
| |
| #disable remote desktop port |
| if (remotedesktopport($computer_node_name, "DISABLE")) { |
| notify($ERRORS{'OK'}, 0, "remote desktop disabled on $computer_node_name"); |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "remote desktop not disable on $computer_node_name"); |
| } |
| |
| #due to sysprep sshd is set to manual start |
| if (_set_sshd_startmode($computer_node_name, "auto")) { |
| notify($ERRORS{'OK'}, 0, "successfully set sshd service on $computer_node_name to start auto"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to set sshd service on $computer_node_name to start auto"); |
| } |
| |
| #check for root logged in on console and then logoff |
| notify($ERRORS{'OK'}, 0, "checking for any console users $computer_node_name"); |
| |
| my @QA = run_ssh_command($computer_node_name, $IDENTITY_wxp, "cmd /c qwinsta.exe", "root"); |
| foreach my $r (@{$QA[1]}) { |
| if ($r =~ /([>]?)([-a-zA-Z0-9]*)\s+([a-zA-Z0-9]*)\s+ ([0-9]*)\s+([a-zA-Z]*)/) { |
| my $state = $5; |
| my $session = $2; |
| my $user = $3; |
| if ($5 =~ /Active/) { |
| notify($ERRORS{'OK'}, 0, "detected $user on $session still logged on $computer_node_name $r, sleeping 7 before logging off"); |
| sleep 7; |
| my @LF = run_ssh_command($computer_node_name, $IDENTITY_wxp, "cmd /c logoff.exe $session"); |
| foreach my $l (@{$LF[1]}) { |
| notify($ERRORS{'OK'}, 0, "output from attempt to logoff $user on $session"); |
| } |
| |
| } |
| } ## end if ($r =~ /([>]?)([-a-zA-Z0-9]*)\s+([a-zA-Z0-9]*)\s+ ([0-9]*)\s+([a-zA-Z]*)/) |
| } ## end foreach my $r (@{$QA[1]}) |
| |
| #reboot the box based on options |
| if ($imagemeta_postoption =~ /reboot/i) { |
| my $rebooted = 1; |
| my $reboot_wait_count = 0; |
| my @retarray; |
| while ($rebooted) { |
| if ($reboot_wait_count > 55) { |
| notify($ERRORS{'CRITICAL'}, 0, "waited $reboot_wait_count on reboot after auto_create_image on $computer_node_name"); |
| $retarray[1] = "waited $reboot_wait_count on reboot after netdom on $computer_node_name"; |
| return @retarray; |
| } |
| notify($ERRORS{'OK'}, 0, "$computer_node_name not completed reboot sleeping for 25"); |
| sleep 25; |
| if (_pping($computer_node_name)) { |
| |
| #it pingable check if sshd is open |
| notify($ERRORS{'OK'}, 0, "$computer_node_name is pingable, checking sshd port"); |
| my $sshd = _sshd_status($computer_node_name, $image_name); |
| if ($sshd =~ /on/) { |
| $rebooted = 0; |
| notify($ERRORS{'OK'}, 0, "$computer_node_name sshd is open"); |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name sshd NOT open yet,sleep 5"); |
| sleep 5; |
| } |
| } #_pping |
| $reboot_wait_count++; |
| |
| } #while |
| } #reboot |
| |
| #win2003 only - need to set private adapter to static without a gateway |
| # win2003 and probably vista zero out one gateway and we only need a gateway on the public adapter |
| # so we need to remove the one on the private side |
| # downside - we need to reset it to dhcp before making an image..... |
| |
| if ($image_os_name =~ /^(win2003)/) { |
| insertloadlog($reservation_id, $computer_id, "info", "detected OS which requires network gateway modification"); |
| notify($ERRORS{'OK'}, 0, "detected win2003 OS, proceeding to change private adapter to static from dhcp on $computer_node_name"); |
| my %ip; |
| my $myadapter; |
| my @ipconfig = run_ssh_command($computer_node_name, $IDENTITY_wxp, "ipconfig -all", "root"); |
| |
| # build hash of needed info and set the correct private adapter. |
| foreach my $a (@{$ipconfig[1]}) { |
| $myadapter = $1 if ($a =~ /Ethernet adapter (.*):/); |
| $ip{$myadapter}{"private"} = 1 |
| if ($a =~ /IP Address([\s.]*): $privateIP/); |
| $ip{$myadapter}{"subnetmask"} = $2 |
| if ($a =~ /Subnet Mask([\s.]*): ([.0-9]*)/); |
| } |
| |
| my $privateadapter; |
| my $subnetmask; |
| |
| foreach my $key (keys %ip) { |
| if (defined($ip{$key}{private})) { |
| if ($ip{$key}{private}) { |
| $privateadapter = "\"$key\""; |
| $subnetmask = $ip{$key}{subnetmask}; |
| } |
| } |
| } |
| |
| notify($ERRORS{'OK'}, 0, "attempted to convert private adapter on $computer_node_name to static with no gateway"); |
| |
| #not using run_ssh_command here |
| if (open(NETSH, "/usr/bin/ssh -x -i $IDENTITY_wxp $computer_node_name \"netsh interface ip set address name=\\\"$privateadapter\\\" source=static addr=$privateIP mask=$subnetmask\" & 2>&1 |")) { |
| |
| #losing connection |
| my $go = 1; |
| while ($go) { |
| |
| #print "hi\n"; |
| sleep 4; |
| if (open(PS, "ps -ef |")) { |
| my @ps = <PS>; |
| close(PS); |
| sleep 4; |
| foreach my $p (@ps) { |
| if ($p =~ /$computer_node_name netsh interface/) { |
| if ($p =~ /(root)\s+([0-9]*)/) { |
| if (open(KILLIT, "kill -9 $2 |")) { |
| close(KILLIT); |
| close(NETSH); |
| notify($ERRORS{'OK'}, 0, "killing ssh $computer_node_name netsh process"); |
| } |
| } |
| } |
| } ## end foreach my $p (@ps) |
| } ## end if (open(PS, "ps -ef |")) |
| |
| $go = 0; |
| } ## end while ($go) |
| } ## end if (open(NETSH, "/usr/bin/ssh -x -i $IDENTITY_wxp $computer_node_name \"netsh interface ip set address name=\\\"$privateadapter\\\" source=static addr=$privateIP mask=$subnetmask\" & 2>&1 |"... |
| |
| #make sure it came back |
| if (_sshd_status($computer_node_name, $image_name)) { |
| notify($ERRORS{'OK'}, 0, "successful $computer_node_name is accessible after static assignment"); |
| insertloadlog($reservation_id, $computer_id, "info", "SUCCESS network gateway modification successful"); |
| } |
| else { |
| |
| } |
| |
| #disable NetBios |
| notify($ERRORS{'OK'}, 0, "attempted to convert private adapter on $computer_node_name to static with no gateway"); |
| my $path1 = "$TOOLS/disablenetbios.vbs"; |
| my $path2 = "$computer_node_name:disablenetbios.vbs"; |
| if (run_scp_command($path1, $path2, $IDENTITY_wxp)) { |
| notify($ERRORS{'DEBUG'}, 0, "copied $path1 to $path2"); |
| my @DNBIOS = run_ssh_command($computer_node_name, $IDENTITY_wxp, "cscript.exe //Nologo disablenetbios.vbs", "root"); |
| foreach my $l (@{$DNBIOS[1]}) { |
| if ($l =~ /denied|socket/) { |
| notify($ERRORS{'WARNING'}, 0, "failed to disablenetbios.vbs @{ $DNBIOS[1] }"); |
| } |
| } |
| |
| } ## end if (run_scp_command($path1, $path2, $IDENTITY_wxp... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "run_scp_command failed to copy $path1 to $path2"); |
| } |
| |
| } ## end if ($image_os_name =~ /^(win2003)/) |
| } ## end if ($image_os_name =~ /winxp|wxp|win2003/) |
| |
| # Linux post-load tasks |
| elsif ($image_os_name =~ /^(rh[0-9]image|rhel[0-9]|fc[0-9]image|rhfc[0-9]|rhas[0-9]|esx[0-9]+)/) { |
| |
| #linux specfic routines |
| #FIXME move to generic post options on per image basis |
| if ($image_os_name =~ /^(esx[0-9]*)/) { |
| |
| #esx specific post |
| my $cmdstring = "/usr/sbin/esxcfg-vswitch -a vSwitch1;/usr/sbin/esxcfg-vswitch -L vmnic1 vSwitch1;/usr/sbin/esxcfg-vswitch -A \"Virtual Machine Public Network\" vSwitch1"; |
| |
| my @sshd = run_ssh_command($computer_node_name, $IDENTITY_bladerhel, $cmdstring, "root"); |
| foreach my $l (@{$sshd[1]}) { |
| |
| #any response is a potential problem |
| notify($ERRORS{'DEBUG'}, 0, "esxcfg-vswitch output: $l"); |
| } |
| |
| #restart mgmt-vmware |
| sleep(8); # sleep briefly before attemping to restart |
| # restart needs to include "&" for some reason it doesn't return but completes - dunno? |
| @sshd = run_ssh_command($computer_node_name, $IDENTITY_bladerhel, "/etc/init.d/mgmt-vmware restart &", "root"); |
| foreach my $l (@sshd) { |
| if ($l =~ /failed/i) { |
| notify($ERRORS{'WARNING'}, 0, "failed to restart mgmt-vmware @sshd"); |
| return 0; |
| } |
| } |
| } ## end if ($image_os_name =~ /^(esx[0-9]*)/) |
| #FIXME - could be an issue for esx servers |
| if (changelinuxpassword($computer_node_name, "root")) { |
| notify($ERRORS{'OK'}, 0, "successfully changed root password on $computer_node_name"); |
| |
| #insertloadlog($reservation_id, $computer_id, "info", "SUCCESS randomized roots password"); |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "failed to edit root password on $computer_node_name"); |
| } |
| |
| #disable ext_sshd |
| my @stopsshd = run_ssh_command($computer_node_name, $IDENTITY_bladerhel, "/etc/init.d/ext_sshd stop", "root"); |
| foreach my $l (@{$stopsshd[1]}) { |
| if ($l =~ /Stopping ext_sshd/) { |
| notify($ERRORS{'OK'}, 0, "ext sshd stopped on $computer_node_name"); |
| last; |
| } |
| } |
| |
| #if an image, clear wtmp and krb token files |
| # FIXME - move to createimage |
| if ($image_os_name =~ /^(rh[0-9]image|rhel[0-9]|fc[0-9]image|rhfc[0-9]|rhas[0-9]|esx[0-9]+)/) { |
| my @cleartmp = run_ssh_command($computer_node_name, $IDENTITY_bladerhel, "/usr/sbin/tmpwatch -f 0 /tmp; /bin/cp /dev/null /var/log/wtmp", "root"); |
| foreach my $l (@{$cleartmp[1]}) { |
| notify($ERRORS{'DEBUG'}, 0, "output from cleartmp post load $computer_node_name $l"); |
| } |
| } |
| |
| # clear external_sshd file of any AllowUsers string |
| my $path1 = "$computer_node_name:/etc/ssh/external_sshd_config"; |
| my $path2 = "/tmp/$computer_node_name.sshd"; |
| if (run_scp_command($path1, $path2, $IDENTITY_bladerhel)) { |
| notify($ERRORS{'DEBUG'}, 0, "scp success retrieved $path1"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve $path1"); |
| } |
| #remove from sshd |
| if (open(SSHDCFG, "/tmp/$computer_node_name.sshd")) { |
| @file = <SSHDCFG>; |
| close SSHDCFG; |
| foreach my $l (@file) { |
| $l = "" if ($l =~ /AllowUsers/); |
| } |
| if (open(SCP, ">/tmp/$computer_node_name.sshd")) { |
| print SCP @file; |
| close SCP; |
| } |
| undef $path1; |
| undef $path2; |
| $path1 = "/tmp/$computer_node_name.sshd"; |
| $path2 = "$computer_node_name:/etc/ssh/external_sshd_config"; |
| if (run_scp_command($path1, $path2, $IDENTITY_bladerhel)) { |
| notify($ERRORS{'DEBUG'}, 0, "scp success copied $path1 to $path2"); |
| unlink $path1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to copy $path1 to $path2"); |
| } |
| } ## end if (open(SSHDCFG, "/tmp/$computer_node_name.sshd"... |
| |
| |
| } ## end elsif ($image_os_name =~ /^(rh[0-9]image|rhel[0-9]|fc[0-9]image|rhfc[0-9]|rhas[0-9]|esx[0-9]+)/) [ if ($image_os_name =~ /winxp|wxp|win2003/) |
| |
| # IP configuration |
| if ($IPCONFIGURATION ne "manualDHCP") { |
| insertloadlog($reservation_id, $computer_id, "info", "detected change required in IP address configuration on node"); |
| |
| #not default setting |
| if ($IPCONFIGURATION eq "dynamicDHCP") { |
| my $assignedIPaddress = getdynamicaddress($computer_node_name, $image_os_name); |
| if ($assignedIPaddress) { |
| |
| #update computer table |
| if (update_computer_address($computer_id, $assignedIPaddress)) { |
| notify($ERRORS{'OK'}, 0, "dynamic address collected $assignedIPaddress -- updated computer table"); |
| insertloadlog($reservation_id, $computer_id, "dynamicDHCPaddress", "SUCCESS collected dynamicDHCP address"); |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "failed to update dynamic address $assignedIPaddress for$computer_id $computer_node_name "); |
| insertloadlog($reservation_id, $computer_id, "dynamicDHCPaddress", "FAILED to update dynamicDHCP address failing reservation"); |
| return 0; |
| } |
| } ## end if ($assignedIPaddress) |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not fetch dynamic address from $computer_node_name $image_name"); |
| insertloadlog($reservation_id, $computer_id, "dynamicDHCPaddress", "FAILED to collected dynamicDHCP address failing reservation"); |
| return 0; |
| } |
| } ## end if ($IPCONFIGURATION eq "dynamicDHCP") |
| elsif ($IPCONFIGURATION eq "static") { |
| insertloadlog($reservation_id, $computer_id, "info", "setting staticIPaddress"); |
| |
| if (setstaticaddress($computer_node_name, $image_os_name, $computer_ip_address)) { |
| notify($ERRORS{'DEBUG'}, 0, "set static address on $computer_ip_address $computer_node_name "); |
| insertloadlog($reservation_id, $computer_id, "staticIPaddress", "SUCCESS set static IP address on public interface"); |
| } |
| else { |
| insertloadlog($reservation_id, $computer_id, "staticIPaddress", "failed to set static IP address on public interface"); |
| return 0; |
| } |
| } ## end elsif ($IPCONFIGURATION eq "static") [ if ($IPCONFIGURATION eq "dynamicDHCP") |
| } ## end if ($IPCONFIGURATION ne "manualDHCP") |
| |
| return 1; |
| } ## end sub load |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 capture_prepare |
| |
| Parameters : |
| Returns : 1 if sucessful, 0 if failed |
| Description : |
| |
| =cut |
| |
| sub capture_prepare { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Get data |
| my $image_name = $self->data->get_image_name(); |
| my $computer_short_name = $self->data->get_computer_short_name(); |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Print some preliminary information |
| notify($ERRORS{'OK'}, 0, "image=$image_name, computer=$computer_short_name"); |
| |
| # Modify currentimage.txt |
| if (write_currentimage_txt($self->data)) { |
| notify($ERRORS{'OK'}, 0, "currentimage.txt updated on $computer_short_name"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update currentimage.txt on $computer_short_name"); |
| } |
| |
| if ($self->_edit_nodetype($computer_node_name, $image_name)) { |
| notify($ERRORS{'OK'}, 0, "nodetype modified, node $computer_node_name, image name $image_name"); |
| } # Close if _edit_nodetype |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not edit nodetype, node $computer_node_name, image name $image_name"); |
| return 0; |
| } # Close _edit_nodetype failed |
| |
| my @Images; |
| my ($i, $imagefile); |
| |
| # Get the image repository path |
| my $image_repository_path = $self->_get_image_repository_path(); |
| if (!$image_repository_path) { |
| notify($ERRORS{'CRITICAL'}, 0, "xCAT image repository information could not be determined"); |
| return 0; |
| } |
| |
| # Get the image template repository path |
| my $tmpl_repository_path = $self->_get_image_template_path(); |
| if (!$tmpl_repository_path) { |
| notify($ERRORS{'CRITICAL'}, 0, "xCAT template repository information could not be determined"); |
| return 0; |
| } |
| |
| # Get the image template repository path |
| my $basetmpl = $self->_get_base_template_filename(); |
| if (!$basetmpl) { |
| notify($ERRORS{'CRITICAL'}, 0, "xCAT template repository information could not be determined"); |
| return 0; |
| } |
| |
| notify($ERRORS{'OK'}, 0, "attempting to create $tmpl_repository_path/$image_name.tmpl"); |
| if (open(IMAGE, "/bin/cp $tmpl_repository_path/$basetmpl $tmpl_repository_path/$image_name.tmpl |")) { |
| @Images = <IMAGE>; |
| close(IMAGE); |
| foreach $i (@Images) { |
| |
| #if anything could mean failure |
| if ($i) { |
| notify($ERRORS{'OK'}, 0, "@Images"); |
| } |
| } |
| } # Close if open handle for cp tmpl file command |
| |
| #check to see if the new image file is there |
| if (open(IMAGES, "/bin/ls -1 $tmpl_repository_path |")) { |
| @Images = <IMAGES>; |
| close(IMAGES); |
| ($i, $imagefile) = 0; |
| foreach $i (@Images) { |
| if ($i =~ /$image_name.tmpl/) { |
| $imagefile = 1; |
| } |
| } |
| if ($imagefile) { |
| notify($ERRORS{'OK'}, 0, "$tmpl_repository_path/$image_name created"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, " $tmpl_repository_path/$image_name NOT created"); |
| return 0; |
| } |
| } # Close if tmpl file exists |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not execute /bin/ls -1 $tmpl_repository_path $! "); |
| return 0; |
| } # Close tmpl file does not exist |
| |
| # Call xCAT's nodeset, configure xCAT to save image on next reboot |
| if (_nodeset_option($computer_node_name, "image")) { |
| notify($ERRORS{'OK'}, 0, "$computer_node_name set to image state"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "failed $computer_node_name set to image state"); |
| return 0; |
| } |
| |
| notify($ERRORS{'OK'}, 0, "returning 1"); |
| return 1; |
| } ## end sub capture_prepare |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 capture_monitor |
| |
| Parameters : |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub capture_monitor { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Get the required data |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| my $image_name = $self->data->get_image_name(); |
| |
| # Get the image repository path |
| my $image_repository_path = $self->_get_image_repository_path(); |
| if (!$image_repository_path) { |
| notify($ERRORS{'CRITICAL'}, 0, "xCAT image repository information could not be determined"); |
| return 0; |
| } |
| |
| # Wait for node to reboot |
| notify($ERRORS{'OK'}, 0, "sleeping for 120 seconds before beginning to monitor image copy process"); |
| sleep 120; |
| |
| # Set variables to control how may attempts are made to wait for capture to finish |
| my $capture_loop_attempts = 80; |
| my $capture_loop_wait = 30; |
| |
| # Figure out and print how long will wait before timing out |
| my $maximum_wait_minutes = ($capture_loop_attempts * $capture_loop_wait) / 60; |
| notify($ERRORS{'OK'}, 0, "beginning to wait for image capture to complete, maximum wait time: $maximum_wait_minutes minutes"); |
| |
| my $image_size = 0; |
| my $nodeset_status; |
| CAPTURE_LOOP: for (my $capture_loop_count = 0; $capture_loop_count < $capture_loop_attempts; $capture_loop_count++) { |
| notify($ERRORS{'OK'}, 0, "attempt $capture_loop_count/$capture_loop_attempts: image copy not complete, sleeping for $capture_loop_wait seconds"); |
| sleep $capture_loop_wait; |
| |
| # Get the nodeset status for the node being captured |
| $nodeset_status = _nodeset_option($computer_node_name, "stat"); |
| notify($ERRORS{'DEBUG'}, 0, "nodeset status for $computer_node_name: $nodeset_status"); |
| |
| # nodeset stat will return 'boot' when image capture (Partimage) is complete |
| if ($nodeset_status eq "boot") { |
| last CAPTURE_LOOP; |
| } |
| |
| # Check the image size to see if it's growing |
| notify($ERRORS{'OK'}, 0, "checking size of $image_name"); |
| my $current_image_size = $self->get_image_size($image_name); |
| |
| # Check if image size is larger than the last time it was checked |
| if ($current_image_size > $image_size) { |
| notify($ERRORS{'OK'}, 0, "image size has increased: $image_size -> $current_image_size, still copying"); |
| $image_size = $current_image_size; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "image size is the same: $image_size=$current_image_size, copy may be complete"); |
| } |
| } ## end for (my $capture_loop_count = 0; $capture_loop_count... |
| |
| # Exiting waiting loop, nodeset status should be boot if successful |
| if ($nodeset_status eq "boot") { |
| # Nodeset 'boot' flag has been set, image copy process is complete |
| notify($ERRORS{'OK'}, 0, "image copy complete, nodeset status was set to 'boot' for $computer_node_name"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "image copy timed out, waited $maximum_wait_minutes minutes, nodeset status for $computer_node_name never changed to boot: $nodeset_status"); |
| return 0; |
| } |
| |
| # Create mbr and sfdisk files |
| if (open(LS, "/bin/ls -1s $image_repository_path |")) { |
| my @LS = <LS>; |
| close(LS); |
| foreach my $l (@LS) { |
| if ($l =~ /$image_name-hda/) { |
| |
| #create hda.mbr and hda.sfdisk |
| if (open(CP, "/bin/cp $image_repository_path/$image_name-hda.mbr $image_repository_path/$image_name-sda.mbr |")) { |
| close(CP); |
| notify($ERRORS{'OK'}, 0, "copied $image_name-hda.mbr to $image_repository_path/$image_name-sda.mbr"); |
| |
| #create sfdisk modify hardrive type |
| if (open(CP, "/bin/cp $image_repository_path/$image_name-hda.sfdisk $image_repository_path/$image_name-sda.sfdisk |")) { |
| close(CP); |
| notify($ERRORS{'OK'}, 0, "copied $image_name-hda.sfdisk to $image_repository_path/$image_name-sda.sfdisk"); |
| |
| #read in file |
| if (open(FILE, "$image_repository_path/$image_name-sda.sfdisk")) { |
| my @lines = <FILE>; |
| close(FILE); |
| foreach my $l (@lines) { |
| if ($l =~ s/hda/sda/g) { |
| |
| #editing file |
| } |
| } |
| |
| #print array to file |
| if (open(OUTFILE, ">$image_repository_path/$image_name-sda.sfdisk")) { |
| print OUTFILE @lines; |
| close(OUTFILE); |
| notify($ERRORS{'OK'}, 0, "modified drivetype of $image_name-sda.sfdisk"); |
| } |
| } ## end if (open(FILE, "$image_repository_path/$image_name-sda.sfdisk"... |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not open $image_repository_path/$image_name-sda.mbr for editing $!"); |
| } |
| } # Close if copy hda.sfdisk command |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not copy $image_name-hda.sfdisk to $image_repository_path/$image_name-sda.sfdisk $!"); |
| } |
| } # Close if copy mbr file command |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not copy $image_name-hda.mbr to $image_repository_path/$image_name-sda.mbr $!"); |
| } |
| } # Close if imagename-hda |
| |
| elsif ($l =~ /$image_name-sda/) { |
| |
| #create sda.mbr and sda.sfdisk |
| if (open(CP, "/bin/cp $image_repository_path/$image_name-sda.mbr $image_repository_path/$image_name-hda.mbr |")) { |
| close(CP); |
| notify($ERRORS{'OK'}, 0, "copied $image_name-sda.mbr to $image_repository_path/$image_name-hda.mbr"); |
| |
| #create sfdisk |
| if (open(CP, "/bin/cp $image_repository_path/$image_name-sda.sfdisk $image_repository_path/$image_name-hda.sfdisk |")) { |
| close(CP); |
| notify($ERRORS{'OK'}, 0, "copied $image_name-sda.sfdisk to $image_repository_path/$image_name-hda.sfdisk"); |
| |
| #read in file |
| if (open(FILE, "$image_repository_path/$image_name-hda.sfdisk")) { |
| my @lines = <FILE>; |
| close(FILE); |
| foreach my $l (@lines) { |
| if ($l =~ s/sda/hda/g) { |
| |
| #editing file |
| } |
| } |
| |
| #print array to file |
| if (open(OUTFILE, ">$image_repository_path/$image_name-hda.sfdisk")) { |
| print OUTFILE @lines; |
| close(OUTFILE); |
| notify($ERRORS{'OK'}, 0, "modified drivetype of $image_name-hda.sfdisk"); |
| } |
| } ## end if (open(FILE, "$image_repository_path/$image_name-hda.sfdisk"... |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "could not open $image_repository_path/$image_name-hda.sfdisk for editing $!"); |
| } |
| } ## end if (open(CP, "/bin/cp $image_repository_path/$image_name-sda.sfdisk $image_repository_path/$image_name-hda.sfdisk |"... |
| else { |
| notify($ERRORS{'OK'}, 0, "could not copy $image_repository_path/$image_name-sda.sfdisk to $image_repository_path/$image_name-hda.sfdisk $!"); |
| } |
| } ## end if (open(CP, "/bin/cp $image_repository_path/$image_name-sda.mbr $image_repository_path/$image_name-hda.mbr |"... |
| else { |
| notify($ERRORS{'OK'}, 0, "could not copy $image_repository_path/$image_name-sda.mbr to $image_repository_path/$image_name-hda.mbr $!"); |
| } |
| } # Close if image_name-sda |
| |
| } # Close foreach line returned from the ls imagerepository command |
| } # Close if ls imagerepository |
| |
| # Set file premissions on image files to 644 |
| # Allows other management nodes to retrieve the image if neccessary |
| if (open(CHMOD, "/bin/chmod -R 644 $image_repository_path/$image_name\* 2>&1 |")) { |
| close(CHMOD); |
| notify($ERRORS{'DEBUG'}, 0, "recursive update file permissions 644 on $image_repository_path/$image_name"); |
| } |
| |
| # Image capture complete, return 1 |
| notify($ERRORS{'OK'}, 0, "image capture complete"); |
| return 1; |
| |
| } ## end sub capture_monitor |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _edit_template |
| |
| Parameters : imagename,drivetype |
| Returns : 0 failed or 1 success |
| Description : general routine to edit /opt/xcat/install/image/x86/imagename.tmpl |
| used in imaging process |
| |
| =cut |
| |
| sub _edit_template { |
| my ($imagename, $drivetype) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'CRITCAL'}, 0, "drivetype is not defined") |
| if (!(defined($drivetype))); |
| notify($ERRORS{'CRITCAL'}, 0, "imagename is not defined") |
| if (!(defined($imagename))); |
| |
| my $template = "$XCAT_ROOT/install/image/x86/$imagename.tmpl"; |
| my @lines; |
| if (open(FILE, $template)) { |
| @lines = <FILE>; |
| close FILE; |
| my $line; |
| for $line (@lines) { |
| if ($line =~ /^export DISKS=/) { |
| $line = "export DISKS=\"$drivetype\"\n"; |
| last; |
| } |
| } |
| |
| #dump back to template file |
| if (open(FILE, ">$template")) { |
| print FILE @lines; |
| close FILE; |
| return 1; |
| } |
| else { |
| |
| # could not open nodetype file for editing |
| notify($ERRORS{'CRITICAL'}, 0, "could not open $template for writing\nerror message: $!"); |
| return 0; |
| } |
| } ## end if (open(FILE, $template)) |
| else { |
| |
| # could not open nodetype file for editing |
| notify($ERRORS{'CRITICAL'}, 0, "could not open $template for reading\nerror message: $!"); |
| return 0; |
| } |
| } ## end sub _edit_template |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _edit_nodetype |
| |
| Parameters : node, imagename, osname |
| Returns : 0 failed or 1 success |
| Description : xCAT specific edits xcat's nodetype file with requested image name |
| |
| =cut |
| |
| sub _edit_nodetype { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Use arguments for computer and image if they were passed |
| my $computer_node_name = shift; |
| my $image_name = shift; |
| |
| # Use the new image name if it is set |
| $image_name = $self->data->get_image_name() if !$image_name; |
| |
| # Get the rest of the variables |
| $computer_node_name = $self->data->get_computer_node_name() |
| if !$computer_node_name; |
| my $image_os_name = $self->data->get_image_os_name(); |
| my $image_architecture = $self->data->get_image_architecture(); |
| my $image_os_source_path = $self->data->get_image_os_source_path(); |
| |
| # Fix for Linux images on henry4 |
| my $management_node_hostname = $self->data->get_management_node_hostname(); |
| my $image_os_type = $self->data->get_image_os_type(); |
| if ( $management_node_hostname =~ /henry4/i |
| && $image_os_type =~ /linux/i |
| && $image_os_source_path eq 'image') |
| { |
| $image_os_source_path = 'linux_image'; |
| notify($ERRORS{'DEBUG'}, 0, "fixed Linux image path for henry4: image --> linux_image"); |
| } |
| |
| # Check to make sure the variables are populated |
| if (!$computer_node_name) { |
| notify($ERRORS{'CRITICAL'}, 0, "computer node name is not defined"); |
| return 0; |
| } |
| if (!$image_name) { |
| notify($ERRORS{'CRITICAL'}, 0, "image name is not defined"); |
| return 0; |
| } |
| if (!$image_os_name) { |
| notify($ERRORS{'CRITICAL'}, 0, "image OS name is not defined"); |
| return 0; |
| } |
| if (!$image_architecture) { |
| notify($ERRORS{'CRITICAL'}, 0, "image architecture is not defined"); |
| return 0; |
| } |
| if (!$image_os_source_path) { |
| notify($ERRORS{'CRITICAL'}, 0, "image OS source path is not defined"); |
| return 0; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "$computer_node_name, image=$image_name, os=$image_os_name, arch=$image_architecture, path=$image_os_source_path"); |
| |
| # Assemble the nodetype.tab and lock file paths |
| my $nodetype_file_path = "$XCAT_ROOT/etc/nodetype.tab"; |
| my $lock_file_path = "$nodetype_file_path.lockfile"; |
| |
| # Open the lock file |
| if (sysopen(LOCKFILE, $lock_file_path, O_RDONLY | O_CREAT)) { |
| notify($ERRORS{'DEBUG'}, 0, "opened $lock_file_path"); |
| |
| # Set exclusive lock on lock file |
| if (flock(LOCKFILE, LOCK_EX)) { |
| notify($ERRORS{'DEBUG'}, 0, "set exclusive lock on $lock_file_path"); |
| |
| if (open(NODETYPE, $nodetype_file_path)) { #read file |
| notify($ERRORS{'DEBUG'}, 0, "opened $nodetype_file_path"); |
| |
| # Get the nodetype.tab lines and close the file |
| my @nodetype_lines = <NODETYPE>; |
| notify($ERRORS{'DEBUG'}, 0, "lines found in nodetype.tab: " . scalar @nodetype_lines); |
| |
| # Close the nodetype.tab file |
| close(NODETYPE); |
| notify($ERRORS{'DEBUG'}, 0, "closed $nodetype_file_path"); |
| |
| # Loop through the nodetype.tab lines |
| for my $line (@nodetype_lines) { |
| |
| # Skip over non-matching lines |
| next if ($line !~ /^$computer_node_name\s+([,\w]*)/); |
| notify($ERRORS{'OK'}, 0, "matching line found: $line"); |
| |
| # Replace line matching $computer_node_name |
| $line = "$computer_node_name\t\t$image_os_source_path,$image_architecture,$image_name\n"; |
| notify($ERRORS{'OK'}, 0, "line modified: $line"); |
| } ## end for my $line (@nodetype_lines) |
| |
| # Dump modified array to nodetype.tab file |
| if (open(NODETYPE, ">$nodetype_file_path")) { |
| notify($ERRORS{'OK'}, 0, "nodetype.tab opened"); |
| print NODETYPE @nodetype_lines; |
| notify($ERRORS{'OK'}, 0, "nodetype.tab contents replaced"); |
| close(NODETYPE); |
| notify($ERRORS{'OK'}, 0, "nodetype.tab saved"); |
| close(LOCKFILE); |
| notify($ERRORS{'DEBUG'}, 0, "lock file closed"); |
| return 1; |
| } ## end if (open(NODETYPE, ">$nodetype_file_path")) |
| else { |
| |
| # Could not open nodetype.tab file for editing |
| notify($ERRORS{'CRITICAL'}, 0, "could not open file for writing: $nodetype_file_path, $!"); |
| close(LOCKFILE); |
| notify($ERRORS{'DEBUG'}, 0, "lock file closed"); |
| return 0; |
| } |
| } ## end if (open(NODETYPE, $nodetype_file_path)) |
| else { |
| |
| # could not open nodetype file for reading |
| notify($ERRORS{'CRITICAL'}, 0, "could not open file for reading: $nodetype_file_path, $!"); |
| close(LOCKFILE); |
| notify($ERRORS{'DEBUG'}, 0, "lock file closed"); |
| return 0; |
| } |
| } ## end if (flock(LOCKFILE, LOCK_EX)) |
| else { |
| |
| # Could not open lock |
| notify($ERRORS{'CRITICAL'}, 0, "unable to get exclusive lock on $lock_file_path to edit nodetype.tab, $!"); |
| close(LOCKFILE); |
| notify($ERRORS{'DEBUG'}, 0, "lock file closed"); |
| return 0; |
| } |
| } ## end if (sysopen(LOCKFILE, $lock_file_path, O_RDONLY... |
| |
| else { |
| |
| # Could not open lock file |
| notify($ERRORS{'CRITICAL'}, 0, "unable to open $lock_file_path to edit nodetype.tab, $!"); |
| return 0; |
| } |
| |
| } ## end sub _edit_nodetype |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _pping |
| |
| Parameters : $node |
| Returns : 1 or 0 |
| Description : using xcat pping cmd to ping blade, xcat specific |
| |
| =cut |
| |
| sub _pping { |
| my $node = $_[0]; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'WARNING'}, 0, "_pping: node is not defined") |
| if (!(defined($node))); |
| if (open(PPING, "$XCAT_ROOT/bin/pping $node 2>&1 |")) { |
| my @file = <PPING>; |
| close(PPING); |
| foreach my $l (@file) { |
| chomp $l; |
| notify($ERRORS{'OK'}, 0, "pinging $l"); |
| if ($l =~ /noping/) { |
| return 0; |
| } |
| if ($l =~ /$node: ping/) { |
| return 1; |
| } |
| } ## end foreach my $l (@file) |
| return 1; |
| } ## end if (open(PPING, "$XCAT_ROOT/bin/pping $node 2>&1 |"... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "could not execute $XCAT_ROOT/bin/pping $node"); |
| return 0; |
| } |
| } ## end sub _pping |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _nodeset |
| |
| Parameters : $node |
| Returns : xcat state of node or 0 |
| Description : using xcat nodeset cmd to retrieve state of blade, xcat specific |
| |
| =cut |
| |
| sub _nodeset { |
| my $node = $_[0]; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'WARNING'}, 0, "_nodeset: node is not defined") |
| if (!(defined($node))); |
| return 0 if (!(defined($node))); |
| |
| my ($blah, $case); |
| my @file; |
| my $l; |
| if (open(NODESET, "$XCAT_ROOT/bin/nodeset $node stat |")) { |
| |
| #notify($ERRORS{'OK'},0,"executing $XCAT_ROOT/bin/nodeset $node stat "); |
| @file = <NODESET>; |
| close NODESET; |
| foreach $l (@file) { |
| chomp($l); |
| ($blah, $case) = split(/:\s/, $l); |
| } |
| if ($case) { |
| |
| #notify($ERRORS{'OK'},0,"$node in $case state "); |
| return $case; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "case for $node is empty"); |
| return 0; |
| } |
| } ## end if (open(NODESET, "$XCAT_ROOT/bin/nodeset $node stat |"... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute $XCAT_ROOT/bin/nodeset $node stat"); |
| return 0; |
| } |
| } ## end sub _nodeset |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _nodeset |
| |
| Parameters : $node $option |
| Returns : xcat state of node or 0 |
| Description : using xcat nodeset cmd to use the input option of blade, xcat specific |
| |
| =cut |
| |
| sub _nodeset_option { |
| my ($node, $option) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'WARNING'}, 0, "_nodeset_option: node is not defined") |
| if (!(defined($node))); |
| notify($ERRORS{'WARNING'}, 0, "_nodeset_option: option is not defined") |
| if (!(defined($option))); |
| my ($blah, $case); |
| my @file; |
| my $l; |
| if (open(NODESET, "$XCAT_ROOT/bin/nodeset $node $option |")) { |
| |
| #notify($ERRORS{'OK'},0,"executing $XCAT_ROOT/bin/nodeset $node $option"); |
| @file = <NODESET>; |
| close NODESET; |
| foreach $l (@file) { |
| chomp($l); |
| ($blah, $case) = split(/:\s/, $l); |
| } |
| if ($case) { |
| notify($ERRORS{'OK'}, 0, "$node in $case state "); |
| return $case; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "case for $node is empty"); |
| return 0; |
| } |
| } ## end if (open(NODESET, "$XCAT_ROOT/bin/nodeset $node $option |"... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute $XCAT_ROOT/bin/nodeset $node $option"); |
| return 0; |
| } |
| |
| } ## end sub _nodeset_option |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 makesshgkh |
| |
| Parameters : imagename |
| Returns : 0 or 1 |
| Description : xCAT specific scans node for public ssh key |
| |
| =cut |
| |
| sub makesshgkh { |
| my $node = $_[0]; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'WARNING'}, 0, "node is not defined") |
| if (!(defined($node))); |
| if (!(defined($node))) { |
| return 0; |
| } |
| if (open(MAKESSHGKH, "$XCAT_ROOT/sbin/makesshgkh $node 2>&1 |")) { |
| while (<MAKESSHGKH>) { |
| chomp($_); |
| if ($_ =~ /Scanning keys/) { |
| |
| #notify($ERRORS{'OK'},0,"$_"); |
| } |
| else { |
| |
| #possible error |
| #notify($ERRORS{'OK'},0,"possible error in $_ "); |
| } |
| } ## end while (<MAKESSHGKH>) |
| close(MAKESSHGKH); |
| return 1; |
| } ## end if (open(MAKESSHGKH, "$XCAT_ROOT/sbin/makesshgkh $node 2>&1 |"... |
| return 0; |
| } ## end sub makesshgkh |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _rpower |
| |
| Parameters : $node, $option |
| Returns : 1 connected 0 not connected |
| Description : xCAT specific command - hard power cycle the blade |
| |
| =cut |
| |
| sub _rpower { |
| my ($node, $option) = @_; |
| |
| #make sure node and option are defined |
| notify($ERRORS{'WARNING'}, 0, "_rpower: node is not defined") |
| if (!(defined($node))); |
| notify($ERRORS{'WARNING'}, 0, "_rpower: option is not defined setting to cycle") |
| if (!(defined($option))); |
| return 0 if (!(defined($node))); |
| |
| $option = "cycle" if (!(defined($option))); |
| |
| my $l; |
| my @file; |
| RPOWER: |
| if (open(RPOWER, "$XCAT_ROOT/bin/rpower $node $option |")) { |
| @file = <RPOWER>; |
| close(RPOWER); |
| foreach $l (@file) { |
| if ($l =~ /not in bay/) { |
| |
| # not in bay problem |
| if (_fix_rpower($node)) { |
| goto RPOWER; #try again |
| } |
| } |
| if ($l =~ /$node:\s+(on|off)/) { |
| return $1; |
| } |
| } ## end foreach $l (@file) |
| return 0; |
| } ## end if (open(RPOWER, "$XCAT_ROOT/bin/rpower $node $option |"... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "_rpower: could not run $XCAT_ROOT/bin/rpower $node $option $!"); |
| return 0; |
| } |
| |
| } ## end sub _rpower |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _fix_rpower |
| |
| Parameters : nodename |
| Returns : 1(success) or 0(failure) |
| Description : due to a bug in a previous firmware version. |
| it's belived to be fixed in previous versions |
| |
| =cut |
| |
| sub _fix_rpower { |
| my $node = $_[0]; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'WARNING'}, 0, "node not set") if (!defined($node)); |
| |
| # this function kicks the management this is a known xcat bug, the |
| # workaround is to run rinv nodename all twice |
| my $notfixed = 1; |
| my $tries = 0; |
| while ($notfixed) { |
| $tries++; |
| if ($tries > 10) { |
| notify($ERRORS{'CRITICAL'}, 0, "_fix_rpower failed $tries on $node"); |
| return 0; |
| } |
| |
| #notify($ERRORS{'OK'},0,"executing $XCAT_ROOT/bin/rinv $node all"); |
| if (open(RINV, "$XCAT_ROOT/bin/rinv $node all |")) { |
| my @rinv = <RINV>; |
| my $line; |
| close RINV; |
| foreach $line (@rinv) { |
| next if ($line =~ /HTTP login failed/); #expected |
| if ($line =~ /Machine Type/) { |
| notify($ERRORS{'OK'}, 0, "rinv succeded for $node"); |
| return 1; |
| } |
| } |
| } ## end if (open(RINV, "$XCAT_ROOT/bin/rinv $node all |"... |
| else { |
| notify($ERRORS{'OK'}, 0, "could not execute $XCAT_ROOT/bin/rinv $node all $!"); |
| } |
| } ## end while ($notfixed) |
| |
| } ## end sub _fix_rpower |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 node_status |
| |
| Parameters : [0]: computer node name (optional) |
| [1]: log file path (optional) |
| Returns : If called in scalar or boolean context: |
| 1: node is down or needs to be reloaded |
| 0: node is up and does not need to be reloaded |
| undefined: error occurred while checking node status |
| |
| hashref: reference to hash with keys/values: |
| {status} => <"READY","FAIL"> |
| {ping} => <0,1> |
| {ssh} => <0,1> |
| {rpower} => <0,1> |
| {nodeset} => <"boot", "install", "image", ...> |
| {nodetype} => <image name> |
| {currentimage} => <image name> |
| Description : Checks the status of an xCAT-provisioned machine. If no |
| arguments are supplied, the node and image for the current |
| reservation will be used. |
| |
| =cut |
| |
| sub node_status { |
| my $self = shift; |
| my ($computer_node_name, $log); |
| |
| my $management_node_os_name = 0; |
| my $management_node_keys = 0; |
| my $computer_host_name = 0; |
| my $computer_short_name = 0; |
| my $computer_ip_address = 0; |
| my $image_os_name = 0; |
| my $image_name = 0; |
| |
| # Check if subroutine was called as a class method |
| if (ref($self) !~ /xcat/i) { |
| #$cidhash->{hostname}, $cidhash->{OSname}, $cidhash->{MNos}, $cidhash->{IPaddress}, $LOG |
| $computer_node_name = $self; |
| |
| $log = shift; |
| $log = 0 if !$log; |
| $computer_short_name = $computer_node_name; |
| } |
| else { |
| |
| # Get the computer name from the DataStructure |
| $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Check if this was called as a class method, but a node name was also specified as an argument |
| my $node_name_argument = shift; |
| $computer_node_name = $node_name_argument if $node_name_argument; |
| $computer_host_name = $self->data->get_computer_host_name(); |
| $computer_short_name = $self->data->get_computer_short_name(); |
| $image_name = $self->data->get_image_name(); |
| $log = 0; |
| } ## end else [ if (ref($self) !~ /xcat/i) |
| |
| # Check the node name variable |
| if (!defined($computer_node_name) || !$computer_node_name) { |
| notify($ERRORS{'WARNING'}, 0, "node name could not be determined"); |
| return; |
| } |
| notify($ERRORS{'DEBUG'}, 0, "checking status of node: $computer_node_name"); |
| |
| |
| |
| # Create a hash to store status components |
| my %status; |
| |
| # Initialize all hash keys here to make sure they're defined |
| $status{status} = 0; |
| $status{nodetype} = 0; |
| $status{currentimage} = 0; |
| $status{ping} = 0; |
| $status{rpower} = 0; |
| $status{nodeset} = 0; |
| $status{ssh} = 0; |
| |
| # Check the nodetype.tab file |
| notify($ERRORS{'DEBUG'}, $log, "checking the current image listed in nodetype.tab for $computer_short_name"); |
| my $nodetype_file_path = "$XCAT_ROOT/etc/nodetype.tab"; |
| if (open(NODETYPE, $nodetype_file_path)) { |
| notify($ERRORS{'OK'}, 0, "opened $nodetype_file_path for reading"); |
| |
| # Get all the lines in nodetype.tab |
| my @nodetype_lines = <NODETYPE>; |
| |
| # Close the nodetype.tab file |
| close NODETYPE; |
| |
| # Find the nodetype.tab line for the computer |
| # Example line: vcln1-1 image,x86,winxp-base1-v21 |
| # vclb2-8 rhas5,x86,rhel5-base587-v0 |
| my $nodetype_contents = join("\n", @nodetype_lines); |
| if ($nodetype_contents =~ /^$computer_short_name\s+(\w+),(\w+),(.+)$/xm, $nodetype_contents) { |
| my $nodetype_install_path = $1; |
| my $nodetype_image_architecture = $2; |
| my $nodetype_image_name = $3; |
| |
| # Remove any spaces from the beginning and end of the $nodetype_image_name string |
| $nodetype_image_name =~ s/^\s+//; |
| $nodetype_image_name =~ s/\s+$//; |
| |
| notify($ERRORS{'DEBUG'}, 0, "found nodetype.tab line: path=$nodetype_install_path, arch=$nodetype_image_architecture, image=$nodetype_image_name"); |
| $status{nodetype} = $nodetype_image_name; |
| } ## end if ($nodetype_contents =~ /^$computer_short_name\s+(\w+),(\w+),(.+)$/xm... |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to find line in nodetype.tab for computer: $computer_short_name"); |
| return; |
| } |
| } ## end if (open(NODETYPE, $nodetype_file_path)) |
| else { |
| notify($ERRORS{'WARNING'}, $log, "could not open $nodetype_file_path for reading"); |
| return; |
| } |
| |
| # Check if node is pingable |
| notify($ERRORS{'DEBUG'}, $log, "checking if $computer_host_name is pingable"); |
| if (_pingnode($computer_host_name)) { |
| $status{ping} = 1; |
| notify($ERRORS{'OK'}, $log, "$computer_host_name is pingable ($status{ping})"); |
| } |
| else { |
| $status{ping} = 0; |
| notify($ERRORS{'OK'}, $log, "$computer_host_name is not pingable ($status{ping})"); |
| } |
| |
| # Check the rpower status |
| notify($ERRORS{'DEBUG'}, $log, "checking $computer_short_name xCAT rpower status"); |
| my $rpower_status = _rpower($computer_short_name, "stat"); |
| if ($rpower_status =~ /on/i) { |
| $status{rpower} = 1; |
| } |
| else { |
| $status{rpower} = 0; |
| } |
| notify($ERRORS{'OK'}, $log, "$computer_short_name rpower status: $rpower_status ($status{rpower})"); |
| |
| # Check the xCAT nodeset status |
| notify($ERRORS{'DEBUG'}, $log, "checking $computer_short_name xCAT nodeset status"); |
| my $nodeset_status = _nodeset($computer_short_name); |
| notify($ERRORS{'OK'}, $log, "$computer_short_name nodeset status: $nodeset_status"); |
| $status{nodeset} = $nodeset_status; |
| |
| # Check the sshd status |
| notify($ERRORS{'DEBUG'}, $log, "checking if $computer_short_name sshd service is accessible"); |
| my $sshd_status = _sshd_status($computer_short_name, $status{nodetype}, $log); |
| |
| # If sshd is accessible, perform sshd-dependent checks |
| if ($sshd_status =~ /on/) { |
| $status{ssh} = 1; |
| notify($ERRORS{'DEBUG'}, $log, "$computer_short_name sshd service is accessible, performing dependent checks"); |
| |
| # Check the currentimage.txt file on the node |
| notify($ERRORS{'DEBUG'}, $log, "checking image specified in currentimage.txt file on $computer_short_name"); |
| if ($status{nodetype} =~ /win|image/) { |
| my $status_currentimage = _getcurrentimage($computer_short_name); |
| if ($status_currentimage) { |
| notify($ERRORS{'OK'}, $log, "$computer_short_name currentimage.txt has: $status_currentimage"); |
| $status{currentimage} = $status_currentimage; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, $log, "$computer_short_name currentimage.txt could not be checked"); |
| } |
| } ## end if ($status{nodetype} =~ /win|image/) |
| else { |
| notify($ERRORS{'OK'}, $log, "currentimage.txt can not be checked for image type: $status{nodetype}"); |
| } |
| } ## end if ($sshd_status =~ /on/) |
| else { |
| $status{ssh} = 0; |
| } |
| notify($ERRORS{'OK'}, $log, "$computer_short_name sshd status: $sshd_status ($status{ssh})"); |
| |
| # Check if nodetype.tab matches reservation image name |
| my $nodetype_image_match = 0; |
| if ($status{nodetype} eq $image_name) { |
| notify($ERRORS{'OK'}, $log, "nodetype.tab ($status{nodetype}) matches reservation image ($image_name)"); |
| $nodetype_image_match = 1; |
| } |
| else { |
| notify($ERRORS{'OK'}, $log, "nodetype.tab ($status{nodetype}) does not match reservation image ($image_name)"); |
| } |
| |
| # Check if nodetype.tab matches currentimage.txt |
| my $nodetype_currentimage_match = 0; |
| if ($status{nodetype} eq $status{currentimage}) { |
| notify($ERRORS{'OK'}, $log, "nodetype.tab ($status{nodetype}) matches currentimage.txt ($status{currentimage})"); |
| $nodetype_currentimage_match = 1; |
| } |
| else { |
| notify($ERRORS{'OK'}, $log, "nodetype.tab ($status{nodetype}) does not match currentimage.txt ($status{currentimage}), assuming nodetype.tab is correct"); |
| } |
| |
| # Determine the overall machine status based on the individual status results |
| $status{status} = 'READY'; |
| if (!$status{rpower}) { |
| $status{status} = 'RELOAD'; |
| notify($ERRORS{'OK'}, $log, "rpower status is not on, node needs to be reloaded"); |
| } |
| if (!$status{ssh}) { |
| $status{status} = 'RELOAD'; |
| notify($ERRORS{'OK'}, $log, "sshd is not accessible, node needs to be reloaded"); |
| } |
| if (!$nodetype_image_match) { |
| $status{status} = 'RELOAD'; |
| notify($ERRORS{'OK'}, $log, "nodetype.tab does not match requested image, node needs to be reloaded"); |
| } |
| |
| # Node is up and doesn't need to be reloaded |
| if ($status{status} =~ /ready/i) { |
| notify($ERRORS{'OK'}, $log, "node is up and does not need to be reloaded"); |
| } |
| else { |
| notify($ERRORS{'OK'}, $log, "node is either down or needs to be reloaded"); |
| } |
| |
| notify($ERRORS{'OK'}, $log, "returning node status hash reference with {status}=$status{status}"); |
| return \%status; |
| } ## end sub node_status |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _assign2project |
| |
| Parameters : $node, $project |
| Returns : 0 or 1 |
| Description : xCAT specific changes the networking to capable switch modules to either vcl,hpc or vclhpc project |
| |
| =cut |
| |
| sub _assign2project { |
| my ($node, $project) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| notify($ERRORS{'CRITICAL'}, 0, "node is not defined") |
| if (!(defined($node))); |
| notify($ERRORS{'CRITICAL'}, 0, "project is not defined") |
| if (!(defined($project))); |
| my $PROJECTtab = "$XCAT_ROOT/etc/project.tab"; |
| my $assign2project = "$XCAT_ROOT/sbin/assign2project"; |
| my $LCK = $PROJECTtab . "lockfile"; |
| |
| #make sure this management node can make assignments |
| |
| if (-r $PROJECTtab) { #do we have a project.tab file to work with |
| |
| #read project tab |
| if (open(PT, "<$PROJECTtab")) { |
| my @pt = <PT>; |
| close(PT); |
| my $p; |
| foreach $p (@pt) { |
| if ($p =~ /^$node\s+/) { |
| if ($p =~ /^$node\s*$project$/i) { |
| notify($ERRORS{'OK'}, 0, "$node is set correctly to $project"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "starting to set exclusive lock on $LCK"); |
| if (sysopen(LF, $LCK, O_RDONLY | O_CREAT)) { |
| if (flock(LF, LOCK_EX)) { #set exclusive lock on LF |
| notify($ERRORS{'OK'}, 0, "setting exclusive lock on $LCK"); |
| notify($ERRORS{'OK'}, 0, "$node is set incorrectly changing to $project project"); |
| if (open(AP, "$assign2project $node $project 2>&1 |")) { |
| my @file = <AP>; |
| close(AP); |
| foreach my $l (@file) { |
| notify($ERRORS{'OK'}, 0, "output @file"); |
| if ($l =~ /configurations are already correct! Nothing done/) { |
| notify($ERRORS{'OK'}, 0, "$node is currently assigned to $project - releasing lock"); |
| close(LF); |
| return 1; |
| } |
| if ($l =~ /Done!/) { |
| notify($ERRORS{'OK'}, 0, "$node is successfully assigned to $project - releasing lock"); |
| close(LF); |
| return 1; |
| } |
| |
| } #foreach |
| notify($ERRORS{'CRITICAL'}, 0, "provided unexpected output $node $project - output= @file"); |
| close(LF); |
| return 0; |
| |
| } #if AP |
| } #flock |
| } #sysopen |
| } #else |
| } #if node |
| } #foreach |
| } #if open |
| else { |
| notify($ERRORS{'WARNING'}, 0, "could not open $PROJECTtab for reading $!"); |
| close(LF); |
| return 0; |
| } |
| } #if tabfile readable |
| else { |
| notify($ERRORS{'OK'}, 0, "project.tab does not exist on this Management node"); |
| return 1; |
| |
| } |
| |
| } ## end sub _assign2project |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 does_image_exist |
| |
| Parameters : optional: image name |
| Returns : 1 if image exists, 0 if it doesn't |
| Description : Checks the management node's local image repository for the |
| existence of the requested image. This subroutine does not |
| attempt to copy the image from another management node. The |
| retrieve_image() subroutine does this. Callers of |
| does_image_exist must also call retrieve_image if image library |
| retrieval functionality is desired. |
| |
| =cut |
| |
| sub does_image_exist { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Get the image name, first try passed argument, then data |
| my $image_name = shift; |
| $image_name = $self->data->get_image_name() if !$image_name; |
| if (!$image_name) { |
| notify($ERRORS{'WARNING'}, 0, "unable to determine image name"); |
| return; |
| } |
| |
| # Get the image repository path |
| my $image_repository_path = $self->_get_image_repository_path(); |
| if (!$image_repository_path) { |
| notify($ERRORS{'WARNING'}, 0, "image repository path could not be determined"); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "image repository path: $image_repository_path"); |
| } |
| |
| # Get the tmpl repository path |
| my $tmpl_repository_path = $self->_get_image_template_path(); |
| if (!$tmpl_repository_path) { |
| notify($ERRORS{'WARNING'}, 0, "image template path could not be determined"); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "template repository path: $tmpl_repository_path"); |
| } |
| |
| # Check if template file exists for the image |
| # -s File has nonzero size |
| my $tmpl_file_exists; |
| if (-s "$tmpl_repository_path/$image_name.tmpl") { |
| $tmpl_file_exists = 1; |
| notify($ERRORS{'DEBUG'}, 0, "template file exists: $image_name.tmpl"); |
| } |
| else { |
| $tmpl_file_exists = 0; |
| notify($ERRORS{'OK'}, 0, "template file does not exist: $tmpl_repository_path/$image_name.tmpl"); |
| } |
| |
| # Check if image files exist (Partimage files) |
| # Open the repository directory |
| if (!opendir(REPOSITORY, $image_repository_path)) { |
| notify($ERRORS{'WARNING'}, 0, "unable to open the image repository directory: $image_repository_path"); |
| return; |
| } |
| |
| # Get the list of files in the repository and close the directory |
| my @repository_files = readdir(REPOSITORY); |
| closedir(REPOSITORY); |
| |
| # Check if any files exist for the image |
| my $image_files_exist; |
| if (my @image_files = grep(/$image_name/, @repository_files)) { |
| $image_files_exist = 1; |
| my $image_file_list = join(@image_files, "\n"); |
| notify($ERRORS{'DEBUG'}, 0, "image files exist in repository:\n$image_file_list"); |
| } |
| else { |
| $image_files_exist = 0; |
| notify($ERRORS{'OK'}, 0, "image files do not exist in repository: $image_repository_path/$image_name"); |
| } |
| |
| # Image files found |
| if ($tmpl_file_exists && $image_files_exist) { |
| notify($ERRORS{'OK'}, 0, "image $image_name exists on this management node, returning 0"); |
| return 1; |
| } |
| elsif (!$tmpl_file_exists && !$image_files_exist) { |
| notify($ERRORS{'OK'}, 0, "image $image_name does not exist on this management node, returning 1"); |
| return 0; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "image $image_name partially exists on this management node, tmpl=$tmpl_file_exists, image=$image_files_exist, returning undefined"); |
| return; |
| } |
| |
| } ## end sub does_image_exist |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 retrieve_image |
| |
| Parameters : |
| Returns : |
| Description : Attempts to retrieve an image from an image library partner |
| |
| =cut |
| |
| sub retrieve_image { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return; |
| } |
| |
| # Make sure imag library functions are enabled |
| my $image_lib_enable = $self->data->get_management_node_image_lib_enable(); |
| if (!$image_lib_enable) { |
| notify($ERRORS{'OK'}, 0, "image library functions are disabled"); |
| return; |
| } |
| |
| # Get the image name |
| my $image_name = shift; |
| $image_name = $self->data->get_image_name() if !$image_name; |
| if (!$image_name) { |
| notify($ERRORS{'WARNING'}, 0, "unable to determine image name"); |
| return; |
| } |
| |
| # Get the other image library variables |
| my $image_lib_user = $self->data->get_management_node_image_lib_user() |
| || 'undefined'; |
| my $image_lib_key = $self->data->get_management_node_image_lib_key() |
| || 'undefined'; |
| my $image_lib_partners = $self->data->get_management_node_image_lib_partners() || 'undefined'; |
| if ("$image_lib_user $image_lib_key $image_lib_partners" =~ /undefined/) { |
| notify($ERRORS{'WARNING'}, 0, "image library configuration data is missing: user=$image_lib_user, key=$image_lib_key, partners=$image_lib_partners"); |
| return; |
| } |
| |
| # Get the image repository path |
| my $image_repository_path = $self->_get_image_repository_path(); |
| if (!$image_repository_path) { |
| notify($ERRORS{'WARNING'}, 0, "image repository path could not be determined"); |
| return; |
| } |
| |
| # Get the tmpl repository path |
| my $tmpl_repository_path = $self->_get_image_template_path(); |
| if (!$tmpl_repository_path) { |
| notify($ERRORS{'WARNING'}, 0, "image template path could not be determined"); |
| return; |
| } |
| |
| # Check if template file exists for the image |
| # -s File has nonzero size |
| if (-s "$tmpl_repository_path/$image_name.tmpl") { |
| notify($ERRORS{'OK'}, 0, "template file already exists: $image_name.tmpl"); |
| } |
| else { |
| |
| # Get the name of the base tmpl file |
| my $basetmpl = $self->_get_base_template_filename(); |
| |
| # Template file doesn't exist, try to make a copy of the base template file |
| if (copy("$tmpl_repository_path/$basetmpl", "$tmpl_repository_path/$image_name.tmpl")) { |
| notify($ERRORS{'OK'}, 0, "template file copied: $basetmpl --> $image_name.tmpl"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "template file could not be copied copied: $basetmpl --> $image_name.tmpl, $!"); |
| return; |
| } |
| } ## end else [ if (-s "$tmpl_repository_path/$image_name.tmpl") |
| |
| # Attempt to copy image from other management nodes |
| notify($ERRORS{'OK'}, 0, "attempting to copy $image_name from other management nodes"); |
| |
| # Split up the partner list |
| my @partner_list = split(/,/, $image_lib_partners); |
| if ((scalar @partner_list) == 0) { |
| notify($ERRORS{'WARNING'}, 0, "image lib partners variable is not listed correctly or does not contain any information: $image_lib_partners"); |
| return; |
| } |
| |
| # Loop through the partners, attempt to copy |
| foreach my $partner (@partner_list) { |
| notify($ERRORS{'OK'}, 0, "checking if $partner has $image_name"); |
| |
| # Use ssh to call ls on the partner management node |
| my ($ls_exit_status, $ls_output_array_ref) = run_ssh_command($partner, $image_lib_key, "ls -1 $image_repository_path", $image_lib_user); |
| |
| # Check if the ssh command failed |
| if (!$ls_output_array_ref) { |
| notify($ERRORS{'WARNING'}, 0, "unable to run ls command via ssh on $partner"); |
| next; |
| } |
| |
| # Convert the output array to a string |
| my $ls_output = join("\n", @{$ls_output_array_ref}); |
| |
| # Check the ls output for permission denied |
| if ($ls_output =~ /permission denied/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "permission denied when checking if $partner has $image_name, exit status=$ls_exit_status, output:\n$ls_output"); |
| next; |
| } |
| |
| # Check the ls output for the image name |
| if ($ls_output !~ /$image_name[\.\-]/i) { |
| notify($ERRORS{'OK'}, 0, "$image_name does not exist on $partner"); |
| next; |
| } |
| |
| # Image exists |
| notify($ERRORS{'OK'}, 0, "$image_name exists on $partner, attempting to copy"); |
| |
| # Attempt copy |
| if (run_scp_command("$image_lib_user\@$partner:$image_repository_path/$image_name*", $image_repository_path, $image_lib_key)) { |
| notify($ERRORS{'OK'}, 0, "$image_name files copied via SCP"); |
| last; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to copy $image_name files via SCP"); |
| next; |
| } |
| } ## end foreach my $partner (@partner_list) |
| |
| # Make sure image was copied |
| if ($self->does_image_exist($image_name)) { |
| notify($ERRORS{'OK'}, 0, "$image_name was copied to this management node"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "$image_name was not copied to this management node"); |
| return 0; |
| } |
| } ## end sub retrieve_image |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _check_pxe_grub_file |
| |
| Parameters : imagename |
| Returns : 0 failed or 1 success |
| Description : checks the pxe and grub files for xCAT management nodes |
| if file size is equal to 0 delete the file and return true |
| return true if file not empty |
| only return false if failure to execute or delete files |
| |
| =cut |
| |
| sub _check_pxe_grub_files { |
| my $imagename = $_[0]; |
| my ($package, $filename, $line, $sub) = caller(0); |
| notify($ERRORS{'WARNING'}, 0, "node is not defined") |
| if (!(defined($imagename))); |
| if (!(defined($imagename))) { |
| return 0; |
| } |
| my $path = "/tftpboot/xcat/image/x86/"; |
| my $ide_grub = "$path" . "$imagename" . "-ide.grub"; |
| my $scsi_grub = "$path" . "$imagename" . "-scsi.grub"; |
| my $ide_pxe = "$path" . "$imagename" . "-ide.pxe"; |
| my $scsi_pxe = "$path" . "$imagename" . "-scsi.pxe"; |
| my @errors; |
| if (-e "$ide_grub") { |
| |
| #file exists |
| my $fs = -s "$ide_grub"; |
| if ($fs == 0) { |
| notify($ERRORS{'CRITICAL'}, 0, "filesize for $ide_grub is zero, deleted "); |
| unlink $ide_grub; |
| } |
| } |
| else { |
| |
| #notify($ERRORS{'OK'},0,"skipping $ide_grub file does not exist"); |
| } |
| if (-e "$scsi_grub") { |
| |
| #file exists |
| my $fs = -s "$scsi_grub"; |
| if ($fs == 0) { |
| notify($ERRORS{'CRITICAL'}, 0, "filesize for $scsi_grub is zero, deleted "); |
| unlink $scsi_grub; |
| } |
| } |
| else { |
| |
| #notify($ERRORS{'OK'},0,"skipping $scsi_grub file does not exist"); |
| } |
| if (-e "$ide_pxe") { |
| |
| #file exists |
| my $fs = -s "$ide_pxe"; |
| if ($fs == 0) { |
| notify($ERRORS{'CRITICAL'}, 0, "filesize for $ide_pxe is zero, deleted "); |
| unlink $ide_pxe; |
| } |
| } |
| else { |
| |
| #notify($ERRORS{'OK'},0,"skipping $ide_pxe file does not exist"); |
| } |
| if (-e "$scsi_pxe") { |
| |
| #file exists |
| my $fs = -s "$scsi_pxe"; |
| if ($fs == 0) { |
| notify($ERRORS{'CRITICAL'}, 0, "filesize for $scsi_grub is zero, deleted "); |
| unlink $scsi_pxe; |
| } |
| } |
| else { |
| |
| #notify($ERRORS{'OK'},0,"skipping file $scsi_pxe does not exist"); |
| } |
| |
| return 1; |
| |
| } ## end sub _check_pxe_grub_files |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_image_size |
| |
| Parameters : $image_name (optional) |
| Returns : 0 failure or size of image |
| Description : in size of Kilobytes |
| |
| =cut |
| |
| sub get_image_size { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Either use a passed parameter as the image name or use the one stored in this object's DataStructure |
| my $image_name = shift; |
| $image_name = $self->data->get_image_name() if !$image_name; |
| if (!$image_name) { |
| notify($ERRORS{'CRITICAL'}, 0, "image name could not be determined"); |
| return 0; |
| } |
| notify($ERRORS{'DEBUG'}, 0, "getting size of image: $image_name"); |
| |
| my $image_repository_path = $self->_get_image_repository_path(); |
| if (!$image_repository_path) { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to determine image repository location, returning 0"); |
| return 0; |
| } |
| |
| # Execute the command |
| my $du_command = "du -c $image_repository_path/$image_name* 2>&1"; |
| notify($ERRORS{'DEBUG'}, 0, "du command: $du_command"); |
| my $du_output = `$du_command`; |
| |
| # Save the exit status |
| my $du_exit_status = $?; |
| |
| #notify($ERRORS{'DEBUG'}, 0, "du exit staus: $du_exit_status, output:\n$du_output"); |
| |
| # Check the du command output |
| if ($du_exit_status > 0) { |
| notify($ERRORS{'WARNING'}, 0, "du exit status > 0: $du_exit_status, output:\n$du_output"); |
| return 0; |
| } |
| elsif ($du_output !~ /total/s) { |
| notify($ERRORS{'WARNING'}, 0, "du command did not produce expected output, du exit staus: $du_exit_status, output:\n$du_output"); |
| return 0; |
| } |
| |
| # Find the du output line containing 'total' |
| $du_output =~ /(\d+)\s+total/s; |
| my $size_bytes = $1; |
| |
| # Check the du command output |
| if (!$size_bytes) { |
| notify($ERRORS{'WARNING'}, 0, "du produced unexpected output: $du_exit_status, output:\n$du_output"); |
| return 0; |
| } |
| |
| # Calculate the size in MB |
| my $size_mb = int($size_bytes / 1024); |
| notify($ERRORS{'DEBUG'}, 0, "returning image size: $size_mb MB ($size_bytes bytes)"); |
| return $size_mb; |
| |
| } ## end sub get_image_size |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _get_image_repository_path |
| |
| Parameters : none, must be called as an xCAT object method |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub _get_image_repository_path { |
| my $self = shift; |
| my $return_template_path = shift; |
| |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Get the required variables from the DataStructure |
| my $management_node_id = $self->data->get_management_node_id(); |
| my $management_node_hostname = $self->data->get_management_node_hostname(); |
| my $install_path = $self->data->get_management_node_install_path(); |
| my $image_os_name = $self->data->get_image_os_name(); |
| my $image_os_type = $self->data->get_image_os_type(); |
| my $image_os_install_type = $self->data->get_image_os_install_type(); |
| my $image_os_source_path = $self->data->get_image_os_source_path(); |
| my $image_architecture = $self->data->get_image_architecture(); |
| |
| if (!(defined($image_os_name) && defined($image_os_type) && defined($image_os_install_type) && defined($image_os_source_path) && defined($image_architecture))) { |
| notify($ERRORS{'CRITICAL'}, 0, "some of the required data could not be retrieved"); |
| return 0; |
| } |
| |
| $return_template_path = 0 if !defined($return_template_path); |
| |
| notify($ERRORS{'DEBUG'}, 0, "OS=$image_os_name, OS type=$image_os_type, OS install type=$image_os_install_type, OS source=$image_os_source_path"); |
| |
| # Fix for Linux images on henry4 |
| if ( $management_node_hostname =~ /henry4/i |
| && $image_os_type =~ /linux/i |
| && $image_os_source_path eq 'image') |
| { |
| $image_os_source_path = 'linux_image'; |
| notify($ERRORS{'DEBUG'}, 0, "fixed Linux image path for henry4: image --> linux_image"); |
| } |
| |
| # Remove trailing / from $image_os_source_path if exists |
| $image_os_source_path =~ s/\/$//; |
| |
| # If image OS source path has a leading /, assume it was meant to be absolute |
| # Otherwise, prepend the install path |
| my $image_install_path; |
| if ($image_os_source_path =~ /^\//) { |
| |
| # If $image_os_source_path = '/centos5', use '/centos5' |
| $image_install_path = $image_os_source_path; |
| } |
| else { |
| |
| # If $image_os_source_path = 'centos5', use '/install/centos5' |
| # Note: $install_path has a leading / |
| $image_install_path = "$install_path/$image_os_source_path"; |
| } |
| |
| # Note: $XCAT_ROOT has a leading / |
| # Note: $image_install_path has a leading / |
| |
| # Check $return_template_path, either return repo path or template directory path |
| # This is done because the code to figure out the paths is mostly the same |
| # _get_image_repository_path calls this subroutine with the $return_template_path flag set |
| my $return_path; |
| if ($return_template_path) { |
| $return_path = "$XCAT_ROOT$image_install_path/$image_architecture"; |
| notify($ERRORS{'DEBUG'}, 0, "template path: $return_path"); |
| return $return_path; |
| } |
| elsif ($image_os_install_type eq 'kickstart') { |
| |
| # Kickstart installs use the xCAT path for both repo and tmpl paths |
| $return_path = "$XCAT_ROOT$image_install_path/$image_architecture"; |
| notify($ERRORS{'DEBUG'}, 0, "kickstart path: $return_path"); |
| return $return_path; |
| } |
| else { |
| |
| # Imaging installs use the xCAT path for the tmpl path, and the install path for the repo path |
| $return_path = "$image_install_path/$image_architecture"; |
| notify($ERRORS{'DEBUG'}, 0, "repository path: $return_path"); |
| return $return_path; |
| } |
| } ## end sub _get_image_repository_path |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _get_image_template_path |
| |
| Parameters : none, must be called as an xCAT object method |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub _get_image_template_path { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| return $self->_get_image_repository_path(1); |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _get_base_template_filename |
| |
| Parameters : none, must be called as an xCAT object method |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub _get_base_template_filename { |
| my $self = shift; |
| if (ref($self) !~ /xCAT/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| # Get some variables |
| my $image_os_name = $self->data->get_image_os_name(); |
| my $image_os_type = $self->data->get_image_os_type(); |
| |
| # Get the image template directory path |
| my $image_template_path = $self->_get_image_template_path(); |
| if (!$image_template_path) { |
| notify($ERRORS{'CRITICAL'}, 0, "image template path could not be determined"); |
| return 0; |
| } |
| |
| # Find the template file to use, from most specific to least |
| # Try OS-specific: <OS name>.tmpl |
| if (-e "$image_template_path/$image_os_name.tmpl") { |
| notify($ERRORS{'DEBUG'}, 0, "OS specific base image template file found: $image_template_path/$image_os_name.tmpl"); |
| return "$image_os_name.tmpl"; |
| } |
| elsif (-e "$image_template_path/$image_os_type.tmpl") { |
| notify($ERRORS{'DEBUG'}, 0, "OS type specific base image template file found: $image_template_path/$image_os_type.tmpl"); |
| return "$image_os_type.tmpl"; |
| } |
| elsif (-e "$image_template_path/default.tmpl") { |
| notify($ERRORS{'DEBUG'}, 0, "default base image template file found: $image_template_path/default.tmpl"); |
| return "default.tmpl"; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to find suitable base image template file in $image_template_path"); |
| return 0; |
| } |
| } ## end sub _get_base_template_filename |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| initialize() if (!$XCAT_ROOT); |
| 1; |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| __END__ |
| |
| =head1 BUGS and LIMITATIONS |
| |
| There are no known bugs in this module. |
| Please report problems to the VCL team (vcl_help@ncsu.edu). |
| |
| =head1 AUTHOR |
| |
| Aaron Peeler, aaron_peeler@ncsu.edu |
| Andy Kurth, andy_kurth@ncsu.edu |
| |
| =head1 SEE ALSO |
| |
| L<http://vcl.ncsu.edu> |
| |
| |
| =cut |