getimages.in 6.28 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2003-2015 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;
}
Leigh B Stoller's avatar
Leigh B Stoller committed
67
# Extra fetch options on FreeBSD 9.3 or greater.
68
my $FETCHOPTIONS = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
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 80 81 82
use Image;
use OSinfo;
use libaudit;
use EmulabConstants;
83
use libEmulab;
84 85 86 87 88 89 90 91 92 93 94

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

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

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

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
#
# 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.
124
if ($UID) {
125 126 127 128
    die("*** $0:\n".
	"    Must run this as root!\n");
}

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

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

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 171
#
# 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'};
172
    my $newtime  = timegm(strptime($attributes->{'modtime'}));
173 174

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

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

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

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

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

    #
224
    # We use image_import to bring in or update the image. 
225
    #
226 227 228 229 230 231 232 233 234 235
    # 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});
236
    }
237 238 239 240 241 242 243
    else {
	$cmd .= " -p " . TBOPSPID() . " '$metaurl'";
    }
    print "Running '$cmd'\n" if ($debug);
    system($cmd);
    fatal("Could not import image from $metaurl")
	if ($?);
244

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

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

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

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