os_select.in 6.29 KB
Newer Older
1 2 3
#!/usr/bin/perl -wT

#
4
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
24 25 26
#

# os_select sets the os that should boot next on a node, and sets
27
# next_op_mode accordingly.
28 29 30

sub usage() {
    print <<"EOF";
31
Usage: os_select [-h] [-d] [-c] [-1 | -t] [<osid>] <node> [<node> ...]
32 33
 -h    Display this help message
 -d    Debug mode
34 35 36
 -c    Clear the specified boot osid for nodes. Do not provide an osid.
 -1    Apply change to one-time boot field
 -t    Apply change to temporary boot field
37
 -b    Reset to default boot osid. Do not provide an osid.
38
 -l    Show the current settings.
39 40 41
 -w    Put the nodes in PXEWAIT mode (clears all osids)
 osid  OS identifier for the selected OS. Must be either numeric ID or
       a string in the form <pid>/<osname> (see web interface for listing)
42 43 44 45
 node  Node identifiers (ie pcXX)
EOF
    exit(-1);
}
46
my $optlist = "hdc1tblw";
47 48 49 50 51 52 53 54 55

# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

$| = 1; #Turn off line buffering on output

# Configure variables
my $TB          = "@prefix@";
56
my $TBOPS       = "@TBSTATEDEMAIL@";
57
my $TBLOG	= "@TBLOGFACIL@";
58 59 60 61 62

# Testbed Support libraries
use lib "@prefix@/lib";
use libdb;
use libtestbed;
63
use OSImage;
64
use Node;
65
use English;
66 67
use Getopt::Std;
use Sys::Syslog;
68 69

# Constants
70
my $MBKERNEL = TB_OSID_MBKERNEL;
71
my %osidmap = # Map some magic OSIDs to op_modes
72
    ( $MBKERNEL => "MINIMAL");
73

74 75 76 77 78 79 80 81 82 83 84
# Functions

sub set_nextmode($;$);
sub set_boot_osid($);
sub node_opmode($);
sub debug($;$);
sub notify($);
sub info($);
sub fatal($);
sub warning($);

85
# Global vars
86
my $debug	= 0; # debug/verbose
87 88 89
my $oneshot	= 0; # apply change to next_boot_osid.
my $tempmode	= 0; # apply change to temp_boot_osid.
my $clear       = 0; # Clear the selected boot (def,temp,next).
90
my $default     = 0; # Reset back to default osid.
91
my $list	= 0; # Just list the current settings.
92
my $waitmode	= 0; # Send the node into PXEWAIT
93
my @nodes       = ();
94
my $osid;
95
my $osimage;
96

97
# Set up syslog
98
openlog("osselect", "pid", $TBLOG);
99

100
#
101 102
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
103
#
104 105 106
%options = ();
if (! getopts($optlist, \%options)) { usage(); }
if (defined($options{"h"})) { usage(); }
Leigh Stoller's avatar
Leigh Stoller committed
107
if (defined($options{"d"})) { $debug=1; }
108
if (defined($options{"1"})) { $oneshot=1; }
109 110
if (defined($options{"t"})) { $tempmode=1; }
if (defined($options{"c"})) { $clear=1; }
111
if (defined($options{"b"})) { $default=1; }
112
if (defined($options{"l"})) { $list=1; }
113
if (defined($options{"w"})) { $waitmode=1; }
114

115 116 117 118 119 120 121
if ($waitmode + $list + $clear + $default > 1) {
    print STDERR "Should specify only one of -b, -c, -l, -w\n";
    usage();
}

# In certain modes, there is no OSID. Just a list of nodes.
if (! ($clear || $list || $default || $waitmode)) {
122 123 124 125 126
    usage()
	if (@ARGV < 2);
    $osid = shift();

    # Untaint args.
127
    if ($osid =~ /^([-\w\+\.\/]+(?::\d+)?)$/) {
128 129 130 131
	$osid = $1;
    }
    else {
	fatal("Bad data in osid: '$osid'");
132 133
    }
}
134 135 136
else {
    usage()
	if (@ARGV < 1);
137 138
}

