Commit 78503406 authored by Leigh Stoller's avatar Leigh Stoller

Redo the entire template library. I've been meaning to use perl

"object" and this was a good opportunity to see if they are useful and
easy enough to use. Yep they are; the code is much cleaner with many
fewer utility functions to get at stuff. I recommend this approach
from now on.

The problem is the php side, which ends up duplicating some stuff, but
in the old style. This is not so bad for the template code since I
have made it a point not to do anything but display functions in php;
all modifications are handled in the backend.
parent f8d54de3
......@@ -23,7 +23,7 @@ BIN_STUFF = power snmpit tbend tbprerun tbreport \
tbswap nseswap tarfiles_setup node_history tbrsync \
node_attributes archive_control template_create \
template_swapin template_swapout template_graph \
template_exprun template_delete
template_exprun template_delete template_metadata
SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup \
batch_daemon exports_setup reload_daemon sched_reserve \
......@@ -53,7 +53,7 @@ LIBEXEC_STUFF = rmproj wanlinksolve wanlinkinfo \
spewrpmtar webtarfiles_setup webfrisbeekiller gentopofile \
webnodeattributes webarchive_control webtemplate_create \
webtemplate_swapin webtemplate_swapout webtemplate_exprun \
webtemplate_graph
webtemplate_graph webtemplate_metadata
LIB_STUFF = libtbsetup.pm exitonwarn.pm libtestbed.pm snmpit_intel.pm \
snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm power_rpc27.pm \
......@@ -62,7 +62,7 @@ LIB_STUFF = libtbsetup.pm exitonwarn.pm libtestbed.pm snmpit_intel.pm \
snmpit_nortel.pm \
libaudit.pm libreboot.pm libosload.pm libtestbed.py \
libadminmfs.pm libtblog.pm libtblog_simple.pm libArchive.pm \
power_mail.pm power_whol.pm libTemplates.pm
power_mail.pm power_whol.pm Template.pm
ifeq ($(SYSTEM),FreeBSD)
FBSDVERSION := $(shell uname -v | sed -e 's/FreeBSD \([0-9]\).*/FreeBSD\1/')
......
This diff is collapsed.
......@@ -53,7 +53,7 @@ if ($UID) {
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use libTemplates;
use Template;
# Be careful not to exit on transient error; 0 means infinite retry.
$libdb::DBQUERY_MAXTRIES = 0;
......@@ -92,9 +92,7 @@ my $userdir;
my $workdir;
my $user_name = "Testbed Operations";
my $user_email = "$TBOPS";
my $istemplate;
my $guid;
my $version;
my $template;
#
# Turn off line buffering on output
......@@ -360,11 +358,10 @@ sub dosomething($$)
# We still generate the log file, but it is not available via the
# web interface. Needs more thought.
#
my $exptidx = $exphash->{'idx'};
$istemplate = libTemplates::IsTemplateInstanceExperiment($exptidx);
if ($istemplate) {
libTemplates::MapExptidxtoTemplate($exptidx, \$guid, \$version) == 0
or fatal("Error template info the experiment $pid/$eid");
my $exptidx = $exphash->{'idx'};
my $instance = Template::Instance->LookupByExptidx($exptidx);
if (defined($instance)) {
$template = $instance->template();
}
#
......@@ -593,8 +590,11 @@ sub swapexp($;$)
my $running = ($exphash->{'state'} eq EXPTSTATE_ACTIVE);
if ($running) {
if ($istemplate) {
system("$template_swapout -b -e $eid $guid/$version");
if (defined($template)) {
my $guid = $template->guid();
my $vers = $template->vers();
system("$template_swapout -b -e $eid $guid/$vers");
}
else {
system("$swapexp -b -s out $pid $eid");
......@@ -637,8 +637,11 @@ sub cancelexp($)
# It does not matter if the experiment is running; endexp does the
# right thing.
#
if ($istemplate) {
system("$template_swapout -b -e $eid $guid/$version");
if (defined($template)) {
my $guid = $template->guid();
my $vers = $template->vers();
system("$template_swapout -b -e $eid $guid/$vers");
}
else {
system("$endexp -b $pid $eid");
......
......@@ -1280,7 +1280,7 @@ END {
# We are screwed; a recursive error. Someone will have to clean
# up by hand.
#
SENDMAIL(TBOPS,
SENDMAIL($TBOPS,
"Experiment Configure Failure: $pid/$eid",
"Recursive error in cleanup! This is very bad.");
$? = $saved_exitcode;
......
......@@ -36,7 +36,7 @@ use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
use libTemplates;
use Template;
# Locals.
my $idleswap = 0;
......@@ -162,13 +162,10 @@ $EUID = $UID = $unix_uid;
$ENV{'USER'} = $creator;
$ENV{'LOGNAME'} = $creator;
if (libTemplates::IsTemplateInstanceExperiment($exptidx)) {
my ($guid, $vers);
if (libTemplates::MapExptidxtoTemplate($exptidx, \$guid, \$vers) < 0) {
die("*** $0:\n".
" Error mapping experiment to its template!\n");
}
if (my $instance = Template::Instance->LookupByExptidx($exptidx)) {
my $guid = $instance->guid();
my $vers = $instance->vers();
exec "$template_swapout -e $eid $guid/$vers";
}
else {
......
This diff is collapsed.
......@@ -5,6 +5,7 @@
# All rights reserved.
#
use English;
use strict;
use Getopt::Std;
use POSIX qw(isatty setsid);
use POSIX qw(strftime);
......@@ -44,12 +45,12 @@ my $description = "";
my $modify = 0;
my $pid;
my $tid;
my $eid;
my $gid;
my $inputfile;
# For modify.
my $parent_guid;
my $parent_vers;
my $parent_template;
#
# Configure variables
......@@ -66,9 +67,11 @@ my $CONTROL = "@USERNODE@";
# Locals
my $user_name;
my $user_email;
my $template;
my $dbuid;
my $guid;
my $version = 1;
my $vers;
my $eid;
my $archive_idx = 0;
# For the END block below.
my $cleaning = 0;
......@@ -93,7 +96,7 @@ use libdb;
use libtestbed;
use libtblog;
use libArchive;
use libTemplates;
use Template;
# Be careful not to exit on transient error
$libdb::DBQUERY_MAXTRIES = 120;
......@@ -163,78 +166,20 @@ if (! TBProjAccessCheck($dbuid, $pid, $gid, TB_PROJECT_CREATEEXPT)) {
# On modify, must check access to the parent template.
#
if ($modify) {
if (!libTemplates::TBValidExperimentTemplate($parent_guid, $parent_vers)) {
$parent_template = Template->Lookup($parent_guid, $parent_vers);
if (!defined($parent_template)) {
tbdie("Experiment template $parent_guid/$parent_vers does not exist!");
}
if (!libTemplates::TBExptTemplateAccessCheck($dbuid,
$parent_guid,
TB_EXPT_READINFO)) {
if (!$parent_template->AccessCheck($dbuid, TB_EXPT_READINFO)) {
tbdie("You do not have permission to modify experiment template ".
"$parent_guid/$parent_vers");
}
# Hmm, this is redundant in the table.
$guid = $parent_guid;
}
else {
#
# Otherwise, grab new guid before tables are locked.
#
if (libTemplates::NewGUID(\$guid) < 0) {
tbdie("Could not get a new GUID!");
}
}
#
# Create a template record; the tid has to be unique within the project,
# across both the experiments and template tables, since there will be
# two paths to experiment creation for the near term. See corresponding
# code in batchexp.
#
DBQueryFatal("lock tables experiments write, ".
" experiment_templates write");
my $query_result =
DBQueryFatal("select pid,tid from experiment_templates ".
"where tid='$tid' and pid='$pid'");
if ($query_result->numrows) {
DBQueryWarn("unlock tables");
tbdie("Template ID $tid in project $pid is already in use!");
}
#
# Find unused version number now that tables are locked.
#
if ($modify) {
$query_result =
DBQueryFatal("select MAX(vers) from experiment_templates ".
"where guid='$guid'");
$version = ($query_result->fetchrow_array())[0];
$version++;
}
# Currently, we make up an eid using the guid and version. This will
# change later.
$eid = "T${guid}-${version}";
#
# Sanity check; make sure this eid is not in use. Tables are still locked.
#
$query_result =
DBQueryFatal("select pid,eid from experiments ".
"where eid='$eid' and pid='$pid'");
if ($query_result->numrows) {
DBQueryWarn("unlock tables");
tbdie("Experiment ID $eid in project $pid is already in use!");
}
#
# Create a template record to "reserve" the tid. We pass the rest of
# it off to the normal experiment path right now (too much stuff to
# duplicate with a deadline looming right around the corner).
# Create a template record.
#
my %args = ();
......@@ -242,21 +187,16 @@ if ($modify) {
$args{'parent_guid'} = $parent_guid;
$args{'parent_vers'} = $parent_vers;
}
$args{'guid'} = $guid;
$args{'vers'} = $version;
$args{'pid'} = $pid;
$args{'gid'} = $gid;
$args{'tid'} = $tid;
$args{'eid'} = $eid;
$args{'uid'} = $dbuid;
$args{'description'} = DBQuoteSpecial($description);
$args{'description'} =~ s/^\'(.*)\'$/$1/;
if (libTemplates::NewTemplateRecord(\%args) < 0) {
if (! ($template = Template->Create(\%args))) {
tbdie("Could not create a new template record!");
}
# Now safe to unlock; the tid/eid is reserved;
DBQueryFatal("unlock tables");
#
# At this point, we need to force a cleanup no matter how we exit.
......@@ -267,29 +207,32 @@ $justexit = 0;
#
# The description is versioned metadata the user can modify.
#
libTemplates::NewTemplateMetadata($guid, $version,
"description", $description, $dbuid) == 0
$template->NewMetadata("description", $description, $dbuid) == 0
or fatal(-1, "Failed to insert metadata record for description");
#
# The TID is versioned metadata the user can modify.
#
libTemplates::NewTemplateMetadata($guid, $version, "TID", $tid, $dbuid) == 0
$template->NewMetadata("TID", $tid, $dbuid) == 0
or fatal(-1, "Failed to insert metadata record for description");
#
# Copy the rest of the metadata.
# Copy the rest of the metadata from parent to child
#
if ($modify) {
libTemplates::CopyTemplateMetadata($parent_guid,
$parent_vers, $version, $dbuid)
== 0 or fatal(-1, "Failed to copy metadata records");
$template->CopyMetadata($parent_template, $dbuid) == 0
or fatal(-1, "Failed to copy metadata records");
}
# Grab stuff we need out of the template.
$guid = $template->guid();
$vers = $template->vers();
$eid = $template->eid();
# Now invoke batchexp to preload the experiment. Note special -x option.
system("$batchexp ".
"-x " . ($modify ? "T${parent_guid}-${parent_vers}" : "-") . " " .
"-q -i -f -E 'Experiment Template Preload $guid/$version' ".
"-x " . ($modify ? $parent_template->eid() : "-") . " " .
"-q -i -f -E 'Experiment Template Preload $guid/$vers' ".
"-p $pid -e $eid $inputfile");
fatal($? >> 8, "Oops")
if ($?);
......@@ -298,8 +241,9 @@ fatal($? >> 8, "Oops")
$exptcreated = 1;
# Input files are kept in the DB, with the template.
exit(-1)
if (libTemplates::AddTemplateInputFile($guid, $version, $inputfile) < 0);
fatal(-1, "Could not add NS file to template store")
if ($template->AddInputFile($inputfile) < 0);
#
# Grab archive index for new templates.
#
......@@ -317,14 +261,14 @@ else {
# Grab the archive index for the parent; the archive is shared.
#
libArchive::TBExperimentArchiveInfo($pid,
"T${parent_guid}-${parent_vers}",
$parent_template->eid(),
\$archive_idx, undef)
>= 0 or fatal(-1, "Could not get archive index for parent!");
# Do a commit point on the template archive since it does not actually
# change after it is created (instances are branches).
libArchive::TBCommitExperimentArchive($pid,
"T${parent_guid}-${parent_vers}",
$parent_template->eid(),
"TemplateCreate")
>= 0 or fatal(-1, "Failed to commit experiment archive!");
}
......@@ -333,7 +277,7 @@ else {
%args = ();
$args{'archive_idx'} = $archive_idx;
libTemplates::UpdateTemplateRecord($guid, $version, \%args) == 0
$template->Update(\%args) == 0
or fatal(-1, "Could not update template record!");
#
......@@ -342,7 +286,7 @@ libTemplates::UpdateTemplateRecord($guid, $version, \%args) == 0
# to get the parameters is via the parser, but we want to save this
# info forever (after the underlying experiment is terminated).
#
$query_result =
my $query_result =
DBQueryWarn("select name,value from virt_parameters ".
"where pid='$pid' and eid='$eid'");
......@@ -350,18 +294,7 @@ fatal(-1, "Could not get virt_parameters for $pid/$eid")
if (! $query_result);
while (my ($name, $value) = $query_result->fetchrow_array()) {
if (defined($value)) {
$value = DBQuoteSpecial($value);
}
else {
$value = "NULL";
}
DBQueryWarn("insert into experiment_template_parameters set ".
" parent_guid='$guid', ".
" parent_vers='$version', ".
" pid='$pid', tid='$tid', ".
" name='$name', value=$value")
$template->NewFormalParameter($name, $value) == 0
or fatal(-1, "Could not set formal parameter for $pid/$eid")
}
......@@ -526,11 +459,8 @@ sub cleanup()
# And delete all the other stuff?
}
libTemplates::DeleteTemplateInputFiles($guid, $version)
if (defined($guid));
libTemplates::DeleteTemplateRecord($guid, $version)
if (defined($guid));
$template->Delete()
if (defined($template));
}
sub fatal($$)
......@@ -558,7 +488,7 @@ END {
# We are screwed; a recursive error. Someone will have to clean
# up by hand.
#
SENDMAIL(TBOPS,
SENDMAIL($TBOPS,
"Template Creation Failure: $pid/$tid",
"Recursive error in cleanup! This is very bad.");
$? = $saved_exitcode;
......
......@@ -5,8 +5,9 @@
# All rights reserved.
#
use English;
use strict;
use Getopt::Std;
use POSIX qw(isatty setsid);
use POSIX qw(setsid);
use POSIX qw(strftime);
#
......@@ -61,8 +62,9 @@ my $dbuid;
my $logname;
my $user_name;
my $user_email;
my @versionlist = ();
my %instances = ();
my $template;
my @templates = ();
my %instances = (); # Indexed by vers.
# Protos
sub ParseArgs();
......@@ -76,7 +78,7 @@ use lib "@prefix@/lib";
use libdb;
use libtestbed;
use libtblog;
use libTemplates;
use Template;
# Be careful not to exit on transient error
$libdb::DBQUERY_MAXTRIES = 0;
......@@ -130,118 +132,100 @@ if ($waitmode) {
#
# Grab template info.
#
if (libTemplates::TemplateInfo($guid, $version) < 0) {
tbdie("Could not get info for template $guid/$version!");
$template = Template->Lookup($guid, $version);
if (!defined($template)) {
tbdie("Experiment template $guid/$version does not exist!");
}
if (! TBProjAccessCheck($dbuid,
$template->pid(), $template->gid(),
TB_PROJECT_CREATEEXPT)) {
tberror("You do not have permission to delete template $guid/$version");
exit(1);
}
#
# In recursive mode, build up a list of all of the children of this
# template.
# Ask for the children. If not in recursive mode, there better not be any!
#
if ($recursive) {
#
# Get the entire list of templates
#
my %children = ();
my @kids = ($version);
my $query_result =
DBQueryFatal("select vers,parent_vers from experiment_templates ".
"where parent_guid='$guid' ".
"order by vers desc");
my @children;
while (my ($vers, $parent_vers) = $query_result->fetchrow_array()) {
$children{$parent_vers} = []
if (!exists($children{$parent_vers}));
$template->Children(\@children) == 0
or tbdie("Could not construct list of child templates for $guid/$version");
# List of all children for the parent.
push(@{ $children{$parent_vers} }, $vers);
}
# Descend the tree getting all children recursively.
while (@kids) {
my $kid = pop(@kids);
# New kid to delete.
push(@versionlist, $kid);
# New children of kid
unshift(@kids, @{ $children{$kid} })
if (exists($children{$kid}));
}
# Remove most recent templates first.
@versionlist = sort {$b <=> $a} @versionlist;
if ($recursive) {
@templates = @children;
}
else {
tbdie("Cannot delete template $template cause it has children @children")
if (@children);
# Just one.
@versionlist = ($version);
@templates = ($template);
}
print STDERR "Looking at @versionlist\n"
if ($debug);
if ($debug) {
print STDERR "Looking at @templates\n"
if ($debug);
}
#
# Now look at each template and confirm there are no instantiations that
# are currently active (there will be old ones since we do not delete them).
# We also want to find out the instance ids and the experiment ids so we
# can get the other tables.
# Now get the instance lists for each template and see if any are active.
#
my $active = 0;
foreach my $vers (@versionlist) {
my $query_result =
DBQueryFatal("select i.idx,i.exptidx,e.idx ".
" from experiment_template_instances as i ".
"left join experiments as e on e.idx=i.exptidx ".
"where i.parent_guid='$guid' and i.parent_vers='$vers'");
while (my ($i_idx,$i_exptidx,$exptidx) = $query_result->fetchrow_array()) {
if (defined($exptidx)) {
tbwarn("Template $guid/$vers in still instantiated! ".
"Please terminate experiment $exptidx.");
}
$instances{$vers} = []
if (!exists($instances{$vers}));
foreach my $template (@templates) {
my @instance_list;
# List of all instances for this version of the template.
push(@{ $instances{$vers} }, $i_idx);
$template->InstanceList(1, \@instance_list) == 0
or tbdie("Could not get instance list for $template!");
print STDERR "Will remove instance $i_idx ($i_exptidx)\n"
if ($debug);
print STDERR "Instances for $template: @instance_list\n"
if ($debug);
# Check each instance to make sure its not a current instance.
foreach my $instance (@instance_list) {
my $current = $instance->Instantiated();
exit(-1)
if ($current < 0);
if ($current) {
tberror("Instance $instance is still instantiated!");
$active++;
}
}
$instances{$template->vers()} = [ @instance_list ];
}
exit(1)
if ($active);
#
# Okay, delete the instance records.
# Okay, do the deletions.
#
foreach my $vers (@versionlist) {
foreach my $instance (@{ $instances{$vers} }) {
print "Deleting template instance $guid, $vers, $instance ... \n";
libTemplates::DeleteTemplateInstanceRecord($guid, $vers, $instance)
== 0 or exit(-1);
foreach my $template (@templates) {
my @instance_list = @{ $instances{$template->vers()} };
print STDERR "Deleting instances: @instance_list\n"
if ($debug && @instance_list);
foreach my $instance (@instance_list) {
print "Deleting template instance $instance ... \n";
$instance->Delete() == 0
or tbdie("Could not delete instance $instance");
}
my $pid = $template->pid();
my $eid = $template->eid();
# This is the hidden experiment under the template.
if (ExpState($pid, $eid)) {
system("$endexp -x -q -w $pid $eid");
exit(-1)
if ($?);
}
#
# And then the template experiment.
#
my ($pid, $eid);
libTemplates::TemplateInfo($guid, $vers, \$pid, undef, undef, \$eid) == 0
or exit(-1);
system("$endexp -x -q -w $pid $eid");
exit(-1)
if ($?);
# And delete all the other stuff?
libTemplates::DeleteTemplateInputFiles($guid, $vers) == 0
or exit(-1);
libTemplates::DeleteTemplateRecord($guid, $vers) == 0
or exit(-1);
# And finally the template record.
$template->Delete() == 0
or tbdie("Could not delete template $template");
}
#
......@@ -257,6 +241,8 @@ exit(0);
#
sub ParseArgs()
{
my %options;
if (! getopts($optlist, \%options)) {
usage();
}
......
......@@ -5,6 +5,7 @@
# All rights reserved.
#
use English;
use strict;
use Getopt::Std;
use POSIX qw(isatty setsid);
use POSIX qw(strftime);
......@@ -49,16 +50,11 @@ my $paramfile;
my %parameters = ();
my $action;
my $description;
my $pid;
my $tid;
my $runid;
my $eid;
my $gid;
my $guid;
my $version;
my $inputfile;
my $exptidx;
my $instidx;
my $runid;
#
# Configure variables
......@@ -76,9 +72,10 @@ my $CONTROL = "@USERNODE@";
my $user_name;
my $user_email;
my $dbuid;
my $logname;
my $runidx;
my $currunidx;
my $pid;
my $exptidx;
my $template;
my $instance;
# For the END block below.
my $cleaning = 0;
my $justexit = 1;
......@@ -86,6 +83,7 @@ my $justexit = 1;
# Programs we need
my $checkquota = "$TB/sbin/checkquota";
my $archcontrol = "$TB/bin/archive_control";
my $eventcontrol= "$TB/bin/eventsys_control";
# Protos
sub ParseArgs();
......@@ -99,7 +97,7 @@ use lib "@prefix@/lib";
use libdb;
use libtestbed;
use libtblog;
use libTemplates;
use Template;
# Be careful not to exit on transient error
$libdb::DBQUERY_MAXTRIES = 0;
......@@ -159,42 +157,37 @@ if ($waitmode) {
}
#
# Grab template info.
# Grab template and do access check.
#
if (libTemplates::TemplateInfo($guid, $version, \$pid, \$tid, \$gid) < 0) {
tbdie("Could not get info for template $guid/$version!");
}
# This is the instance IDX ...
if (! TBExptIDX($pid, $eid, \$exptidx)) {
fatal(-1, "Could not get experiment index for $pid,$eid!");
$template = Template->Lookup($guid, $version);
if (!defined($template)) {
tbdie("Experiment template $guid/$version does not exist!");
}
#
# Make sure it is valid and actually running.
#
if (! libTemplates::IsTemplateInstanceExperiment($exptidx)) {
tberror("No such template instance experiment $exptidx!");
if (! TBProjAccessCheck($dbuid,
$template->pid(), $template->gid(),
TB_PROJECT_CREATEEXPT)) {
tberror("You do not have permission to instantiate template ".
"$guid/$version");
exit(1);
}
$pid = $template->pid();
#
# And get the instance info,
# Grab Instance.
#
if (libTemplates::TemplateInstanceInfoByExptidx($exptidx, \$currunidx,
undef, \$instidx) < 0) {
fatal(-1, "Could not get current experiment run index!");
if (! TBExptIDX($pid, $eid, \$exptidx)) {
tbdie("Could not get experiment index for $pid,$eid!");
}
if (ExpState($pid, $eid) ne EXPTSTATE_ACTIVE()) {
tberror("Template instance experiment $exptidx in not active!");
exit(1);
}
$instance = Template::Instance->LookupByExptidx($exptidx);
#
# Make sure UID is allowed to create experiments in this project.