getimages.in 6.28 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2003-2016 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{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/>.
# 
# }}}
23 24 25 26 27 28 29 30 31 32 33 34
#
use strict;
use English;
use Getopt::Std;
use XML::Simple;
use Date::Parse;
use Time::Local;
use Data::Dumper;
use File::Temp qw(tempfile);

#
# Checkin at the master (Utah) to see if we should download and install
35 36
# any new images. This is very ProtoGeni specific and should probably
# not be used in other contexts unless you know what you are doing.
37 38 39
#
sub usage()
{
40
    print STDERR "Usage: getimages [-d] [-n]\n";
41 42 43
    print STDERR "  -h     This message\n";
    exit(-1);
}
44
my $optlist  = "hdt:";
45
my $debug    = 1;
46 47 48 49 50 51 52 53 54
my $testfile;

# Protos
sub fatal($);

#
# Configure variables
#
my $TB		 = "@prefix@";
55
my $METAURL      = "http://www.emulab.net/genirack-imageinfo-v2.xml";
56
my $SUDO         = "/usr/local/bin/sudo";
57
my $FETCH	 = "/usr/bin/fetch";
58 59
my $PROTOUSER    = "elabman";
my $WAP          = "$TB/sbin/withadminprivs";
60
my $metadata	 = "/tmp/imageinfo-$$.xml";
61 62 63 64 65 66
my $IMAGEIMPORT  = "$TB/sbin/image_import";

my $FBSD_VERSION = 4;
if (`uname -r` =~ /^(\d+\.\d*)/) {
    $FBSD_VERSION = $1;
}
67
# Extra fetch options on FreeBSD 9.3 or greater.
68
my $FETCHOPTIONS = "";
69
if ($FBSD_VERSION >= 9.3) {
70 71
    $FETCHOPTIONS = "--no-verify-peer --no-verify-hostname";
}
72 73 74 75 76 77

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
78
use User;
79
use OSImage;
80 81
use libaudit;
use EmulabConstants;
82
use libEmulab;
83 84 85 86 87 88 89 90 91 92 93

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

#
# Untaint the path
# 
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:";

94 95 96 97 98 99 100
#
# When the testbed is shutdown, skip. 
#
if (libEmulab::IsShutDown()) {
    print "Testbed is shutdown; exiting ...\n";
    exit(0);
}
101

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{'h'})) {
    usage();
}
if (defined($options{'d'})) {
    $debug = 1;
}
if (defined($options{'t'})) {
    $testfile = $options{'t'};
}
usage()
    if (@ARGV);

# Only root.
123
if ($UID) {
124 125 126 127
    die("*** $0:\n".
	"    Must run this as root!\n");
}

128
# Record output in case of error.
129
LogStart(0, undef, LIBAUDIT_LOGONLY() | LIBAUDIT_LOGTBLOGS());
130

131 132 133 134 135
#
# We use elabman user below for marking the image update.
#
my $elabman = User->Lookup(PROTOUSER());

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#
# Fetch the metadata, which tells what to do.
#
if (!defined($testfile)) {
    print "Fetching metadata from the server\n"
	if ($debug);
    system("$FETCH -o $metadata $METAURL") == 0
	or fatal("Could not fetch $METAURL");
}
else {
    $metadata = $testfile;
}

#
# Must wrap the parser in eval since it exits on error.
#
my $xmlparse = eval { XMLin($metadata,
			    VarAttr => 'name',
			    ForceArray => ['image'],
			    ContentKey => '-content',
			    SuppressEmpty => undef); };
fatal($@)
    if ($@);

#
#
#
foreach my $imageid (keys(%{ $xmlparse->{'image'} })) {
    my $attributes = $xmlparse->{'image'}->{$imageid}->{'attribute'};

    if ($debug) {
	print STDERR Data::Dumper->Dump([$attributes], [$imageid]);
    }

    my $metaurl  = $attributes->{'metaurl'};
171
    my $newtime  = timegm(strptime($attributes->{'modtime'}));
172 173

    #
174
    # If we have an entry in the DB, we use the modtime as a serial
175
    # number to determine if we need to go the next step.
176
    #
177 178
    # XXX What if the local site has its own more recent version?
    #
179 180
    # Lookup will sanity check the imageid string.
    #
181
    my $image = OSImage->Lookup(TBOPSPID(), $imageid);
182
    if (defined($image)) {
183 184
	print "Local descriptor found: $image\n"
	    if ($debug);
185

186 187 188 189 190 191 192 193
	if (defined($image->updated())) {
	    my $updated = timelocal(strptime($image->updated()));

	    if ($updated == $newtime) {
		print "Image has not changed, skipping ...\n"
		    if ($debug);
		next;
	    }
194
	}
195
	print "$imageid timestamp has changed.\n";
196 197 198
    }
    else {
	print "$imageid does not exist.\n";
199 200
    }

201
    #
202 203
    # Grab the metadata so we can get the imageurl out of it, we need
    # to fix up local images below.
204
    #
205 206 207 208 209
    my ($fh2, $metafilename) = tempfile(UNLINK => !$debug);
    fatal("Could not create temporary file")
	if (!defined($fh2));
    close($fh2);

210
    print "Fetching $metaurl\n";
211
    system("$FETCH $FETCHOPTIONS -o $metafilename $metaurl") == 0
212
	or fatal("Could not fetch $metaurl");
213 214 215 216 217 218 219 220
    
    my $metaparse = eval { XMLin($metafilename,
				 VarAttr => 'name',
				 ContentKey => '-content',
				 SuppressEmpty => undef); };
    fatal($@)
	if ($@);
    my $imageurl = $metaparse->{'attribute'}->{"imagefile_url"}->{'value'};
221 222

    #
223
    # We use image_import to bring in or update the image. 
224
    #
225 226 227 228 229 230 231 232 233 234
    # XXX Running this script means you want Utah to manage
    # your images, so we ignore locally updated images at the
    # moment and force the existing image metadata url to what
    # Utah has said it should be.
    #
    my $cmd = "$SUDO -u $PROTOUSER $WAP $IMAGEIMPORT -d -g -u $PROTOUSER ";
    if (defined($image)) {
	$cmd .= " -r " . $image->imageid();
	$image->Update({"metadata_url"  => $metaurl,
			"imagefile_url" => $imageurl});
235
    }
236 237 238 239 240 241 242
    else {
	$cmd .= " -p " . TBOPSPID() . " '$metaurl'";
    }
    print "Running '$cmd'\n" if ($debug);
    system($cmd);
    fatal("Could not import image from $metaurl")
	if ($?);
243

244
    $image = OSImage->Lookup(TBOPSPID(), $imageid);
245 246
    if (!defined($image)) {
	fatal("Could not lookup image even though import succeeded");
247
    }
248
    
Leigh Stoller's avatar
Leigh Stoller committed
249
    #
250
    # Mark this too, so that we do not repeat the first test above.
Leigh Stoller's avatar
Leigh Stoller committed
251
    #
252
    $image->MarkUpdate($elabman, $newtime);
253 254
}

255 256
# No email if no errors.
LogAbort();
257
unlink($metadata);
258 259 260 261 262 263 264 265 266 267 268 269 270
exit(0);

sub fatal($)
{
    my ($mesg) = $_[0];

    unlink($metadata)
	if (-e $metadata);
    die("*** $0:\n".
	"    $mesg\n");
}