2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-06-17 11:50:32 +00:00

Merge pull request #452 from hu-weihua/getadapters

Cool. Will merge it, eventually.
This commit is contained in:
neo954
2015-12-15 12:44:26 +08:00
4 changed files with 702 additions and 0 deletions

View File

@ -0,0 +1,53 @@
Predict network adapter name before deployment
==============================================
Traditionally, network interfaces in Linux are enumerated as eth[0123…], but these names do not necessarily correspond to actual labels on the chassis. customer need a methods to get consistent and predictable network device name before provision or network configuration. xCAT provide a tool ``getadapters`` to help customer to resolve this problem.
**[Note]** : This feature needs to restart your target sever which you want to obtain network adapter from.
How to use get adapters
-----------------------
Using below command to obtain the network adapters information ::
getadapters <noderange>
Then will get output like below ::
The whole scan result:
--------------------------------------
node1:1:hitname=enP3p3s0f0|pci=/pci0003:00/0003:00:00.0/0003:01:00.0/0003:02:01.0/0003:03:00.0|mac=98be9459ea24|candidatename=enP3p3s0f0,enx98be9459ea24|vendor=Broadcom Corporation|modle=NetXtreme II BCM57800 1/10 Gigabit Ethernet
node1:2:hitname=enP3p3s0f1|pci=/pci0003:00/0003:00:00.0/0003:01:00.0/0003:02:01.0/0003:03:00.1|mac=98be9459ea25|candidatename=enP3p3s0f1,enx98be9459ea25|vendor=Broadcom Corporation|modle=NetXtreme II BCM57800 1/10 Gigabit Ethernet
node1:3:hitname=enP3p3s0f2|pci=/pci0003:00/0003:00:00.0/0003:01:00.0/0003:02:01.0/0003:03:00.2|mac=98be9459ea26|candidatename=enP3p3s0f2,enx98be9459ea26|vendor=Broadcom Corporation|modle=NetXtreme II BCM57800 1/10 Gigabit Ethernet
node1:4:hitname=enP3p3s0f3|pci=/pci0003:00/0003:00:00.0/0003:01:00.0/0003:02:01.0/0003:03:00.3|mac=98be9459ea27|candidatename=enP3p3s0f3,enx98be9459ea27|vendor=Broadcom Corporation|modle=NetXtreme II BCM57800 1/10 Gigabit Ethernet
node1:5:pci=0001:01:00.0|modle=Mellanox Technologies MT27600 [Connect-IB]
--------------------------------------
node2:1:hitname=enP3p3s0f0|pci=/pci0003:00/0003:00:00.0/0003:01:00.0/0003:02:01.0/0003:03:00.0|mac=98be9459ea24|candidatename=enP3p3s0f0,enx98be9459ea24|vendor=Broadcom Corporation|modle=NetXtreme II BCM57800 1/10 Gigabit Ethernet
node2:2:hitname=enP3p3s0f1|pci=/pci0003:00/0003:00:00.0/0003:01:00.0/0003:02:01.0/0003:03:00.1|mac=98be9459ea25|candidatename=enP3p3s0f1,enx98be9459ea25|vendor=Broadcom Corporation|modle=NetXtreme II BCM57800 1/10 Gigabit Ethernet
Every node gets a separate section to display its all network adapters information, every network adapter owns single line which start as node name and followed by index and other information.
xCAT try its best to collect more information for each network adapter, but cant guarantee collect same much information for every one. If a network adapter can be derived by xcat genesis, this adapter will have a predictable name, if it cant be, it only has the information xcat can obtain.
below are the possible information:
* **hitname**: the consistent name which can be used in ``confignic`` derectly in operating system which follow the same naming scheme with rhels7. (``confignic`` doesnt need to do more work)
* **pci**: the pci location
* **mac**: the MAC address
* **candidatename**: All the names which satisfy predictable network device naming scheme, if customer needs to customize their network adapter name, they can choose one of them. (``confignic`` needs to do more work to support this. if customer want to use their own name, xcat should offer a interface to get customers input and change this column)
* **vendor**: the vender of network device
* **modle**: the modle of network device

View File

