Commit 7779ffa7 authored by Leigh B Stoller's avatar Leigh B Stoller

Merge branch 'master' of git-public.flux.utah.edu:/flux/git/emulab-devel

parents 46e16fab 94d9294f
......@@ -41,7 +41,7 @@ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Turn off line buffering on output
$| = 1;
use lib "/usr/testbed/lib";
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use NodeType;
......
......@@ -173,6 +173,10 @@ install: $(INSTALL_SBINDIR)/tmcd \
control-install: tmcc
@$(MAKE) -C freebsd control-install
subboss-install: client
@$(MAKE) -C $(MDSUBDIR) install
@$(MAKE) -C common subboss-install
client-install: client
@$(MAKE) -C $(MDSUBDIR) install
......
......@@ -42,6 +42,7 @@ install client-install:
local-install: path-install local-script-install symlinks
remote-install: path-install remote-script-install
control-install: path-install control-script-install
subboss-install: subboss-script-install
other-install:
(cd ../../os; $(MAKE) DESTDIR=$(DESTDIR) client-install)
......@@ -84,6 +85,9 @@ common-script-install: dir-install
$(INSTALL) -m 755 $(SRCDIR)/startcmddone $(BINDIR)/startcmddone
(cd config; $(MAKE) DESTDIR=$(DESTDIR) script-install)
subboss-script-install: dir-install
(cd config; $(MAKE) DESTDIR=$(DESTDIR) subboss-script-install)
symlinks: dir-install
rm -f $(TBBINDIR)/tevc$(EXE)
ln -s $(CLIENT_BINDIR)/tevc$(EXE) $(TBBINDIR)/tevc$(EXE)
......
......@@ -45,3 +45,6 @@ install:
script-install:
$(INSTALL_PROGRAM) $(SRCDIR)/librc.pm $(BINDIR)/librc.pm
$(INSTALL_PROGRAM) $(SCRIPTS) $(BINDIR)/rc
subboss-script-install: script-install
$(INSTALL_PROGRAM) $(SRCDIR)/rc.mksubboss $(BINDIR)/rc
#! /usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004-2010 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use strict;
sub mysystem($;$);
sub RecreateDir($$);
sub SetupFatal($);
# Turn off line buffering on output
$| = 1;
# Drag in path stuff so we can find emulab stuff.
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }
# Only root.
if ($EUID != 0) {
die("*** $0:\n".
" Must be root to run this script!\n");
}
use libsetup;
use libtmcc;
use librc;
my $TBDIR = "/usr/testbed";
my ($bossname, $outer_bossip) = tmccbossinfo();
sub doboot()
{
my $EXTRAFS = "/z";
my $TFTPBOOT_DIR = "$EXTRAFS/tftpboot";
my $stuffdir = "$EXTRAFS/tmp";
my $MFSTARBALL = "tftpboot-subboss.tar.gz";
goto skipsetup if (!SUBBOSS());
print "Stopping tftpd (if running)";
# This needs to be system(), not mysystem since we don't
# care if it fails.
system("/usr/local/etc/rc.d/tftpd-hpa.sh stop");
RecreateDir($EXTRAFS,1);
mysystem("$BINDIR/mkextrafs.pl -f $EXTRAFS");
mysystem("mkdir -p $TFTPBOOT_DIR $stuffdir");
mysystem("ln -sf $TFTPBOOT_DIR /tftpboot");
print "Copying over tftpboot tar file from web server and unpacking\n";
#mysystem("wget -q -O $stuffdir/tftpboot.tar.gz ".
# "http://$bossname/downloads/$MFSTARBALL");
mysystem("fetch -q -o $stuffdir/tftpboot.tar.gz ".
"http://$bossname/downloads/$MFSTARBALL");
mysystem("tar xzf $stuffdir/tftpboot.tar.gz -C /tftpboot");
# FIXME clean up stuffdir?
print "Restarting tftpd";
mysystem("/usr/local/etc/rc.d/tftpd-hpa.sh start");
skipsetup:
mysystem("rm -f $BINDIR/rc/rc.mksubboss");
}
#
# Run a command string.
#
sub mysystem($;$)
{
my ($command, $retrycount) = @_;
$retrycount = 1
if (!defined($retrycount));
while ($retrycount--) {
print "Command: '$command'\n";
print "Started at: " . libsetup::TBTimeStamp() . "\n";
system($command);
last
if ($? == 0 || $retrycount == 0);
sleep(1);
}
if ($?) {
SetupFatal("Command failed: $? - $command");
}
print "Finished at: " . libsetup::TBTimeStamp() . "\n";
}
#
# Very paranoid routine to "remove" and optionally recreate a directory.
#
# If the directory exists and is a mount point, we umount it and
# fixup /etc/fstab so it doesn't get remounted.
#
# If we could not unmount it or it isn't a mount point, we just move
# the directory out of the way.
#
# If it exists but is not a directory, we move it out of the way.
#
sub RecreateDir($$)
{
my ($dir,$docreate) = @_;
#
# If path is a directory and already exists, we need to get rid of it.
# If it is a mount point, unmount it. Otherwise, rename it.
#
if (-d "$dir") {
if (system("umount $dir") == 0) {
# was a mounted FS, need to remove it from fstab if present
mysystem("sed -i '.orig' -E '\\;\[\[:space:\]\]$TBDIR\[\[:space:\]\];d' /etc/fstab");
}
# remove it if it is empty
rmdir("$dir");
}
#
# At this point, if the target still exists (directory or not)
# we have to move it out of the way. If that fails, we die.
#
if (-e "$dir") {
mysystem("mv $dir $dir.old.$$");
}
#
# Finally, make the directory
#
if ($docreate) {
mysystem("mkdir -p $dir");
}
}
#
# Print error and exit.
#
sub SetupFatal($)
{
my ($msg) = @_;
die("*** $0:\n".
" $msg\n");
}
doboot();
......@@ -19,12 +19,10 @@
#
# TODO:
# Users can add notifications for themselves
# Support branch deletion
# Support commits that remove revisions (ie. are not fast-forwards)
# Support tag creation/deletion/etc.
#
use strict;
use IPC::Open2;
use POSIX 'setsid';
use Getopt::Long;
sub get_config($$);
......@@ -178,7 +176,8 @@ my $EMPTYREV = "0"x40;
my $CT_CREATE = "create";
my $CT_UPDATE = "update";
my $CT_DELETE = "delete";
my $CT_FORCED_UPDATE = "force-update";
my $CT_REWIND = "rewind";
my $CT_REBASE = "rebase";
#
# Tired of typing this and getting it wrong
......@@ -188,11 +187,11 @@ my $STDERRNULL = " 2> /dev/null";
######################################################################
# Function Prototypes
######################################################################
sub change_type($$);
sub change_type($$$);
sub ref_type($);
sub rev_type($);
sub revparse($);
sub changed_files($$);
sub changed_files_single_revision($);
sub changed_files(@);
sub get_mail_addresses($@);
sub get_merge_base($$);
sub uniq(@);
......@@ -202,7 +201,6 @@ sub get_commits($$$);
sub send_mail($$@);
sub short_refname($);
sub debug(@);
sub rev_string($$);
sub object_exists($$);
sub filter_out_objects_in_repo($@);
......@@ -252,10 +250,17 @@ if ($mailconfigfile) {
#
my @reflines;
if ($testmode) {
my $fullref = `git rev-parse --symbolic-full-name $testbranch`;
if (!$fullref) {
exit(1);
}
my $newrev = `git rev-parse $fullref $STDERRNULL`;
chomp $newrev;
#
# Provide a simple way to grab some commits - the three most recent ones
#
@reflines = ("$testbranch~2 $testbranch $testbranch");
@reflines = ("$newrev~2 $newrev $fullref");
} else {
#
# Get all of the references that are being pushed from stdin - we do this in
......@@ -284,6 +289,9 @@ if ($detach && !$debug) {
# Loop over all of the references we got on stdin
#
foreach my $refline (@reflines) {
my @commits;
my @changed_files;
chomp $refline;
debug("Read line '$refline'");
......@@ -293,6 +301,7 @@ foreach my $refline (@reflines) {
# happened in the middle
#
my ($oldrev, $newrev, $refname) = split(/\s+/, $refline);
my $ref_type = ref_type($refname);
#
# Use rev-parse so that fancy symbolic names, etc. can be used
......@@ -305,38 +314,57 @@ foreach my $refline (@reflines) {
# Figure out what type of change occured (update, creation, deletion, etc.)
# and what type of objects (commit, tree, etc.) we got
#
my $ct = change_type($oldrev,$newrev);
my $ct = change_type($oldrev,$newrev,$ref_type);
my $old_type = rev_type($oldrev);
my $new_type = rev_type($newrev);
debug("Change type: $ct ($old_type,$new_type)");
#
# For now, only handle commits that update existing branches or make
# new ones
# For now, only handle commit objects. Tag objects require extra work.
#
if ($new_type ne "commit") {
if ($new_type && $new_type ne "commit") {
debug("Skipping non-commit");
next;
}
#
# Get all commits between these two revisions
# and all files that changed
# Figure out which commits we're interested in based on reference type
# and change type.
#
my @commits = get_commits($oldrev,$newrev,$refname);
if ($ref_type eq 'tag') {
if ($ct eq $CT_DELETE) {
# We want to know where the tag used to point before deletion
push @commits, $oldrev;
} else {
# Tags only have delete, create, and update. Rewind and rebase
# don't make sense in tag context.
#
# We only care about the new value of the tag here.
push @commits, $newrev;
}
} elsif ($ref_type eq 'branch') {
if ($ct eq $CT_DELETE) {
# We want to know where the branch used to point before deletion
push @commits, $oldrev;
} elsif ($ct eq $CT_REWIND) {
# There's no new history to show, but we still want to see where
# the branch now points.
push @commits, $newrev;
} else {
@commits = get_commits($oldrev,$newrev,$refname);
# We only want to see *new* commits, which means that commits already
# in the main repository need to be excluded too.
if (defined $exclude_repo) {
@commits = filter_out_objects_in_repo($exclude_repo, @commits);
}
}
}
next unless (@commits);
debug("commits are: ", join(" ",@commits));
my @changed_files;
if (defined $exclude_repo || $ct eq $CT_FORCED_UPDATE) {
if (defined $exclude_repo) {
@commits = filter_out_objects_in_repo($exclude_repo, @commits);
next unless (@commits);
}
@changed_files = map { changed_files_single_revision($_) } @commits;
} else {
@changed_files = changed_files($oldrev,$newrev);
}
@changed_files = changed_files(@commits);
debug("Changed files: ", join(",",@changed_files));
#
......@@ -364,8 +392,8 @@ debug("finishing");
# Does this change represent the creation, deletion, or update of an object?
# Takes old and new revs
#
sub change_type($$) {
my ($oldrev, $newrev) = @_;
sub change_type($$$) {
my ($oldrev, $newrev, $ref_type) = @_;
#
# We can detect creates and deletes by looking for a special 'null'
......@@ -375,13 +403,18 @@ sub change_type($$) {
return $CT_CREATE;
} elsif ($newrev eq $EMPTYREV) {
return $CT_DELETE;
} elsif ($ref_type eq 'tag') {
return $CT_UPDATE;
} else {
my $merge_base = get_merge_base($oldrev,$newrev);
my $merge_base = get_merge_base($oldrev,$newrev);
my $oldrev = revparse($oldrev);
my $newrev = revparse($newrev);
if ($merge_base eq $oldrev) {
return $CT_UPDATE;
return $CT_UPDATE;
} elsif ($merge_base eq $newrev) {
return $CT_REWIND;
} else {
return $CT_FORCED_UPDATE;
return $CT_REBASE;
}
}
}
......@@ -396,6 +429,20 @@ sub rev_type($) {
return $rev_type;
}
#
# Find out what type of reference this is
#
sub ref_type($) {
my ($ref) = @_;
my $type;
if ($ref =~ m#^refs/heads/#) {
$type = 'branch';
} elsif ($ref =~ m#^refs/tags/#) {
$type = 'tag';
}
return $type;
}
#
# Parse (possibly) symbolic revision name into hash
# Note: Dies if the revision name is bogus!
......@@ -413,30 +460,31 @@ sub revparse($) {
}
#
# Given two revisions, return a list of the files that were changed between
# them
# Given a list of commit object hashes, return the list of files changed by
# all commits.
#
sub changed_files($$) {
my ($oldrev,$newrev) = @_;
sub changed_files(@) {
my %files;
my $revstring = rev_string($oldrev,$newrev);
debug("running '$GIT diff-tree -r --name-only '$revstring''");
my @lines = `$GIT diff-tree -r --name-only '$revstring' $STDERRNULL`;
chomp @lines;
return @lines;
}
debug("running '$GIT diff-tree --stdin -r --name-only --no-commit-id' on '@_'");
my $pid = open2(\*OUT, \*IN, "$GIT diff-tree --stdin -r --name-only --no-commit-id");
#
# Given one revision, return a list of the files that were changed between
# it and its parents
#
sub changed_files_single_revision($) {
my ($rev) = @_;
print IN "$_\n" for (@_);
close(IN);
while (<OUT>) {
chomp;
$files{$_} = 1;
}
close(OUT);
debug("running '$GIT diff-tree -r --name-only '$rev''");
my @lines = `$GIT diff-tree -r --name-only '$rev' $STDERRNULL`;
chomp @lines;
return @lines;
waitpid($pid, 0);
my $rc = $? >> 8;
if ($rc) {
die "'git diff-tree' exited with return code $rc\n";
}
return keys(%files);
}
#
......@@ -581,7 +629,7 @@ sub commit_mail($\@$@) {
#
# Construct the subject line. For now, we just say what repo (if defined)
# and what branch it happened on
# and what branch/tag it happened on
#
my $subject = "git commit: ";
my $ref_type;
......@@ -589,31 +637,37 @@ sub commit_mail($\@$@) {
$subject .= "[$reponame] ";
}
if ($refname =~ m#refs/tags/#) {
$ref_type = 'tag';
} elsif ($refname =~ m#refs/heads/#) {
$ref_type = 'branch';
}
$ref_type = ref_type($refname);
$subject .= $ref_type . ' ' . short_refname($refname);
my $what_happened;
if ($ct eq $CT_UPDATE) {
$subject .= " updated";
} elsif ($ct eq $CT_FORCED_UPDATE) {
$subject .= " force-updated";
$what_happened .= 'updated';
} elsif ($ct eq $CT_REWIND) {
$what_happened .= 'rewound';
} elsif ($ct eq $CT_REBASE) {
$what_happened .= 'rebased';
} elsif ($ct eq $CT_CREATE) {
$subject .= " created";
$what_happened .= 'created';
} elsif ($ct eq $CT_DELETE) {
$subject .= " deleted";
$what_happened .= 'deleted';
}
$subject .= ' ' . $what_happened;
my $actionstring = ucfirst($ref_type) . ' ' . short_refname($refname) .
" has been ${ct}d\n\n";
" has been $what_happened";
if ($ct eq $CT_FORCED_UPDATE) {
$actionstring .= "New and/or modified commits shown below\n\n";
if ($ct eq $CT_REBASE) {
$actionstring .= ". The following commits are new or have been modified:";
} elsif ($ct eq $CT_REWIND) {
$actionstring .= " to point to the following commit:";
} elsif ($ct eq $CT_DELETE) {
$actionstring .= ". It previously pointed to the following commit:";
}
$actionstring .= "\n\n";
my @fullbody;
foreach my $rev (@$commits) {
#
......@@ -654,38 +708,47 @@ sub commit_mail($\@$@) {
#
sub get_commits($$$) {
my ($oldrev,$newrev,$refname) = @_;
my $ct = change_type($oldrev,$newrev);
my $ct = change_type($oldrev,$newrev, ref_type($refname));
#
# If this is an update, we can just ask git for the revisions between the
# two revisions we were given. We call git-cherry for this information
# so that we can identify if a particular commit already exists in the
# repository with a different commit hash (for the rebase case).
# two revisions we were given.
#
if ($ct eq $CT_UPDATE || $ct eq $CT_FORCED_UPDATE) {
if ($ct eq $CT_UPDATE) {
my $revstring = "$oldrev..$newrev";
debug("running '$GIT rev-list --reverse --date-order '$revstring'");
my @revs = `$GIT rev-list --reverse --date-order '$revstring'`;
chomp @revs;
return @revs;
} elsif ($ct eq $CT_REBASE) {
debug("running '$GIT cherry '$oldrev' '$newrev'");
# Only return revs prefixed with a '+' since commits prefixed with a
# '-' are already in the repository with a different commit hash. We
# should have seen those already, so we don't want to see them here.
# Note that '+' commits may not be "new" per-se, but they are not the
# same as the existing commit with the same commit message due to
# rebasing.
# '-' are already in the repository with a different commit hash.
#
# The '-' commits are the same as their non-rebased counterparts, except
# their ancestry is different. For the email messages, we don't care
# about these since we should have seen the original commits already.
#
# The '+' commits are either new or are rebased commits whose *content*
# has changed. We definitely want to see these. Note that this only
# applies to the content of the commit, not the commit message.
my @revs;
my @all_revs;
for (`$GIT cherry '$oldrev' '$newrev'`) {
debug($_);
chomp;
unshift @revs, $1 if (/^\+\s+(.*)$/);
@_ = split /\s+/, $_;
unshift @revs, $_[1] if ($_[0] eq '+');
unshift @all_revs, $_[1];
}
return @revs;
# If cherry finds that all of the commits are already present,
# report 'em all anyway. We still need to know that the rebase
# happened, and reporting just the head doesn't make any sense.
@revs = @all_revs if (!@revs);
return @revs;
} elsif ($ct eq $CT_CREATE) {
#
# For tags, just return the new revision. This at least tells us
# where the tag points.
#
if ($refname =~ m#^refs/tags/#) {
return ($newrev);
}
#
# If it's a create, we have to be a bit more fancy: we look for all
# commits reachable from the new branch, but *not* reachable from any
......@@ -710,6 +773,12 @@ sub get_commits($$$) {
debug("running '$GIT rev-parse --not $other_branches | $GIT rev-list --pretty --stdin $newrev'");
my @commits = `$GIT rev-parse --not $other_branches | $GIT rev-list --reverse --date-order --stdin $newrev`;
# We always want to be notified when a branch is created, so if there are no commits reachable
# from only this branch just report on the head of the branch.
push @commits, $newrev if (!@commits);
debug("commits are @commits");
chomp @commits;
return @commits;
}
......@@ -779,9 +848,17 @@ sub short_refname($) {
my $refname = `git rev-parse --abbrev-ref $ref $STDERRNULL`;
chomp $refname;
# This shouldn't be necessary, but return the full ref if
# rev-parse doesn't return anything.
# Fall back to full name if rev-parse fails for some reason
$refname = $ref if (!$refname);
debug("got short refname \"$refname\"");
# If the ref didn't get shortened, it may be because it was deleted. Just
# chop off 'refs/heads' or 'refs/tags' and return the rest.
if ($refname =~ m#^refs/(?:heads|tags)/(.*)#) {
$refname = $1;
}
return $refname;
}
......@@ -825,25 +902,6 @@ sub get_config($$) {
}
}
#
# Return an appropriate string to get a revision: if the ref was created or
# deleted, this looks a little different
#
sub rev_string($$) {
my ($oldrev, $newrev) = @_;
my $ct = change_type($oldrev,$newrev);
if ($ct eq $CT_UPDATE) {
return "$oldrev..$newrev";
} elsif ($ct eq $CT_CREATE) {
return $newrev;
} elsif ($ct eq $CT_DELETE) {
return $oldrev;
} else {
# Shouldn't be able to get here
return undef;
}
}
#
# Returns the merge base (i.e., common ancestor) of
# the two supplied revisions.
......
......@@ -25,7 +25,7 @@ SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl \
spewconlog opsdb_control newnode suchown archive_list \
wanodecheckin wanodecreate spewimage \
anonsendmail epmodeset fixexpinfo node_traffic \
dumpdescriptor
dumpdescriptor subboss_tftpboot_sync
WEB_SBIN_SCRIPTS= webnewnode webdeletenode webspewconlog webarchive_list \
webwanodecheckin webspewimage
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT