#!/usr/bin/perl -wT # # EMULAB-COPYRIGHT # Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group. # All rights reserved. # use English; use Getopt::Std; # # This script is invoked from ops and from the web interface. Must check # all the args. # sub usage() { print("Usage: node_attributes {-m|a} name=value [name=value ...] node [node ...]\n". " node_attributes -r attr [attr ... ] node\n". " node_attributes -r attr [attr ... ] nodelist: node [node ...]\n". " node_attributes -l node [node ...]\n". "\n". "Must specify one of: ". " -m modify attributes\n". " -a add attributes\n". " -r remove attributes\n". " -l list attributes\n". "\n". "For multi-node attribute removal, use the \"nodelist:\" syntax"); exit(-1); } my $optlist = "mardl"; # # Define a few constants # my $NODEATTRS_TABLE = "node_attributes"; my $NODEATTRS_KEY = "attrkey"; my $NODEATTRS_VAL = "attrvalue"; # # Configure variables # my $TB = "@prefix@"; my $TBOPS = "@TBOPSEMAIL@"; my $TBLOGS = "@TBLOGSEMAIL@"; my @nodes = (); my %attrs = (); my $debug = 0; my $errors = 0; my $modify_attrs = 0; my $add_attrs = 0; my $remove_attrs = 0; my $list_attrs = 0; # # Load the Testbed support stuff. # use lib "@prefix@/lib"; use libdb; use libtestbed; if (!TBAdmin($UID)) { print "Error: You must be an admin to use this command!\n"; exit(-1); } # un-taint path $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; # Turn off line buffering on output $| = 1; # # Parse command arguments. Once we return from getopts, all that should be # left are the required arguments. # my %options = (); my $operation = 0; if (! getopts($optlist, \%options)) { usage(); } if (defined($options{"d"})) { $debug = 1; } if (defined($options{"m"})) { $modify_attrs = 1; $operation++; } if (defined($options{"a"})) { $add_attrs = 1; $operation++; } if (defined($options{"r"})) { $remove_attrs = 1; $operation++; } if (defined($options{"l"})) { $list_attrs = 1; $operation++; } # # Sanity checks. # if ($operation > 1) { print "Error: Only one of -m, -a, -r, or -l may be specified!\n"; usage(); } elsif ($operation == 0) { print "Error: One of -m, -a, -r, or -l MUST be specified!\n"; usage(); } if (! @ARGV) { usage(); } # # Shift off the set strings (name=value). Verify that each one is in the # proper format. # while (@ARGV) { my $string = $ARGV[0]; # # Attributes are bare when in removal mode. # If the 'nodelist:' token is given, then the remainder # of the command line contains nodes. Otherwise, the last # argument must be the node to operate on. # if ($remove_attrs) { if ($string =~ /nodelist:/) { shift @ARGV; last; } elsif (@ARGV == 1) { last; } else { $attrs{$string} = "remove"; } } else { if (! ($string =~ /([-\w]*)=[\']?([^\']*)[\']?/)) { last; } $attrs{$1} = "$2"; } shift @ARGV; } if ($debug) { foreach my $option (keys(%attrs)) { print "Will set $option to '$attrs{$option}'\n"; } } # Be sure at least one node was specified. if (! @ARGV) { print "You must specify one or more nodes!\n"; usage(); } # Untaint the nodes. foreach my $node ( @ARGV ) { if ($node =~ /^([-\w]+)$/) { $node = $1; } else { die("Bad node name: $node."); } if (!TBValidNodeName($node)) { die("Node is not a valid node: $node\n"); } push(@nodes, $node); } if ($debug) { print "node list: @nodes\n"; } # If this is a attribute listing command, then let's list 'em! # XXX: this is done in a very lame way right now. if ($list_attrs) { my $nodelist = join("','", @nodes); my $query_result = DBQueryFatal("select * from node_attributes where ". "node_id in ('$nodelist') order by node_id"); print "node_id \t attribute \t value\n"; while (my $row = join("\t",$query_result->fetchrow_array())) { print "$row\n"; } exit(0); } # # Process the attributes to add, mod, or remove # foreach my $attr (keys(%attrs)) { my $value = $attrs{$attr}; # # Do a checkslot on the key and value to make sure they are # valid. # if ($attr ne "" && !TBcheck_dbslot($attr, $NODEATTRS_TABLE, $NODEATTRS_KEY, TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) { die("*** $0:\n". " Illegal attribute name: '$attr'\n"); } if ($value ne "" && !TBcheck_dbslot($value, $NODEATTRS_TABLE, $NODEATTRS_VAL, TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) { die("*** $0:\n". " Illegal value contents: '$value'\n"); } # # If this is a modify operation, then just build up a single clause to # execute later. Otherwise perform the insert/delete operation # for each node now. # if ($modify_attrs) { foreach my $node (@nodes) { DBQueryFatal("replace into $NODEATTRS_TABLE values ". "('$node','$attr','$value')"); } } # XXX: maybe just merge this with modify operation. elsif ($add_attrs) { foreach my $node (@nodes) { DBQueryFatal("insert into $NODEATTRS_TABLE values ". "('$node','$attr','$value')"); } } elsif ($remove_attrs) { foreach my $node (@nodes) { DBQueryFatal("delete from $NODEATTRS_TABLE where ". "node_id='$node' and $NODEATTRS_KEY='$attr'"); } } } exit(0);