139 140 141 142 143 144 145 146 147 148
# Untaint the nodes.
foreach my $node ( @ARGV ) {
    if ($node =~ /^([-\@\w]+)$/) {
	$node = $1;
    }
    else {
	fatal("Bad node name: $node");
    }
    push(@nodes, $node);
}
149

150
#
151 152
# Figure out who called us. Only root, people with admin status
# in the DB, or members of the right project can do this.
153
#
154 155 156 157 158 159 160 161 162
if ($UID && !TBAdmin($UID)) {
    if ($list && !TBNodeAccessCheck($UID, TB_NODEACCESS_READINFO, @nodes)) {
	fatal("os_select: You do not have permission to access ".
	      "one or more of the nodes.\n");
    }
    if (!$list && !TBNodeAccessCheck($UID, TB_NODEACCESS_MODIFYINFO, @nodes)) {
	fatal("os_select: You do not have permission to modify ".
	      "one or more of the nodes.\n");
    }
163 164 165 166 167
}

#
# Grab the info for the OSID. 
#
168
if (defined($osid)) {
169
    $osimage = OSImage->Lookup($osid);
170
    fatal("Unknown OSID: $osid")
171 172 173
	if (!defined($osimage));
    fatal("Cannot os_select an image alias")
	if ($osimage->isImageAlias());
174
}
175
elsif ($default) {
176
    $osimage = "<DEFAULT>";
177
}
178

179 180
foreach my $node (@nodes) {
    my $nodeobject = Node->Lookup($node);
181

182 183 184 185 186 187 188
    # In list mode, show all the IDs
    if ($list) {
	my $tb = $nodeobject->temp_boot_osid();
	my $nb = $nodeobject->next_boot_osid();
	my $db = $nodeobject->def_boot_osid();
	print "$node: ";
	if ($tb) {
189
	    $info = OSImage->Lookup($tb);
190 191 192 193
	    print "temp=$info";
	    print ", ";
	}
	if ($nb) {
194
	    $info = OSImage->Lookup($nb);
195 196 197 198
	    print "next=$info";
	    print ", ";
	}
	if ($db) {
199
	    $info = OSImage->Lookup($db);
200 201 202 203 204 205
	    print "default=$info";
	}
	print "\n";
	next;
    }

206
    if ($waitmode) {
207
	if ($nodeobject->OSSelect($osimage, undef, $debug) != 0) {
208 209 210 211 212
	    fatal("OSSelect(): clearing all fields failed on $node");
	}
	next;
    }

213
    # The field to change in the DB. 
214 215 216 217 218 219
    my $field  = "def_boot_osid";
    $field = "next_boot_osid"
	if ($oneshot);
    $field = "temp_boot_osid"
	if ($tempmode);

220 221 222
    if ($nodeobject->OSSelect($osimage, $field, $debug) != 0) {
	fatal("OSSelect(): " .
	      ($osimage ? "$osimage " : "") ."failed on $node");
223
    }
224
}
225
exit(0);
226

227 228 229 230 231 232 233 234 235 236
sub debug($;$)
{
    my $msg = shift;
    my $notice = shift || 0;
    my $prio="info";
    
    if ($notice) { $prio = "notice"; }
    
    syslog($prio, $msg);
    if ($debug) { print $msg; }
237 238
}

239 240 241 242 243 244 245 246
sub notify($)
{
    my $msg = shift;
    
    if (!$debug) {
	SENDMAIL($TBOPS, "os_select error", $msg);
    } 
    debug($msg, 1);
247 248
}

249 250 251 252 253
sub info($)
{
    my $msg = shift;
    
    debug($msg);
254 255
}

256 257 258 259 260 261
sub fatal($)
{
    my $msg = shift;
    
    notify("FATAL: $msg\n");
    exit(1);
262 263
}

264 265 266 267 268
sub warning($)
{
    my $msg = shift;
    
    info("WARNING: $msg\n");
269 270 271 272 273 274
}

# This is called when we exit with exit() or die()
END {
  closelog();
}