Commit d4bcb4aa authored by Leigh B. Stoller's avatar Leigh B. Stoller

Support for a "Commit" button, to allow the user to force a commit

of the archive.
parent b958376f
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated automatically using autoconf version 2.13 # Generated automatically using autoconf version 2.13
# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # Copyright (C) 1992, 93, 94, 95, 96, 05 Free Software Foundation, Inc.
# #
# This configure script is free software; the Free Software Foundation # This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it. # gives unlimited permission to copy, distribute and modify it.
...@@ -2282,6 +2282,8 @@ outfiles="$outfiles Makeconf GNUmakefile \ ...@@ -2282,6 +2282,8 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/idleswap tbsetup/webidleswap tbsetup/switchmac \ tbsetup/idleswap tbsetup/webidleswap tbsetup/switchmac \
tbsetup/newnode_reboot tbsetup/webnodeattributes \ tbsetup/newnode_reboot tbsetup/webnodeattributes \
tbsetup/libtestbed.py \ tbsetup/libtestbed.py \
tbsetup/libArchive.pm tbsetup/archive_control \
tbsetup/webarchive_control \
tbsetup/tarfiles_setup tbsetup/webtarfiles_setup \ tbsetup/tarfiles_setup tbsetup/webtarfiles_setup \
tbsetup/fetchtar.proxy tbsetup/webfrisbeekiller \ tbsetup/fetchtar.proxy tbsetup/webfrisbeekiller \
tbsetup/plab/GNUmakefile tbsetup/plab/libplab.py \ tbsetup/plab/GNUmakefile tbsetup/plab/libplab.py \
......
...@@ -720,6 +720,8 @@ outfiles="$outfiles Makeconf GNUmakefile \ ...@@ -720,6 +720,8 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/idleswap tbsetup/webidleswap tbsetup/switchmac \ tbsetup/idleswap tbsetup/webidleswap tbsetup/switchmac \
tbsetup/newnode_reboot tbsetup/webnodeattributes \ tbsetup/newnode_reboot tbsetup/webnodeattributes \
tbsetup/libtestbed.py \ tbsetup/libtestbed.py \
tbsetup/libArchive.pm tbsetup/archive_control \
tbsetup/webarchive_control \
tbsetup/tarfiles_setup tbsetup/webtarfiles_setup \ tbsetup/tarfiles_setup tbsetup/webtarfiles_setup \
tbsetup/fetchtar.proxy tbsetup/webfrisbeekiller \ tbsetup/fetchtar.proxy tbsetup/webfrisbeekiller \
tbsetup/plab/GNUmakefile tbsetup/plab/libplab.py \ tbsetup/plab/GNUmakefile tbsetup/plab/libplab.py \
......
...@@ -20,7 +20,7 @@ BIN_STUFF = power snmpit tbend tbprerun tbreport \ ...@@ -20,7 +20,7 @@ BIN_STUFF = power snmpit tbend tbprerun tbreport \
node_reboot nscheck node_update savelogs node_control \ node_reboot nscheck node_update savelogs node_control \
portstats checkports eventsys_control os_select tbrestart \ portstats checkports eventsys_control os_select tbrestart \
tbswap nseswap tarfiles_setup node_history tbrsync \ tbswap nseswap tarfiles_setup node_history tbrsync \
node_attributes node_attributes archive_control
SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup \ SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup \
batch_daemon exports_setup reload_daemon sched_reserve \ batch_daemon exports_setup reload_daemon sched_reserve \
...@@ -48,7 +48,7 @@ LIBEXEC_STUFF = rmproj wanlinksolve wanlinkinfo \ ...@@ -48,7 +48,7 @@ LIBEXEC_STUFF = rmproj wanlinksolve wanlinkinfo \
spewlogfile staticroutes routecalc wanassign \ spewlogfile staticroutes routecalc wanassign \
webnodereboot webrmuser webidleswap switchmac \ webnodereboot webrmuser webidleswap switchmac \
spewrpmtar webtarfiles_setup webfrisbeekiller gentopofile \ spewrpmtar webtarfiles_setup webfrisbeekiller gentopofile \
webnodeattributes webnodeattributes webarchive_control
LIB_STUFF = libtbsetup.pm exitonwarn.pm libtestbed.pm snmpit_intel.pm \ LIB_STUFF = libtbsetup.pm exitonwarn.pm libtestbed.pm snmpit_intel.pm \
snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm power_rpc27.pm \ snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm power_rpc27.pm \
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
#
# Command line interface to experiment archive module.
#
sub usage()
{
print STDOUT
"Usage: archive_control [-f] <commit> <pid> <eid>\n";
exit(-1);
}
my $optlist = "df";
my $debug = 0;
my $force = 0;
my $dbuid;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $NFSTRACESUPPORT = @NFSTRACESUPPORT@;
my $NFSTRACE = "$TB/sbin/nfstrace";
# Protos
sub fatal($);
#
# Turn off line buffering on output
#
$| = 1;
# un-taint path
$ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:$TB/bin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use libaudit;
use libArchive;
#
# Verify user and get his DB uid.
#
if (! UNIX2DBUID($UID, \$dbuid)) {
die("*** $0:\n".
" You do not exist in the Emulab Database!\n");
}
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (@ARGV != 3) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"f"})) {
$force = 1;
}
my $action = $ARGV[0];
my $pid = $ARGV[1];
my $eid = $ARGV[2];
#
# Untaint args.
#
if ($action =~ /^(commit)$/) {
$action = $1;
}
else {
usage();
}
if ($pid =~ /^([-\w]+)$/) {
$pid = $1;
}
else {
die("Bad data in pid: $pid.");
}
if ($eid =~ /^([-\w]+)$/) {
$eid = $1;
}
else {
die("Bad data in eid: $eid.");
}
if (! ($expstate = ExpState($pid, $eid))) {
fatal("No such experiment $pid/$eid!");
}
#
# Check permission. Only people with permission to destroy the experiment
# can do this.
#
if (! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_UPDATE)) {
fatal("You do not have permission to control the archive for $pid/$eid!");
}
#
# Do not allow an archive system to be controlled if the experiment is not
# active or swapped. Will probably be changed later.
#
if ($expstate ne EXPTSTATE_ACTIVE &&
$expstate ne EXPTSTATE_SWAPPED) {
fatal("Experiment $pid/$eid must be active or swapped!");
}
#
# This script is always audited. Mail is sent automatically upon exit.
#
if (AuditStart(0)) {
#
# Parent exits normally
#
exit(0);
}
# Temporary
libArchive::setdebug(1);
#
# Allow the user to force a commit of the archive.
#
if ($action eq "commit") {
if ($NFSTRACESUPPORT) {
#
# This program asks the nodes for the trace results.
#
print "Getting files accessed via NFS.\n";
system("$NFSTRACE transfer $pid $eid");
#
# Add the files that have been detected by tracing to the archive.
#
if (libArchive::TBExperimentArchiveAddTracedFiles($pid, $eid) < 0) {
fatal("Failed to add traced files to the experiment archive!");
}
}
#
# Add the special per-experiment archive directory.
#
print "Getting user added files.\n";
if (libArchive::TBExperimentArchiveAddUserFiles($pid, $eid) < 0) {
fatal("Failed to add user specified files to the experiment archive!");
}
#
# Do a SavePoint on the experiment files.
#
print "Doing a savepoint on the experiment archive ...\n";
if (libArchive::TBExperimentArchiveSavePoint($pid, $eid) < 0) {
fatal("Failed to do a savepoint on the experiment archive!");
}
# Commit the archive after swapout
print "Doing a commit on the experiment archive ...\n";
if (libArchive::TBCommitExperimentArchive($pid, $eid, "user_commit") < 0) {
fatal("Failed to commit experiment archive!");
}
}
exit(0);
sub fatal($)
{
my ($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}
...@@ -46,7 +46,7 @@ my $SVNADMIN = "/usr/local/bin/svnadmin"; ...@@ -46,7 +46,7 @@ my $SVNADMIN = "/usr/local/bin/svnadmin";
my $IMPORTER = "$TB/sbin/svn_load_dirs.pl"; my $IMPORTER = "$TB/sbin/svn_load_dirs.pl";
my $inittag = 'root'; my $inittag = 'root';
my $defaultview = 'head'; my $defaultview = 'head';
my $debug = 1; my $debug = 0;
my $svnopt = ($debug ? "" : "-q"); my $svnopt = ($debug ? "" : "-q");
# Little helper and debug function. # Little helper and debug function.
...@@ -59,6 +59,21 @@ sub mysystem($) ...@@ -59,6 +59,21 @@ sub mysystem($)
system($command); system($command);
} }
# Another little helper for scripts that include this library.
sub setdebug($)
{
my ($toggle) = @_;
if ($toggle) {
$debug = 1;
$svnopt = "";
}
else {
$debug = 0;
$svnopt = "-q";
}
}
# #
# Create a new archive. Returns -1 if any error. Otherwise return # Create a new archive. Returns -1 if any error. Otherwise return
# the new record index. # the new record index.
...@@ -153,11 +168,12 @@ sub ArchiveCreate(;$$) ...@@ -153,11 +168,12 @@ sub ArchiveCreate(;$$)
mysystem("cd $dir; mkdir ignore; cd ignore; mkdir $view; ". mysystem("cd $dir; mkdir ignore; cd ignore; mkdir $view; ".
" mkdir $view/trunk $view/savepoint $view/tags; ". " mkdir $view/trunk $view/savepoint $view/tags; ".
" mkdir $view/history; ". " mkdir $view/history; ".
"$SVN import -m 'ArchiveCreate' $view file://$repodir/$view") "$SVN import $svnopt -m 'ArchiveCreate' "/
" $view file://$repodir/$view")
== 0 or goto bad; == 0 or goto bad;
# Create a branch tag in the tags directory to base differences against. # Create a branch tag in the tags directory to base differences against.
mysystem("$SVN copy -m 'ArchiveCreate Branch' ". mysystem("$SVN copy $svnopt -m 'ArchiveCreate Branch' ".
" file://$repodir/$view/trunk ". " file://$repodir/$view/trunk ".
" file://$repodir/$view/tags/${tag}-branch") " file://$repodir/$view/tags/${tag}-branch")
== 0 or goto bad; == 0 or goto bad;
...@@ -271,7 +287,7 @@ sub ArchiveAdd($$;$$) ...@@ -271,7 +287,7 @@ sub ArchiveAdd($$;$$)
mysystem("$TAR cf - -C / $pathname | tar xf - -C $checkin"); mysystem("$TAR cf - -C / $pathname | tar xf - -C $checkin");
} }
else { else {
mysystem("$RSYNC -R -avx --delete /${pathname} $checkin"); mysystem("$RSYNC -R -ax --delete /${pathname} $checkin");
} }
if ($?) { if ($?) {
print STDERR "ArchiveFile: Could not copy in /$pathname\n"; print STDERR "ArchiveFile: Could not copy in /$pathname\n";
...@@ -358,13 +374,13 @@ sub ArchiveSavePoint($;$$$) ...@@ -358,13 +374,13 @@ sub ArchiveSavePoint($;$$$)
goto bad; goto bad;
} }
mysystem("$IMPORTER -no_user_input file://$repodir ". mysystem("$IMPORTER -no_user_input file://$repodir ".
" $view/savepoint . ") " $view/savepoint . > /dev/null")
== 0 or goto bad; == 0 or goto bad;
# #
# Create the tag for this savepoint. # Create the tag for this savepoint.
# #
mysystem("$SVN copy -m 'ArchiveCreate Branch' ". mysystem("$SVN copy $svnopt -m 'ArchiveCreate Branch' ".
" file://$repodir/$view/savepoint ". " file://$repodir/$view/savepoint ".
" file://$repodir/$view/tags/${savetag}") " file://$repodir/$view/tags/${savetag}")
== 0 or goto bad; == 0 or goto bad;
...@@ -531,7 +547,7 @@ sub ArchiveCommit($;$$$) ...@@ -531,7 +547,7 @@ sub ArchiveCommit($;$$$)
} }
# Create a tag in the tags directory for the commit. # Create a tag in the tags directory for the commit.
mysystem("$SVN copy -m 'ArchiveCommit' ". mysystem("$SVN copy $svnopt -m 'ArchiveCommit' ".
" file://$repodir/$view/trunk ". " file://$repodir/$view/trunk ".
" file://$repodir/$view/tags/${newtag}") " file://$repodir/$view/tags/${newtag}")
== 0 or goto bad; == 0 or goto bad;
...@@ -539,13 +555,13 @@ sub ArchiveCommit($;$$$) ...@@ -539,13 +555,13 @@ sub ArchiveCommit($;$$$)
# Create a tag in the history directory for the commit. The # Create a tag in the history directory for the commit. The
# history directory has just the commit tags, so its easy to go # history directory has just the commit tags, so its easy to go
# back in time. # back in time.
mysystem("$SVN copy -m 'ArchiveCommit' ". mysystem("$SVN copy $svnopt -m 'ArchiveCommit' ".
" file://$repodir/$view/trunk ". " file://$repodir/$view/trunk ".
" file://$repodir/$view/history/${newtag}") " file://$repodir/$view/history/${newtag}")
== 0 or goto bad; == 0 or goto bad;
# Create a branch tag in the tags directory to base differences against. # Create a branch tag in the tags directory to base differences against.
mysystem("$SVN copy -m 'ArchiveCommit Branch' ". mysystem("$SVN copy $svnopt -m 'ArchiveCommit Branch' ".
" file://$repodir/$view/trunk ". " file://$repodir/$view/trunk ".
" file://$repodir/$view/tags/${newtag}-branch") " file://$repodir/$view/tags/${newtag}-branch")
== 0 or goto bad; == 0 or goto bad;
...@@ -689,7 +705,7 @@ sub ArchiveFork($$;$$$) ...@@ -689,7 +705,7 @@ sub ArchiveFork($$;$$$)
} }
# Create newview directory in the repo. # Create newview directory in the repo.
mysystem("$SVN mkdir -m 'ArchiveFork' ". mysystem("$SVN mkdir $svnopt -m 'ArchiveFork' ".
" file://$repodir/$newview") " file://$repodir/$newview")
== 0 or goto bad; == 0 or goto bad;
...@@ -708,7 +724,7 @@ sub ArchiveFork($$;$$$) ...@@ -708,7 +724,7 @@ sub ArchiveFork($$;$$$)
== 0 or goto bad; == 0 or goto bad;
# Do not want to copy the tags/savepoints directories. Add new ones. # Do not want to copy the tags/savepoints directories. Add new ones.
mysystem("$SVN mkdir -m 'ArchiveFork' ". mysystem("$SVN mkdir $svnopt -m 'ArchiveFork' ".
" file://$repodir/$newview/savepoint ". " file://$repodir/$newview/savepoint ".
" file://$repodir/$newview/tags") " file://$repodir/$newview/tags")
== 0 or goto bad; == 0 or goto bad;
...@@ -839,12 +855,28 @@ sub ArchiveArchive($$) ...@@ -839,12 +855,28 @@ sub ArchiveArchive($$)
# #
my $directory; my $directory;
if (GetArchiveDirectory($archive_idx, \$directory) < 0) { if (GetArchiveDirectory($archive_idx, \$directory) < 0) {
print STDERR "ArchiveCommit: ". print STDERR "ArchiveArchive: ".
"Archive '$archive_idx' does not exist in the DB!\n"; "Archive '$archive_idx' does not exist in the DB!\n";
return -1; return -1;
} }
#
# Need additional check to make sure that it has not already been
# archived.
#
my ($archived, $date_archived);
if (IsArchiveArchived($archive_idx, \$archived, \$date_archived) < 0) {
return -1;
}
if ($archived) {
print STDERR "ArchiveArchive: ".
"Archive '$archive_idx' already archived on $date_archived!\n";
return 0;
}
if (! -d $directory || ! -w $directory) { if (! -d $directory || ! -w $directory) {
print STDERR "ArchiveFile: $directory cannot be written!\n"; print STDERR "ArchiveArchive: $directory cannot be written!\n";
return -1; return -1;
} }
...@@ -872,7 +904,10 @@ sub ArchiveArchive($$) ...@@ -872,7 +904,10 @@ sub ArchiveArchive($$)
# #
# Update its location in the DB, and remove the old directory. # Update its location in the DB, and remove the old directory.
# #
DBQueryWarn("update archives set directory='$target' ". DBQueryWarn("update archives set ".
" directory='$target', ".
" archived=1, ".
" date_archived=UNIX_TIMESTAMP(now()) ".
"where idx='$archive_idx'") "where idx='$archive_idx'")
or return -1; or return -1;
...@@ -889,7 +924,7 @@ sub ArchiveArchive($$) ...@@ -889,7 +924,7 @@ sub ArchiveArchive($$)
# Destroy an archive. The DB state is retained unless optional flag says # Destroy an archive. The DB state is retained unless optional flag says
# to clean it. # to clean it.
# #
sub ArchiveDestroy($;$) sub ArchiveDestroy($$)
{ {
my ($archive_idx, $clean) = @_; my ($archive_idx, $clean) = @_;
...@@ -902,6 +937,22 @@ sub ArchiveDestroy($;$) ...@@ -902,6 +937,22 @@ sub ArchiveDestroy($;$)
"Archive '$archive_idx' does not exist in the DB!\n"; "Archive '$archive_idx' does not exist in the DB!\n";
return -1; return -1;
} }
#
# Need additional check to make sure that it has not already been
# archived. Do not want to do anything, unless clean is specified.
#
my ($archived, $date_archived);
if (IsArchiveArchived($archive_idx, \$archived, \$date_archived) < 0) {
return -1;
}
if ($archived && !$clean) {
print STDERR "ArchiveDestroy: ".
"Archive '$archive_idx' archived on $date_archived!\n";
return 0;
}
if (! -d $directory || ! -w $directory) { if (! -d $directory || ! -w $directory) {
return 0; return 0;
} }
...@@ -912,7 +963,7 @@ sub ArchiveDestroy($;$) ...@@ -912,7 +963,7 @@ sub ArchiveDestroy($;$)
"Could not remove contents of $directory!\n"; "Could not remove contents of $directory!\n";
return -1; return -1;
} }
if (defined($clean) && $clean) { if ($clean) {
(DBQueryWarn("delete from archive_tags ". (DBQueryWarn("delete from archive_tags ".
"where archive_idx='$archive_idx'") && "where archive_idx='$archive_idx'") &&
DBQueryWarn("delete from archive_views ". DBQueryWarn("delete from archive_views ".
...@@ -943,6 +994,30 @@ sub GetArchiveDirectory($$) ...@@ -943,6 +994,30 @@ sub GetArchiveDirectory($$)
return 0; return 0;
} }
#
# See if the archive has already been archived away, and when.
#
sub IsArchiveArchived($$$)
{
my ($idx, $parch, $pdate) = @_;
my $query_result =
DBQueryWarn("select archived,FROM_UNIXTIME(date_archived) ".
" from archives where idx='$idx'");
return -1
if (!$query_result || !$query_result->numrows);
my ($archived,$date_archived) = $query_result->fetchrow_array();
$$parch = $archived
if (defined($parch));
$$pdate = $date_archived
if (defined($pdate));
return 0;
}
# #
# Get the current tag for an archive, given its index. Returns -1 on error, # Get the current tag for an archive, given its index. Returns -1 on error,
# zero otherwise. Place tag in the return pointer. # zero otherwise. Place tag in the return pointer.
...@@ -1142,11 +1217,14 @@ sub TBExperimentArchiveAddUserFiles($$) ...@@ -1142,11 +1217,14 @@ sub TBExperimentArchiveAddUserFiles($$)
# #
# SavePoint an experiment archive. # SavePoint an experiment archive.
# #
sub TBExperimentArchiveSavePoint($$$) sub TBExperimentArchiveSavePoint($$;$)
{ {
my ($pid, $eid, $tagext) = @_; my ($pid, $eid, $tagext) = @_;
my ($archive_idx, $view); my ($archive_idx, $view);
$tagext = "savepoint"
if (!defined($tagext));
return 0 return 0
if (!$MAINSITE || $pid ne $ALLOWEDPID); if (!$MAINSITE || $pid ne $ALLOWEDPID);
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2006 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
#
# This gets invoked from the Web interface. Simply a wrapper ...
#
#
# Configure variables
#
my $TB = "@prefix@";
#
# Run the real thing, and never return.
#
exec "$TB/bin/archive_control", @ARGV;
die("webarchive_control: Could not exec archive_control: $!");
...@@ -57,6 +57,7 @@ cfg.general.cvs_roots = {} ...@@ -57,6 +57,7 @@ cfg.general.cvs_roots = {}
cfg.options.docroot = "/cvsweb/viewvc" cfg.options.docroot = "/cvsweb/viewvc"
cfg.options.http_expiration_time = 10 cfg.options.http_expiration_time = 10
cfg.options.generate_etags = 1 cfg.options.generate_etags = 1
cfg.options.use_localtime = 1
cfg.general.address = "<a href='mailto:@TBOPSEMAIL_NOSLASH@'>@TBOPSEMAIL_NOSLASH@</a>" cfg.general.address = "<a href='mailto:@TBOPSEMAIL_NOSLASH@'>@TBOPSEMAIL_NOSLASH@</a>"
viewcvs.main(server, cfg) viewcvs.main(server, cfg)
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2006 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
include("showstuff.php3");
#
# Only known and logged in users.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
#
# Verify page arguments.
#
if (!isset($pid) ||
strcmp($pid, "") == 0) {
USERERROR("You must provide a Project ID.", 1);
}
if (!isset($eid) ||
strcmp($eid, "") == 0) {
USERERROR("You must provide an Experiment ID.", 1);
}
if (!TBvalid_pid($pid)) {
PAGEARGERROR("Invalid project ID.");
}