diff --git a/docs/source/advanced/networks/getadapters.rst b/docs/source/advanced/networks/getadapters.rst new file mode 100755 index 000000000..16cb6a06b --- /dev/null +++ b/docs/source/advanced/networks/getadapters.rst @@ -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 + +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 can’t 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 can’t 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`` doesn’t 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 customer’s input and change this column) + +* **vendor**: the vender of network device + +* **modle**: the modle of network device + + + diff --git a/xCAT-client/pods/man1/getadapters.1.pod b/xCAT-client/pods/man1/getadapters.1.pod new file mode 100644 index 000000000..99b4ad479 --- /dev/null +++ b/xCAT-client/pods/man1/getadapters.1.pod @@ -0,0 +1,85 @@ +=head1 NAME + +B - Obtain all network adapters's predictable name and some other information before provision or network configuration. + +=head1 SYNOPSIS + +B I [B<-f>] + +B [B<-h>|B<--help>|B<-v>|B<--version>|B<-V>] + +=head1 DESCRIPTION + +Traditionally, network interfaces in Linux are enumerated as eth[0123…], but these names do not necessarily correspond to actual labels on the chassis. B help customer to get predictable network device name and some other network adapter information before provision or network configuration. + +B use genesis to collect network adapters information, so that mean it need to restart the target node. + +B follows below scheme: + +If the target node is scaned for the first time, B 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 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 don't have network device information in local and the rest have, B only trigger real scan process for these nodes which don't have local information, the nodes have network device information in local, B still use the local information first. + +B 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: the consistent name which can be used by confignic directly in operating system which follow the same naming scheme with rhels7 +B: the pci location +B: the MAC address +B: 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: the vender of network device +B: 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 + + diff --git a/xCAT-genesis-scripts/bin/getadapter b/xCAT-genesis-scripts/bin/getadapter new file mode 100755 index 000000000..1f907230e --- /dev/null +++ b/xCAT-genesis-scripts/bin/getadapter @@ -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 " +findadapter +cli +$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 '' >> "$ADAPTERFILE" + tmp=`udevadm info /sys/class/net/"$nic" | grep " INTERFACE" | awk -F '=' '{print $2}'` + if [ -n "$tmp" ]; then + echo "$tmp" >> "$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 "$tmp" >> "$ADAPTERFILE" + fi + tmp=`udevadm info /sys/class/net/"$nic" | grep DEVPATH | awk -F 'devices' '{print $2}'` + if [ -n "$tmp" ]; then + echo "${tmp%/net*}" >> "$ADAPTERFILE" + fi + tmp=`udevadm info /sys/class/net/"$nic" | grep ID_NET_NAME_MAC | awk -F '=' '{print $2}'` + if [ -n "$tmp" ]; then + echo "${tmp##*enx}" >> "$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 "$tmp" >> "$ADAPTERFILE" + fi + tmp=`udevadm info /sys/class/net/"$nic" | grep ID_MODEL_FROM_DATABASE | awk -F '=' '{print $2}'` + if [ -n "$tmp" ]; then + echo "$tmp" >> "$ADAPTERFILE" + fi + echo '' >> "$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 '' >> "$ADAPTERFILE" + echo "$pci" >> "$ADAPTERFILE" + echo "${tmp##*:}" >> "$ADAPTERFILE" + echo '' >> "$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 '' >> "$ADAPTERFILE" + echo "$pci" >> "$ADAPTERFILE" + echo "${tmp##*: }" >> "$ADAPTERFILE" + echo '' >> "$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 '' >> "$ADAPTERFILE" + echo "$pci" >> "$ADAPTERFILE" + echo "${tmp##*: }" >> "$ADAPTERFILE" + echo '' >> "$ADAPTERFILE" + fi +done + +echo "" >> "$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 + diff --git a/xCAT-server/lib/xcat/plugins/getadapter.pm b/xCAT-server/lib/xcat/plugins/getadapter.pm new file mode 100644 index 000000000..6d028405b --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/getadapter.pm @@ -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 [-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;