@ -0,0 +1,85 @@
=head1 NAME
B<getadapters> - Obtain all network adapters's predictable name and some other information before provision or network configuration.
=head1 SYNOPSIS
B<getadapters> I<noderange> [B<-f>]
B<getadapters> [B<-h>|B<--help>|B<-v>|B<--version>|B<-V>]
=head1 DESCRIPTION
Traditionally, network interfaces in Linux are enumerated as eth[0123<32>], but these names do not necessarily correspond to actual labels on the chassis. B<getadapters> help customer to get predictable network device name and some other network adapter information before provision or network configuration.
B<getadapters> use genesis to collect network adapters information, so that mean it need to restart the target node.
B<getadapters> follows below scheme:
If the target node is scaned for the first time, B<getadapters> will trigger genesis to collect information then save the information at local.
If the target node has ever been scaned, i.e. this node has network device information in local, B<getadapters> use the local information first.
If user doesn't want to use local information, can use B<-f> option to force to trigger new round scan process.
if part nodes of I<noderange> don't have network device information in local and the rest have, B<getadapters> only trigger real scan process for these nodes which don't have local information, the nodes have network device information in local, B<getadapters> still use the local information first.
B<getadapters> tries to collect more information for the target network device, but doesn't guarantee collect same much information for every network device.
Below are the possible information can be collect up to now:
B<hitname>: the consistent name which can be used by confignic directly in operating system which follow the same naming scheme with rhels7
B<pci>: the pci location
B<mac>: the MAC address
B<candidatename>: All the names which satisfy predictable network device naming scheme. I<(if xcat enhance confignic command later, user can use these names to configure their network adapter, even customize their name)>
B<vender>: the vender of network device
B<model>: the model of network device
=head1 OPTIONS
B<-h>
Display usage message.
B<-v>
Command Version.
B<-V>
Display verbose message.
B<-f>
Force to trigger new round scan. ignore the data collected before.
=head1 EXAMPLES
1. To collect node[1-3]'s network device information, enter:
getadapters node[1-2]
Output is similar to:
-->Starting scan for: node1,node2
The whole scan result:
--------------------------------------
[node1] with no need for scan due to old data exist, using the old data:
node1:1:mac=98be9459ea24|pci=/0003:03:00.0|candidatename=enx98be9459ea24|vender=Broadcom Corporation
node1:2:mac=98be9459ea25|pci=/0003:03:00.1|candidatename=enx98be9459ea25|vender=Broadcom Corporation
--------------------------------------
[node2] scan successfully, below are the latest data
node2:1:mac=98be9459ea34|pci=/0003:03:00.0|candidatename=enx98be9459ea34|vender=Broadcom Corporation
node2:2:mac=98be9459ea35|pci=/0003:03:00.1|candidatename=enx98be9459ea35|vender=Broadcom Corporation
Every node gets a separate section to display its all network adapters information, every network adapter owns single line which start as node name and followed by index and other information.
2. Force to trigger new round scan
getadatpers node -f
=head1 SEE ALSO
L<noderange(3)|noderange.3>

View File

