git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@2376 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			272 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| package xCAT::NameRange;
 | |
| require xCAT::Table;
 | |
| require Exporter;
 | |
| use strict;
 | |
| 
 | |
| #Perl implementation of namerange
 | |
| #  NOTE:  This is identical to xCAT::NodeRange except that no
 | |
| #		  database access occurs, no nodes are verified, and 
 | |
| #         no nodegroups are expanded.
 | |
| #		  Made a new utility since NodeRange is used EVERYWHERE in
 | |
| #		  xCAT code and did not want to risk de-stabilizing existing code.
 | |
| our @ISA = qw(Exporter);
 | |
| our @EXPORT = qw(namerange);
 | |
| 
 | |
| my $recurselevel=0;
 | |
| 
 | |
| 
 | |
| sub subnodes (\@@) {
 | |
|     #Subtract set of nodes from the first list
 | |
|     my $nodes = shift;
 | |
|     my $node;
 | |
|     foreach $node (@_) {
 | |
|         @$nodes = (grep(!/^$node$/,@$nodes));
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub expandatom {
 | |
| 	my $atom = shift;
 | |
|     my @nodes= ();
 | |
|     if ($atom =~ /^\(.*\)$/) {     # handle parentheses by recursively calling namerange()
 | |
|       $atom =~ s/^\((.*)\)$/$1/;
 | |
|       $recurselevel++;
 | |
|       return namerange($atom);
 | |
|     }
 | |
|     if ($atom =~ /@/) {
 | |
|           $recurselevel++;
 | |
|           return namerange($atom);
 | |
|      }
 | |
| 
 | |
| 	if ($atom =~ m/^\//) { # A regular expression - not supported in namerange
 | |
|           return ($atom);
 | |
| 	}
 | |
| 
 | |
| 	if ($atom =~ m/(.*)\[(.*)\](.*)/) { # square bracket range
 | |
| 	#for the time being, we are only going to consider one [] per atom
 | |
| 	#xcat 1.2 does no better
 | |
| 		my @subelems = split(/([\,\-\:])/,$2);
 | |
| 		my $subrange="";
 | |
| 		while (my $subelem = shift @subelems) {
 | |
| 			my $subop=shift @subelems;
 | |
| 			$subrange=$subrange."$1$subelem$3$subop";
 | |
| 		}
 | |
| 		foreach (split /,/,$subrange) {
 | |
| 			my @newnodes=expandatom($_);
 | |
| 			@nodes=(@nodes,@newnodes);
 | |
| 		}
 | |
| 		return @nodes;
 | |
| 	}
 | |
| 
 | |
| 	if ($atom =~ m/\+/) {  # process the + operator
 | |
| 		$atom =~ m/^([^0-9]*)([0-9]+)([^\+]*)\+([0-9]+)/;
 | |
| 		my $pref=$1;
 | |
| 		my $startnum=$2;
 | |
| 		my $suf=$3;
 | |
| 		my $end=$4+$startnum;
 | |
|         my $endnum = sprintf("%d",$end);
 | |
|         if (length ($startnum) > length ($endnum)) {
 | |
|           $endnum = sprintf("%0".length($startnum)."d",$end);
 | |
|         }
 | |
| 		foreach ("$startnum".."$endnum") {
 | |
| 			my @addnodes=expandatom($pref.$_.$suf);
 | |
| 			@nodes=(@nodes,@addnodes);
 | |
| 		}
 | |
| 		return (@nodes);
 | |
| 	}
 | |
| 
 | |
|     if ($atom =~ m/[-:]/) { # process the minus range operator
 | |
|       my $left;
 | |
|       my $right;
 | |
|       if ($atom =~ m/:/) {
 | |
|         ($left,$right)=split /:/,$atom;
 | |
|       } else {
 | |
|         my $count= ($atom =~ tr/-//);
 | |
|         if (($count % 2)==0) { #can't understand even numbers of - in range context
 | |
|          # we might not really be in range context
 | |
|            return  ($atom);
 | |
|         }
 | |
|         my $expr="([^-]+?".("-[^-]*"x($count/2)).")-(.*)";
 | |
|         $atom =~ m/$expr/;
 | |
|         $left=$1;
 | |
|         $right=$2;
 | |
|       }
 | |
|       if ($left eq $right) { #if they said node1-node1 for some strange reason
 | |
| 		return expandatom($left);
 | |
|       }
 | |
|       my @leftarr=split(/(\d+)/,$left);
 | |
|       my @rightarr=split(/(\d+)/,$right);
 | |
|       if (scalar(@leftarr) != scalar(@rightarr)) { #Mismatch formatting..
 | |
|         # guess it's meant to be a nodename
 | |
|         return  ($atom);
 | |
|       }
 | |
|       my $prefix = "";
 | |
|       my $suffix = "";
 | |
|       foreach (0..$#leftarr) {
 | |
|         my $idx = $_;
 | |
|         if ($leftarr[$idx] =~ /^\d+$/ and $rightarr[$idx] =~ /^\d+$/) { #pure numeric component
 | |
|           if ($leftarr[$idx] ne $rightarr[$idx]) { #We have found the iterator (only supporting one for now)
 | |
|             my $prefix = join('',@leftarr[0..($idx-1)]); #Make a prefix of the pre-validated parts
 | |
|             my $luffix; #However, the remainder must still be validated to be the same
 | |
|             my $ruffix;
 | |
|             if ($idx eq $#leftarr) {
 | |
|               $luffix="";
 | |
|               $ruffix="";
 | |
|             } else {
 | |
|               $ruffix = join('',@rightarr[($idx+1)..$#rightarr]);
 | |
|               $luffix = join('',@leftarr[($idx+1)..$#leftarr]);
 | |
|             }
 | |
|             if ($luffix ne $ruffix) { #the suffixes mismatched..
 | |
|               return ($atom);
 | |
|             }
 | |
|             foreach ($leftarr[$idx]..$rightarr[$idx]) {
 | |
|               my @addnodes=expandatom($prefix.$_.$luffix);
 | |
|               @nodes=(@nodes,@addnodes);
 | |
|             }
 | |
|             return (@nodes); #the return has been built, return, exiting loop and all
 | |
|           }
 | |
|         } elsif ($leftarr[$idx] ne $rightarr[$idx]) {
 | |
|           return ($atom);
 | |
|         }
 | |
|         $prefix .= $leftarr[$idx]; #If here, it means that the pieces were the same, but more to come
 | |
|       }
 | |
|       #I cannot conceive how the code could possibly be here, but whatever it is, it must be questionable
 | |
|       return  ($atom);
 | |
| 	}
 | |
| 
 | |
| 	return ($atom);
 | |
| }
 | |
| 
 | |
| sub namerange {
 | |
|   #We for now just do left to right operations
 | |
|   my $range=shift;
 | |
|   my %nodes = ();
 | |
|   my %delnodes = ();
 | |
|   my $op = ",";
 | |
|   my @elems = split(/(,(?![^[]*?])(?![^\(]*?\)))/,$range); # commas outside of [] or ()
 | |
|   if (scalar(@elems)==1) {
 | |
|       @elems = split(/(@(?![^\(]*?\)))/,$range);  # only split on @ when no , are present (inner recursion)
 | |
|   }
 | |
| 
 | |
|   while (my $atom = shift @elems) {
 | |
|     if ($atom =~ /^-/) {           # if this is an exclusion, strip off the minus, but remember it
 | |
|       $atom = substr($atom,1);
 | |
|       $op = $op."-";
 | |
|     }
 | |
| 
 | |
|     if ($atom =~ /^\^(.*)$/) {    # get a list of nodes from a file
 | |
|       open(NRF,$1);
 | |
|       while (<NRF>) {
 | |
|         my $line=$_;
 | |
|         unless ($line =~ m/^[\^#]/) {
 | |
|           $line =~ m/^([^:	 ]*)/;
 | |
|           my $newrange = $1;
 | |
|           chomp($newrange);
 | |
|           $recurselevel++;
 | |
|           my @filenodes = namerange($newrange);
 | |
|           foreach (@filenodes) {
 | |
|             $nodes{$_}=1;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       close(NRF);
 | |
|       next;
 | |
|     }
 | |
| 
 | |
|     my %newset = map { $_ =>1 } expandatom($atom);    # expand the atom and make each entry in the resulting array a key in newset
 | |
| 
 | |
|     if ($op =~ /@/) {       # compute the intersection of the current atom and the node list we have received before this
 | |
|       foreach (keys %nodes) {
 | |
|         unless ($newset{$_}) {
 | |
|           delete $nodes{$_};
 | |
|         }
 | |
|       }
 | |
|     } elsif ($op =~ /,-/) {        # add the nodes from this atom to the exclude list
 | |
| 		foreach (keys %newset) {
 | |
| 			$delnodes{$_}=1; #delay removal to end
 | |
| 		}
 | |
| 	} else {          # add the nodes from this atom to the total node list
 | |
| 		foreach (keys %newset) {
 | |
| 			$nodes{$_}=1;
 | |
| 		}
 | |
| 	}
 | |
| 	$op = shift @elems;
 | |
| 
 | |
|     }    # end of main while loop
 | |
| 
 | |
|     # Now remove all the exclusion nodes
 | |
|     foreach (keys %nodes) {
 | |
| 		if ($delnodes{$_}) {
 | |
| 			delete $nodes{$_};
 | |
| 		}
 | |
|     }
 | |
|     if ($recurselevel) {
 | |
|         $recurselevel--;
 | |
|     }
 | |
|     return sort (keys %nodes);
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| 1;
 | |
| 
 | |
| =head1 NAME
 | |
| 
 | |
| xCAT::NameRange - Perl module for xCAT namerange expansion
 | |
| 
 | |
| =head1 SYNOPSIS
 | |
| 
 | |
| 	use xCAT::NameRange;
 | |
| 	my @nodes=namerange("storage@rack1,node[1-200],^/tmp/nodelist,node300-node400,node401+10,500-550");
 | |
| 
 | |
| =head1 DESCRIPTION
 | |
| 
 | |
| namerange interprets xCAT noderange formatted strings and returns a list of 
 | |
| names. The following two operations are supported on elements, and interpreted 
 | |
| left to right:
 | |
| 
 | |
|   , union next element with everything to the left.
 | |
| 
 | |
|   @ take intersection of element to the right with everything on the left 
 | |
|    (i.e. mask out anything to the left not belonging to what is described to 
 | |
|    the right)
 | |
| 
 | |
| Each element can be a number of things:
 | |
| 
 | |
|   A node name, i.e.:
 | |
| 
 | |
| =item * node1
 | |
| 
 | |
| A hyphenated node range (only one group of numbers may differ between the left and right hand side, and those numbers will increment in a base 10 fashion):
 | |
| 
 | |
| node1-node200 node1-compute-node200-compute
 | |
| node1:node200 node1-compute:node200-compute
 | |
| 
 | |
| A namerange denoted by brackets:
 | |
| 
 | |
| node[1-200] node[001-200]
 | |
| 
 | |
| A regular expression describing the namerange:
 | |
| 
 | |
| /d(1.?.?|200)
 | |
| 
 | |
| A node plus offset (this increments the first number found in nodename):
 | |
| 
 | |
| node1+199
 | |
| 
 | |
| And most of the above substituting groupnames.
 | |
| 3C
 | |
| 3C
 | |
| 
 | |
| NameRange tries to be intelligent about detecting padding, so you can:
 | |
| node001-node200
 | |
| And it will increment according to the pattern.
 | |
| 
 | |
| 
 | |
| =head1 COPYRIGHT
 | |
| 
 | |
| Copyright 2007 IBM Corp.  All rights reserved.
 | |
| 
 | |
| 
 | |
| =cut
 |