Commit 26f89fa3 authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Checkpoint dataset and image changes. APT becomes the default for

all users. Cloudlab added to the list, but not exposed except to
admins and studly users.
parent d3674a3e
......@@ -440,7 +440,7 @@ sub CreateDataset($)
if (defined($self->expires()));
my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/;
$cmurl =~ s/protogeni/protogeni\/stoller/;
return Genixmlrpc::CallMethod($cmurl, $context, "CreateDataset", $args);
}
......@@ -471,7 +471,7 @@ sub DeleteDataset($)
$speaksfor_credential->asString()],
};
my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/;
$cmurl =~ s/protogeni/protogeni\/stoller/;
return Genixmlrpc::CallMethod($cmurl, $context, "DeleteDataset", $args);
}
......@@ -502,7 +502,7 @@ sub DescribeDataset($)
$speaksfor_credential->asString()],
};
my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/;
$cmurl =~ s/protogeni/protogeni\/stoller/;
return Genixmlrpc::CallMethod($cmurl, $context, "DescribeDataset", $args);
}
......
......@@ -53,6 +53,8 @@ sub GenCredentials($$;$)
{
my ($target, $geniuser, $privs) = @_;
my ($speaksfor, $credential);
# If the caller does not want a speaksfor, do not generate.
my $wantspeaksfor = wantarray;
my $speaker_signer = $GeniCredential::LOCALSA_FLAG;
#
......@@ -64,21 +66,6 @@ sub GenCredentials($$;$)
$speaker_signer = "/usr/testbed/etc/utah-apt.sa";
}
#
# The Utah SA is always the speaker, even if the user is a guest
# with the alternate CA.
#
my $sa_certificate = GeniCertificate->LoadFromFile($SACERT);
if (!defined($sa_certificate)) {
print STDERR "Could not load certificate from $SACERT\n";
goto bad;
}
my $sa_authority = GeniAuthority->Lookup($sa_certificate->urn());
if (!defined($sa_authority)) {
prnt STDERR "Could not load SA authority object\n";
goto bad;
}
#
# If a local user account, but a nonlocal id, then we should
# have a speaksfor credential stored, as well as a certificate
......@@ -92,10 +79,12 @@ sub GenCredentials($$;$)
print STDERR "No stored speaksfor/certificate for $geniuser\n";
goto bad;
}
$speaksfor = GeniCredential->CreateFromSigned($speaksfor_string);
if (!defined($speaksfor)) {
print STDERR "Could not create speaksfor credential\n";
goto bad;
if ($wantspeaksfor) {
$speaksfor = GeniCredential->CreateFromSigned($speaksfor_string);
if (!defined($speaksfor)) {
print STDERR "Could not create speaksfor credential\n";
goto bad;
}
}
my $certificate =
GeniCertificate->LoadFromString($certificate_string);
......@@ -106,15 +95,31 @@ sub GenCredentials($$;$)
$credential = GeniCredential->Create($target, $certificate);
}
else {
$speaksfor = GeniCredential->Create($geniuser, $sa_authority);
if (!defined($speaksfor)) {
print STDERR "Could not create speaksfor credential\n";
goto bad;
}
$speaksfor->SetType("speaksfor");
if ($speaksfor->Sign($speaker_signer)) {
print STDERR "Could not sign speaksfor credential\n";
goto bad;
if ($wantspeaksfor) {
#
# The Utah SA is always the speaker, even if the user is a guest
# with the alternate CA.
#
my $sa_certificate = GeniCertificate->LoadFromFile($SACERT);
if (!defined($sa_certificate)) {
print STDERR "Could not load certificate from $SACERT\n";
goto bad;
}
my $sa_authority = GeniAuthority->Lookup($sa_certificate->urn());
if (!defined($sa_authority)) {
prnt STDERR "Could not load SA authority object\n";
goto bad;
}
$speaksfor = GeniCredential->Create($geniuser, $sa_authority);
if (!defined($speaksfor)) {
print STDERR "Could not create speaksfor credential\n";
goto bad;
}
$speaksfor->SetType("speaksfor");
if ($speaksfor->Sign($speaker_signer)) {
print STDERR "Could not sign speaksfor credential\n";
goto bad;
}
}
$credential = GeniCredential->Create($target, $geniuser);
}
......@@ -134,7 +139,10 @@ sub GenCredentials($$;$)
print STDERR "Could not sign $target credential\n";
goto bad;
}
return ($credential, $speaksfor);
if (wantarray) {
return ($credential, $speaksfor);
}
return $credential;
bad:
return ();
}
......
......@@ -672,14 +672,14 @@ sub Unlock($)
#
sub UpdateDiskImage($$)
{
my ($self, $image_urn) = @_;
my ($self, $image_url) = @_;
my $rspec = GeniXML::Parse($self->rspec());
if (! defined($rspec)) {
print STDERR "UpdateDiskImage: Could not parse rspec\n";
return -1;
}
my ($node) = GeniXML::FindNodes("n:node", $rspec)->get_nodelist();
GeniXML::SetDiskImage($node, $image_urn);
GeniXML::SetDiskImage($node, $image_url);
if ($self->UpdateVersion({"rspec" => GeniXML::Serialize($rspec)})) {
print STDERR "UpdateDiskImage: Could not update rspec\n";
return -1;
......@@ -687,12 +687,135 @@ sub UpdateDiskImage($$)
return 0;
}
#
# We need to convert from using URNs to using URLs for disk images,
# since we want to support choosing the backend. This list is the
# list as of the conversion, at the APT. Before we instantiate, look
# at the rspec and update the URNs to URLs based on this list.
#
my %APTImages = (
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:demo-profile' => 'https://www.apt.emulab.net/image_metadata.php?uuid=3937a58c-7b2f-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:docker' => 'https://www.apt.emulab.net/image_metadata.php?uuid=5ae4aeef-7b2f-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:docker-running-env' => 'https://www.apt.emulab.net/image_metadata.php?uuid=a130e3e1-7b2f-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:docker-running-env-02' => 'https://www.apt.emulab.net/image_metadata.php?uuid=f30dfd4d-7b2f-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:Docker1' => 'https://www.apt.emulab.net/image_metadata.php?uuid=31d95a10-7b30-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:ESA' => 'https://www.apt.emulab.net/image_metadata.php?uuid=72d8b175-7b35-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:firstclone' => 'https://www.apt.emulab.net/image_metadata.php?uuid=d49dc243-7b31-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:FPTaylorVM' => 'https://www.apt.emulab.net/image_metadata.php?uuid=f2e9010e-7b31-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:FrequentDirection' => 'https://www.apt.emulab.net/image_metadata.php?uuid=1c6b0966-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:geni-lib' => 'https://www.apt.emulab.net/image_metadata.php?uuid=441f3d1c-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:gimiovsomf6' => 'https://www.apt.emulab.net/image_metadata.php?uuid=6260f126-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:hadoop' => 'https://www.apt.emulab.net/image_metadata.php?uuid=ffa8648d-3524-11e4-8944-81966d62745f',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:hg' => 'https://www.apt.emulab.net/image_metadata.php?uuid=91dc43cb-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:imageusage' => 'https://www.apt.emulab.net/image_metadata.php?uuid=adc56dfa-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:Iperf' => 'https://www.apt.emulab.net/image_metadata.php?uuid=d0c81a2d-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:labwikiXORP' => 'https://www.apt.emulab.net/image_metadata.php?uuid=eee561e4-7b32-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:myapt' => 'https://www.apt.emulab.net/image_metadata.php?uuid=13ba0c1d-7b33-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:myclone' => 'https://www.apt.emulab.net/image_metadata.php?uuid=301cb7f7-7b33-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:myimage' => 'https://www.apt.emulab.net/image_metadata.php?uuid=03a2a844-7b2c-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:myvm' => 'https://www.apt.emulab.net/image_metadata.php?uuid=6334311f-7a59-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:myvm2' => 'https://www.apt.emulab.net/image_metadata.php?uuid=aa8a9f6a-7a79-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:ricci-cav-2014' => 'https://www.apt.emulab.net/image_metadata.php?uuid=b3d662b2-7b35-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:rob-profile' => 'https://www.apt.emulab.net/image_metadata.php?uuid=3126f6f2-7b34-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:robtestimage' => 'https://www.apt.emulab.net/image_metadata.php?uuid=4d4357cf-7b34-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:singlenodelime' => 'https://www.apt.emulab.net/image_metadata.php?uuid=608fcd94-7b34-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:singlenodelime-2' => 'https://www.apt.emulab.net/image_metadata.php?uuid=8db1ab6c-7b34-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:singlevmlime' => 'https://www.apt.emulab.net/image_metadata.php?uuid=b192160c-7b34-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:smack-cav2014-test' => 'https://www.apt.emulab.net/image_metadata.php?uuid=a458b87c-7b36-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:ubuntu1204maas' => 'https://www.apt.emulab.net/image_metadata.php?uuid=e5326c2e-7b36-11e4-9439-db9edc46fe2c',
'urn:publicid:IDN+utahddc.geniracks.net+image+emulab-net:Ubuntu1404' => 'https://www.apt.emulab.net/image_metadata.php?uuid=083830cf-7b37-11e4-9439-db9edc46fe2c',
);
sub ConvertDiskImages($)
{
my ($self) = @_;
my $modified = 0;
my $rspec = GeniXML::Parse($self->rspec());
if (! defined($rspec)) {
print STDERR "Could not parse rspec\n";
return -1;
}
foreach my $ref (GeniXML::FindNodes("n:node", $rspec)->get_nodelist()) {
my $diskref = GeniXML::GetDiskImage($ref);
next
if (!defined($diskref));
my $imageurn = GeniXML::GetText("name", $diskref);
next
if (!defined($imageurn));
if (exists($APTImages{$imageurn})) {
GeniXML::SetDiskImage($ref, $APTImages{$imageurn});
$modified = 1;
}
}
if ($modified) {
$self->{'DBROW'}->{'rspec'} = GeniXML::Serialize($rspec);
my $profileid = $self->profileid();
my $version = $self->version();
my $safe_rspec = DBQuoteSpecial($self->rspec());
DBQueryWarn("update apt_profile_versions set rspec=$safe_rspec ".
"where profileid='$profileid' and version='$version'")
if (1);
}
return 0;
}
# Total nonsense, to be thrown away.
sub CheckImageConstraints($$$)
{
my ($self, $iscloudlab, $pmsg) = @_;
my $cloudwww = "www.utah.cloudlab.us";
require URI;
my $rspec = GeniXML::Parse($self->rspec());
if (! defined($rspec)) {
print STDERR "Could not parse rspec\n";
return -1;
}
foreach my $ref (GeniXML::FindNodes("n:node", $rspec)->get_nodelist()) {
my $client_id = GetVirtualId($ref);
my $diskref = GeniXML::GetDiskImage($ref);
next
if (!defined($diskref));
my $image_url = GeniXML::GetText("url", $diskref);
next
if (!defined($image_url));
# Get the hostname for the image URL.
my $uri = URI->new($image_url);
if (!defined($uri)) {
print STDERR "Could not parse $image_url\n";
return -1;
}
my $image_host = $uri->host();
if ($iscloudlab) {
if ($image_host ne $cloudwww) {
$$pmsg = "The disk image specified for node '$client_id' ".
"will not run on the Cloudlab cluster";
return -1;
}
}
else {
if ($image_host eq $cloudwww) {
$$pmsg = "The disk image specified for node '$client_id' ".
"will not run on cluster you selected";
return -1;
}
}
}
return 0;
}
#
# Check blockstores.
#
sub CheckDatasets($$$)
{
my ($xml, $project, $pmsg) = @_;
my ($xml, $ppid, $pmsg) = @_;
my $pid = $ppid;
my $rspec = GeniXML::Parse($xml);
if (! defined($rspec)) {
print STDERR "CheckDatasets: Could not parse rspec\n";
......@@ -703,11 +826,28 @@ sub CheckDatasets($$$)
$ref,
$GeniXML::EMULAB_NS)->get_nodelist()) {
my $leaseurn = GeniXML::GetText("persistent", $blockref);
if (defined($leaseurn) && !GeniHRN::IsValid($leaseurn)) {
#
# We only care about datasets here, we let the backend
# do the error checking on ephemeral blockstores.
#
next
if (!defined($leaseurn));
if (!GeniHRN::IsValid($leaseurn)) {
$$pmsg = "Persistent dataset name is not a valid URN";
return 1;
}
my ($authority, $type, $id) = GeniXML::Parse($leaseurn);
my ($authority, $type, $id) = GeniHRN::Parse($leaseurn);
#
# Separate project from name; this is how the rspec specifies
# the dataset they want, since it might be in another project
#
if ($id =~ /^([-\w]+)\/\/(.+)$/) {
$pid = $1;
$id = $2;
}
#
# Not all backends have blockstore support.
#
......@@ -715,20 +855,33 @@ sub CheckDatasets($$$)
$$pmsg = "Persistent dataset is not on a valid aggregate";
return 1;
}
#
# Dataset must already exists on the aggregate.
#
my $pid = $project->pid();
my $dataset = APT_Dataset->Lookup("$pid/$id");
if (!defined($dataset)) {
$$pmsg = "Persistent dataset '$pid/$id' does not exist";
return 1;
}
my ($d_authority) = GeniXML::Parse($dataset->aggregate_urn());
#
# Dataset must already exists on the aggregate. But we have to
# make these checks again at instantiation, since the dataset
# might be gone, or it might have different permissions
# settings.
#
my ($d_authority) = GeniHRN::Parse($dataset->aggregate_urn());
if ($d_authority ne $authority) {
$$pmsg = "Persistent dataset '$pid/$id' in not on $authority";
return 1;
}
#
# Basic permission checks, very little support at the moment.
# Just make sure that the profile and the dataset are in the
# same pid.
#
if ($ppid ne $dataset->pid()) {
$$pmsg = "Not allowed to use dataset '$pid/$id' in profile";
return 1;
}
}
}
return 0;
......
......@@ -46,8 +46,7 @@ sub usage()
my $optlist = "dvu:a:t:f";
my $debug = 0;
my $verbose = 1;
my $utahddc = 1;
my $DDCURN = "urn:publicid:IDN+utahddc.geniracks.net+authority+cm";
my $DEFAULT_URN = "urn:publicid:IDN+utahddc.geniracks.net+authority+cm";
my $xmlfile;
my $webtask;
my $webtask_id;
......@@ -63,6 +62,7 @@ sub fatal($);
sub UserError($);
sub SnapShot($$$);
sub GenCredentials($$$$);
sub CreateDatasetCreds($$$$$);
#
# Configure variables
......@@ -96,6 +96,7 @@ use libaudit;
use APT_Profile;
use APT_Instance;
use APT_Geni;
use APT_Dataset;
use User;
use OSinfo;
use emutil;
......@@ -211,15 +212,8 @@ my $CMURN;
if (defined($aggregate)) {
$CMURN = $aggregate;
}
elsif ($utahddc) {
$CMURN = $DDCURN;
}
else {
my $cm_certificate = GeniCertificate->LoadFromFile($CMCERT);
if (!defined($cm_certificate)) {
fatal("Could not load certificate from $CMCERT\n");
}
$CMURN = $cm_certificate->urn();
$CMURN = $DEFAULT_URN;
}
my $cm_authority = GeniAuthority->Lookup($CMURN);
if (!defined($cm_authority)) {
......@@ -228,6 +222,8 @@ if (!defined($cm_authority)) {
fatal("Could not load CM authority object");
}
}
my $cmurl = $cm_authority->url();
#$cmurl =~ s/protogeni/protogeni\/stoller/;
#
# Must wrap the parser in eval since it exits on error.
......@@ -283,10 +279,43 @@ my $profile_object = APT_Profile->Lookup($value);
if (!defined($profile_object)) {
fatal("No such profile: $value");
}
#
# Temp hack; replace URNs with URLs.
#
if (1 && $profile_object->ConvertDiskImages()) {
fatal("Not able to convert disk_image URNs to URLs.");
}
my $rspecstr = $profile_object->CheckFirewall(!$localuser);
$profile = $profile_object->profileid();
$version = $profile_object->version();
#
# Look for datasets; need to verify that the datasets being referenced
# still exist and are still permissible to use, and we have to generate
# credentials for those datasets (if not a global dataset). The tricky
# aspect is that while a dataset and a profile have project permissions,
# the experiment has no project association, so if the profile/dataset
# perms are okay, then we send over a credential that tells the CM to
# allow this experiment to use that dataset in that project.
#
my $errmsg = "Bad dataset";
if (APT_Profile::CheckDatasets($rspecstr, $profile_object->pid(), \$errmsg)) {
UserError($errmsg);
}
#
# A temporary hack to make sure that the user does not try to run
# an x386 image on the Cloudlab cluster (ARMs). This will eventually
# get replaced with Jon's constraint checking code.
#
my $iscloudlab =
($CMURN eq "urn:publicid:IDN+utah.cloudlab.us+authority+cm" ? 1 : 0);
if ($profile_object->CheckImageConstraints($iscloudlab, \$errmsg)) {
UserError($errmsg);
}
fatal("fooey");
#
# Use ssh-keygen to see if the key is valid and convertable. We first
# try to get the fingerprint, which will tells us if its already in
......@@ -470,6 +499,15 @@ if ($geniuser->GetKeyBundle(\@sshkeys, 1) < 0 || !@sshkeys) {
fatal("No ssh keys to use for $geniuser!");
}
# Generate the extra credentials that tells the backend this experiment
# can access the datasets.
my @dataset_credentials = ();
if (CreateDatasetCreds($rspecstr,
$profile_object->pid(), $geniuser,
\$errmsg, \@dataset_credentials)) {
fatal($errmsg);
}
#
# Now generate a slice registration and credential
#
......@@ -583,7 +621,7 @@ $webtask->SetProcessID($PID)
# This creates the sliver and starts it.
#
my $response =
Genixmlrpc::CallMethod($cm_authority->url(), undef,
Genixmlrpc::CallMethod($cmurl, undef,
"CreateSliver",
{ "slice_urn" => $slice_urn,
"rspec" => $rspecstr,
......@@ -593,7 +631,9 @@ my $response =
'keys' => \@sshkeys }],
"credentials" =>
[$slice_credential->asString(),
$speaksfor_credential->asString()]});
$speaksfor_credential->asString(),
@dataset_credentials
]});
if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) {
$slice->Delete();
......@@ -638,7 +678,7 @@ while ($seconds > 0) {
$seconds -= $interval;
my $response =
Genixmlrpc::CallMethod($cm_authority->url(), undef,
Genixmlrpc::CallMethod($cmurl, undef,
"SliverStatus",
{ "slice_urn" => $slice_urn,
"credentials" =>
......@@ -698,6 +738,65 @@ else {
$slice->UnLock();
exit(0);
#
# Create credentials to access datasets.
#
sub CreateDatasetCreds($$$$$)
{
my ($xml, $pid, $user, $pmsg, $pref) = @_;
my @credentials = ();
my $rspec = GeniXML::Parse($xml);
if (! defined($rspec)) {
print STDERR "CreateDatasetCreds: Could not parse rspec\n";
return -1;
}
foreach my $ref (GeniXML::FindNodes("n:node", $rspec)->get_nodelist()) {
foreach my $blockref (GeniXML::FindNodesNS("n:blockstore",
$ref,
$GeniXML::EMULAB_NS)->get_nodelist()) {
my $leaseurn = GeniXML::GetText("persistent", $blockref);
#
# We only care about datasets here, we let the backend
# do the error checking on ephemeral blockstores.
#
next
if (!defined($leaseurn));
my ($authority, $type, $id) = GeniHRN::Parse($leaseurn);
#
# Separate project from name; this is how the rspec specifies
# the dataset they want, since it might be in another project
#
if ($id =~ /^([-\w]+)\/\/(.+)$/) {
$pid = $1;
$id = $2;
}
my $dataset = APT_Dataset->Lookup("$pid/$id");
if (!defined($dataset)) {
$$pmsg = "Persistent dataset '$pid/$id' does not exist";
return 1;
}
my $certificate = $dataset->GetCertificate();
if (!defined($certificate)) {
$$pmsg = "No certificate for dataset '$pid/$id'";
return -1;
}
my $credential =
APT_Geni::GenCredentials($certificate, $geniuser,
["blockstores"]);
if (!defined($credential)) {
$$pmsg = "Could not create credential for dataset '$pid/$id'";
return -1;
}
push(@credentials, $credential->asString());
}
}
@$pref = @credentials;
return 0;
}
sub fatal($) {
my ($mesg) = $_[0];
......
......@@ -157,7 +157,7 @@ sub DoCreate()
my $type = "stdataset";
my $fstype;
my $optlist = "ds:t:e:f:";
my $optlist = "ds:t:e:f:w:";
my %options = ();
if (! getopts($optlist, \%options)) {
&$usage();
......@@ -387,7 +387,12 @@ sub DoRefreshInternal($$)
my $response = $dataset->DescribeDataset();
if ($response->code() != GENIRESPONSE_SUCCESS) {
$$pmesg = "DescribeDataset failed: ". $response->output() . "\n";
if ($response->code() != GENIRESPONSE_SEARCHFAILED) {
$$pmesg = "Dataset no longer exists at the target\n";
}
else {
$$pmesg = "DescribeDataset failed: ". $response->output() . "\n";
}
return -1;
}
my $blob = $response->value();
......
......@@ -159,7 +159,8 @@ sub DoSnapshot()
if (!defined($slice)) {
fatal("No slice for quick VM: $uuid");
}
my $authority = $instance->GetGeniAuthority();
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
......@@ -171,9 +172,9 @@ sub DoSnapshot()
#
# If we get an imagename on the command line, the caller is
# saying it is responsible. If we do not get one, we create
# the name and update the underlying profile with the new image
# urn.
# saying it is responsible (clone). If we do not get one, we
# create the name and update the underlying profile with the new
# image urn.
#
my $imagename;
my $node_id;
......@@ -202,16 +203,17 @@ sub DoSnapshot()
if (! defined($manifest)) {
fatal("Could not parse manifest");
}
my $node;
my @nodes = GeniXML::FindNodes("n:node", $manifest)->get_nodelist();
if (!defined($node_id)) {
if (@nodes != 1) {
fatal("Too many nodes (> 1) to snapshot");
}
my ($node) = @nodes;
($node) = @nodes;
$sliver_urn = GeniXML::GetSliverId($node);
}
else {
foreach my $node (@nodes) {
foreach $node (@nodes) {
my $client_id<