@ -0,0 +1,112 @@
#!/bin/bash
#set -x
XCATPORT=3001
export XCATPORT
#XCATMASTER="10.3.5.21"
ADAPTERFILE="/tmp/adapterinfo"
SCANNICLOG="/tmp/adapterscan.log"
rm -f "$SCANNICLOG" >/dev/null 2>&1
if [ -f "$ADAPTERFILE" ]; then
echo "rm -f $ADAPTERFILE" > "$SCANNICLOG"
rm -f "$ADAPTERFILE" >> "$SCANNICLOG" 2>&1
fi
echo "<xcatrequest>
<command>findadapter</command>
<clienttype>cli</clienttype>
<hostname>$HOSTNAME</hostname>" >> "$ADAPTERFILE"
#scan adapters have recognized by operating system
for n in /sys/class/net/*; do
nic=${n##/sys/class/net/}
if [ "$nic" == "lo" ]; then
continue
else
echo '<nic>' >> "$ADAPTERFILE"
tmp=`udevadm info /sys/class/net/"$nic" | grep " INTERFACE" | awk -F '=' '{print $2}'`
if [ -n "$tmp" ]; then
echo "<interface>$tmp</interface>" >> "$ADAPTERFILE"
fi
tmp=`udevadm info /sys/class/net/"$nic" | grep ID_NET_NAME | awk -F '=' '{print $2}'|sort -u| tr -s "\n" "," | sed "s/,$//g"`
if [ -n "$tmp" ]; then
echo "<predictablename>$tmp</predictablename>" >> "$ADAPTERFILE"
fi
tmp=`udevadm info /sys/class/net/"$nic" | grep DEVPATH | awk -F 'devices' '{print $2}'`
if [ -n "$tmp" ]; then
echo "<pcilocation>${tmp%/net*}</pcilocation>" >> "$ADAPTERFILE"
fi
tmp=`udevadm info /sys/class/net/"$nic" | grep ID_NET_NAME_MAC | awk -F '=' '{print $2}'`
if [ -n "$tmp" ]; then
echo "<mac>${tmp##*enx}</mac>" >> "$ADAPTERFILE"
fi
tmp=`udevadm info /sys/class/net/"$nic" | grep ID_VENDOR_FROM_DATABASE | awk -F '=' '{print $2}' | tr -s "\n" "," | sed "s/,$//g"`
if [ -n "$tmp" ]; then
echo "<vendor>$tmp</vendor>" >> "$ADAPTERFILE"
fi
tmp=`udevadm info /sys/class/net/"$nic" | grep ID_MODEL_FROM_DATABASE | awk -F '=' '{print $2}'`
if [ -n "$tmp" ]; then
echo "<model>$tmp</model>" >> "$ADAPTERFILE"
fi
echo '</nic>' >> "$ADAPTERFILE"
fi
done
for pci in `lspci |awk '/Ethernet/ {print $1}' `; do
if ! grep -q "$pci" "$ADAPTERFILE" 2>/dev/null; then
tmp=`lspci |grep "$pci"`
echo '<nic>' >> "$ADAPTERFILE"
echo "<pcilocation>$pci</pcilocation>" >> "$ADAPTERFILE"
echo "<model>${tmp##*:}</model>" >> "$ADAPTERFILE"
echo '</nic>' >> "$ADAPTERFILE"
fi
done
for pci in `lspci |awk '/Network/ {print $1}' `; do
if ! grep -q "$pci" "$ADAPTERFILE" 2>/dev/null; then
tmp=`lspci |grep "$pci"`
echo '<nic>' >> "$ADAPTERFILE"
echo "<pcilocation>$pci</pcilocation>" >> "$ADAPTERFILE"
echo "<model>${tmp##*: }</model>" >> "$ADAPTERFILE"
echo '</nic>' >> "$ADAPTERFILE"
fi
done
for pci in `lspci |awk '/Mellanox/ {print $1}' `; do
if ! grep -q "$pci" "$ADAPTERFILE" 2>/dev/null; then
tmp=`lspci |grep "$pci"`
echo '<nic>' >> "$ADAPTERFILE"
echo "<pcilocation>$pci</pcilocation>" >> "$ADAPTERFILE"
echo "<model>${tmp##*: }</model>" >> "$ADAPTERFILE"
echo '</nic>' >> "$ADAPTERFILE"
fi
done
echo "</xcatrequest>" >> "$ADAPTERFILE"
#cat "$ADAPTERFILE"
if [ -n "$XCATMASTER" ]; then
if [ -f /etc/xcat/cert.pem -a -f /etc/xcat/certkey.pem ]; then #use client cert if available
echo "using /etc/xcat/certkey.pem and /etc/xcat/cert.pem to transmit scan result to $XCATMASTER" >> "$SCANNICLOG"
openssl s_client -key /etc/xcat/certkey.pem -cert /etc/xcat/cert.pem -connect $XCATMASTER:$XCATPORT <"$ADAPTERFILE" >>"$SCANNICLOG" 2>&1
else
echo "transmit scan result without customer certificate to $XCATMASTER" >> "$SCANNICLOG"
openssl s_client -connect $XCATMASTER:$XCATPORT <"$ADAPTERFILE" >>"$SCANNICLOG" 2>&1
fi
else
dhcps=`awk -F ' |;' '/dhcp-server/ { print $(NF-1) }' /var/lib/dhclient/dhclient.leases | tail -n 1`
if [ -n "$dhcps" ]; then
if [ -f /etc/xcat/cert.pem -a -f /etc/xcat/certkey.pem ]; then #use client cert if available
echo "using /etc/xcat/certkey.pem and /etc/xcat/cert.pem to transmit scan result to $dhcps" >> "$SCANNICLOG"
openssl s_client -key /etc/xcat/certkey.pem -cert /etc/xcat/cert.pem -connect $dhcps:$XCATPORT <"$ADAPTERFILE" >>"$SCANNICLOG" 2>&1
else
echo "transmit scan result without customer certificate to $dhcps" >> "$SCANNICLOG"
openssl s_client -connect $dhcps:$XCATPORT <"$ADAPTERFILE" >>"$SCANNICLOG" 2>&1
fi
fi
fi

View File

@ -0,0 +1,452 @@
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#-------------------------------------------------------
=head1
xCAT plugin package to handle getadapters management
Supported command:
getadapters->getadapters
findadapter->getadapters
=cut
#-------------------------------------------------------
package xCAT_plugin::getadapter;
BEGIN{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
}
use lib "$::XCATROOT/lib/perl";
use xCAT::Table;
use xCAT::Utils;
use xCAT::MsgUtils;
use Data::Dumper;
use Getopt::Long;
use File::Path;
use IO::Select;
use Term::ANSIColor;
use Time::Local;
my %usage = (
"getadapters" => "Usage:\n\tgetadapters [-h|--help|-v|--version|V]\n\tgetadapters <noderange> [-f]",
);
my $inforootdir = "/var/lib/xcat/adapters/";
my $VERBOSE=0;
#-------------------------------------------------------
=head3 handled_commands
Return list of commands handled by this plugin
=cut
#-------------------------------------------------------
sub handled_commands
{
return {
getadapters => "getadapter",
findadapter => "getadapter",
};
}
#-------------------------------------------------------
=head3 process_request
Process the command
=cut
#-------------------------------------------------------
sub process_request
{
my $request = shift;
my $callback = shift;
my $subreq = shift;
my $command = $request->{command}->[0];
$SIG{CHLD}='DEFAULT';
if ($command eq "getadapters"){
&handle_getadapters($request, $callback, $subreq);
}
if ($command eq "findadapter"){
&handle_findadapter($request, $callback);
}
return;
}
sub handle_getadapters{
my $request = shift;
my $callback = shift;
my $subreq = shift;
my $command = $request->{command}->[0];
my @args=();
my $HELP;
my $VERSION;
my $FORCE;
if (ref($request->{arg})) {
@args=@{$request->{arg}};
} else {
@args=($request->{arg});
}
@ARGV = @args;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
if (!GetOptions("h|help" => \$HELP,
"v|version" => \$VERSION,
"f" => \$FORCE,
"V" => \$VERBOSE
) ) {
if($usage{$command}) {
my $rsp = {};
$rsp->{error}->[0] = $usage{$command};
$rsp->{errorcode}->[0] = 1;
$callback->($rsp);
}
return;
}
if ($HELP) {
if($usage{$command}) {
my %rsp;
$rsp{data}->[0]=$usage{$command};
$callback->(\%rsp);
}
return;
}
if ($VERSION) {
my $ver = xCAT::Utils->Version();
my %rsp;
$rsp{data}->[0]="$ver";
$callback->(\%rsp);
return;
}
my $tmpnodes = join(",", @{$request->{node}});
my $tmpargs = join(",", @args);
xCAT::MsgUtils->trace($VERBOSE,"d","getadapters: handling command <$command $tmpnodes $tmpargs>");
if($FORCE || ! -d $inforootdir) {
my @tmpnodes = @{$request->{node}};
$request->{missnode} = \@tmpnodes;
&scan_adapters($request, $callback, $subreq);
}else{
my @nodes = @{$request->{node}};
my $node;
my @missnodes = ();
foreach $node (@nodes){
if ( ! -e "$inforootdir/$node.info" || -z "$inforootdir/$node.info"){
push @missnodes,$node;
}
}
if(scalar(@missnodes) != 0){
$request->{missnode} = \@missnodes;
&scan_adapters($request, $callback, $subreq);
}
}
&get_info_from_local($request, $callback);
return;
}
sub handle_findadapter{
my $request = shift;
my $callback = shift;
my $hostname = $request->{hostname}->[0];
xCAT::MsgUtils->trace($VERBOSE,"d","getadapters: receiving a findadapter response from $hostname");
my $nicnum = scalar @{$request->{nic}};
my $content = "";
#print "-----------nicnum = $nicnum----------------\n";
for (my $i = 0; $i < $nicnum; $i++) {
$content .= "$i:";
if(exists($request->{nic}->[$i]->{interface})){
$content .= "hitname=".$request->{nic}->[$i]->{interface}->[0]."|";
}
if(exists($request->{nic}->[$i]->{pcilocation})){
$content .= "pci=".$request->{nic}->[$i]->{pcilocation}->[0]."|";
}
if(exists($request->{nic}->[$i]->{mac})){
$content .= "mac=".$request->{nic}->[$i]->{mac}->[0]."|";
}
if(exists($request->{nic}->[$i]->{predictablename})){
$content .= "candidatename=".$request->{nic}->[$i]->{predictablename}->[0]."|";
}
if(exists($request->{nic}->[$i]->{vendor})){
$content .= "vendor=".$request->{nic}->[$i]->{vendor}->[0]."|";
}
if(exists($request->{nic}->[$i]->{model})){
$content .= "model=".$request->{nic}->[$i]->{model}->[0];
}
$content .= "\n";
}
$content =~ s/\n$//;
my $fd;
if(!open($fd,">$inforootdir/$hostname.info")) {
xCAT::MsgUtils->trace($VERBOSE,"d","findadapter: can't open $inforootdir/$hostname.info to record adapter info");
}else{
print $fd "$content";
close($fd);
}
return;
}
sub scan_adapters{
my $request = shift;
my $callback = shift;
my $subreq = shift;
my @targetscannodes = @{$request->{missnode}};
if (scalar(@{$request->{node}}) == 0){
return 1;
}
my $tmptargetnodes = join(",", @targetscannodes);
xCAT::MsgUtils->trace($VERBOSE,"d","getadapters: issue new scaning for $tmptargetnodes");
my %autorsp;
$autorsp{data}->[0]="-->Starting scan for: $tmptargetnodes";
$callback->(\%autorsp);
if ( ! -d $inforootdir){
mkpath("$inforootdir");
}
my $node;
foreach $node (@targetscannodes){
if ( -e "$inforootdir/$node.info"){
rename("$inforootdir/$node.info", "$inforootdir/$node.info.bak");
xCAT::MsgUtils->trace($VERBOSE,"d","getadapters: move $inforootdir/$node.info to $inforootdir/$node.info.bak");
}else{
open OUT,">$inforootdir/$node.first"
}
}
#do scan stuff
my $pid;
my $forkcount = 0;
my %pidrecord;
foreach $node (@targetscannodes){
$pid = xCAT::Utils->xfork();
if (!defined($pid)){
$autorsp{info}->[0]="failed to fork process to restart $node";
$callback->(\%autorsp);
deletenode($request->{missnode}, "$node");
last;
}elsif ($pid == 0){
# Child process
xCAT::MsgUtils->trace($VERBOSE,"d","getadapters: fork new process $$ to start scaning $node");
my $outref = xCAT::Utils->runxcmd(
{
command => ['nodeset'],
node => ["$node"],
arg => ['runcmd=getadapter'],
},
,$subreq, 0, 1);
if($::RUNCMD_RC != 0){
my $tmp = join(" ", @$outref);
$autorsp{data}->[0]="$tmp";
$callback->(\%autorsp);
exit(1);
}
my $tab = xCAT::Table->new("nodehm");
my $hmhash = $tab->getNodeAttribs(["$node"], ['mgt']);
$tab->close();
if ($hmhash->{mgt} eq "ipmi"){
$outref = xCAT::Utils->runxcmd(
{
command => ["rsetboot"],
node => ["$node"],
arg => ['net'],
},
,$subreq, 0, 1);
if($::RUNCMD_RC != 0){
my $tmp = join(" ", @$outref);
$autorsp{data}->[0]="$tmp";
$callback->(\%autorsp);
exit(1);
}
$outref = xCAT::Utils->runxcmd(
{
command => ['rpower'],
node => ["$node"],
arg => ['reset'],
},
,$subreq, 0, 1);
if($::RUNCMD_RC != 0){
my $tmp = join(" ", @$outref);
$autorsp{data}->[0]="$tmp";
$callback->(\%autorsp);
exit(1);
}
}else{
$outref = xCAT::Utils->runxcmd(
{
command => ["rnetboot"],
node => ["$node"],
},
,$subreq, 0, 1);
if($::RUNCMD_RC != 0){
my $tmp = join(" ", @$outref);
$autorsp{data}->[0]="$tmp";
$callback->(\%autorsp);
exit(1);
}
}
# Exit process
exit(0);
}
# Parent process
$forkcount++;
$pidrecord{$pid} = $node;
}
# Wait for all processes to end
if($forkcount == 0){
deletenode($request->{missnode}, "all");
return 1;
}else{
while($forkcount){
my $cpid;
while (($cpid=waitpid(-1,WNOHANG)) > 0) {
my $cpr=$?;
if($cpr>0){
deletenode($request->{missnode}, "$pidreord{$cpid}");
}
$forkcount--;
}
sleep 0.1;
}
}
check_scan_result($request, $callback);
return 0;
}
sub check_scan_result{
my $request = shift;
my $callback = shift;
my $retry =60;
my $rsp = {};
my $nodenum = scalar @{$request->{missnode}};
my $backnum = 0;
if (exists($request->{missnode}) && (scalar(@{$request->{missnode}}) > 0)){
while($retry && $backnum != $nodenum){
sleep 10;
$retry--;
foreach $backnode (@{$request->{missnode}}){
if( -e "$inforootdir/$backnode.info" ){
$backnum++;
deletenode($request->{missnode}, "$backnode");
}
}
}
}
if($retry == 0 && $backnum != $nodenum){
my $tmpnode = join(",", @{$request->{missnode}});
push @{$rsp->{data}}, "waiting scan result for $tmpnode time out";
$callback->($rsp);
}
}
sub get_info_from_local{
my $request = shift;
my $callback = shift;
my $retry = 60;
my $rsp = {};
push @{$rsp->{data}}, "\nThe whole scan result:";
if (scalar(@{$request->{node}}) == 0){
$callback->({
error=>[qq{Please indicate the nodes which are needed to scan}],
errorcode=>[1]});
return 1;
}
my $node;
foreach $node (@{$request->{node}}){
my $readfile=1;
push @{$rsp->{data}}, "--------------------------------------";
if ( ! -e "$inforootdir/$node.info" && ! -e "$inforootdir/$node.info.bak" ){
push @{$rsp->{data}}, "[$node] Scan failed and without old data. there isn't data to show";
$readfile=0;
}elsif( ! -e "$inforootdir/$node.info" && -e "$inforootdir/$node.info.bak" ){
rename("$inforootdir/$node.info.bak","$inforootdir/$node.info");
push @{$rsp->{data}}, "[$node] Scan failed but old data exist, using the old data:";
}elsif( -e "$inforootdir/$node.info" && ! -e "$inforootdir/$node.info.bak" && ! -e "$inforootdir/$node.first"){
push @{$rsp->{data}}, "[$node] with no need for scan due to old data exist, using the old data:";
}elsif(-e "$inforootdir/$node.info" && ! -e "$inforootdir/$node.info.bak" && -e "$inforootdir/$node.first"){
unlink "$inforootdir/$node.first";
push @{$rsp->{data}}, "[$node] scan successfully, below are the latest data:";
}else{
unlink "$inforootdir/$node.info.bak";
push @{$rsp->{data}}, "[$node] scan successfully, below are the latest data:";
}
if($readfile){
if( -z "$inforootdir/$node.info"){
push @{$rsp->{data}}, "[$node] the file is empty, nothing to show";
}else{
if (open($myfile, "$inforootdir/$node.info")) {
while ($line = <$myfile>) {
push @{$rsp->{data}}, "$node:$line";
}
close($myfile);
}else{
push @{$rsp->{data}}, "[$node] Can't open $inforootdir/$node.info";
}
}
}
}
$callback->($rsp);
return;
}
sub deletenode{
my $arrref = shift;
my $targetnode = shift;
my $arrcount = scalar @$arrref;
my $targetindex=0;
if( "$targetnode" ne "all" ){
for (my $i = 0; $i < $arrcount; $i++){
if ("$arrref->[$i]" eq "$targetnode"){
$targetindex = $i;
last;
}
}
for (my $i = $targetindex; $i < $arrcount-1; $i++){
$arrref->[$i] = $arrref->[$i+1] ;
}
pop @$arrref;
}else{
for (my $i = 0; $i < $arrcount; $i++){
pop @$arrref;
}
}
}
1;