Commit b8436171 authored by Leigh Stoller's avatar Leigh Stoller

Image Alias changes. Most of these changes are related to unifying

OSinfo and Image into a single object for the benefit of the perl
code. The database tables have not changed though.
parent 7aafa075
......@@ -118,7 +118,7 @@ use Experiment;
use User;
use Project;
use Group;
use OSinfo;
use Image;
use emutil;
use libEmulab;
use GeniDB;
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -67,7 +67,7 @@ use libdb;
use libtestbed;
use User;
use Project;
use Image;
use OSImage;
# Protos
sub fatal($);
......@@ -161,6 +161,7 @@ my %xmlfields =
"mtype_*" => ["mtype", $SLOT_OPTIONAL],
"hash", => ["hash", $SLOT_ADMINONLY],
"notes", => ["notes", $SLOT_ADMINONLY],
"architecture" => ["architecture", $SLOT_OPTIONAL],
);
#
# Need a list of node types. We join this over the nodes table so that
......@@ -319,8 +320,8 @@ UserError()
# Now do special checks.
#
my $image = Image->Lookup($editimageid_args{"imageid"},
$editimageid_args{"version"});
my $image = OSImage->Lookup($editimageid_args{"imageid"},
$editimageid_args{"version"});
if (!defined($image)) {
UserError("Image: No such image");
}
......@@ -369,6 +370,16 @@ if (!$isadmin && exists($editimageid_args{"path"})) {
}
}
if ($image->architecture() ||
(exists($editimageid_args{"architecture"}) &&
$editimageid_args{"architecture"} ne "")) {
foreach my $arch (split(",", $editimageid_args{"architecture"})) {
if (!exists($OSImage::IMAGE_ARCHITECTURES{$arch})) {
UserError("Architecture: Not a valid Architecture: $arch");
}
}
}
else {
#
# See what node types this image will work on. Must be at least one!
#
......@@ -446,7 +457,7 @@ if (defined($osidclause)) {
" and make the necessary changes!\n $msg");
}
}
}
exit(0)
if ($verify);
......@@ -459,8 +470,8 @@ exit(0)
delete($editimageid_args{"imageid"});
my $usrerr;
my $editimageid_val = Image->EditImageid($image,
\%editimageid_args, \$usrerr);
my $editimageid_val = OSImage->EditImageid($image,
\%editimageid_args, \$usrerr);
UserError($usrerr)
if (defined($usrerr));
fatal("Could not create new Image!")
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -68,7 +68,6 @@ use libdb;
use libtestbed;
use User;
use Project;
use OSinfo;
# Protos
sub fatal($);
......@@ -161,6 +160,8 @@ my %xmlfields =
# Class may only be changed while making a new class.
"class" => ["class", $SLOT_OPTIONAL],
"architecture" => ["architecture", $SLOT_OPTIONAL],
# Fixed attributes.
"isvirtnode" => ["isvirtnode", $SLOT_OPTIONAL],
......@@ -374,6 +375,12 @@ if (exists($editnodetype_args{"class"})) {
}
}
if (exists($editnodetype_args{"architecture"})) {
my $architecture = $editnodetype_args{"architecture"};
push(@nodetype_data, "architecture='$architecture'");
}
# The rest of them all have names starting with "is" at present.
my @fixed_args = grep(/^is/, keys(%editnodetype_args));
foreach my $name (@fixed_args) {
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -77,6 +77,7 @@ use libdb;
use libtestbed;
use User;
use Project;
use OSImage;
use Image;
use OSinfo;
use Node;
......@@ -223,6 +224,7 @@ my %xmlfields =
"lba_low", => ["lba_low", $SLOT_OPTIONAL],
"lba_high", => ["lba_high", $SLOT_OPTIONAL],
"lba_size", => ["lba_size", $SLOT_OPTIONAL],
"architecture", => ["architecture", $SLOT_OPTIONAL],
);
#
......@@ -420,7 +422,8 @@ if ($allpc) {
}
}
else {
$types_querystring = "select distinct n.type,nt.class from nodes as n ".
$types_querystring =
"select distinct n.type,nt.class,nt.architecture from nodes as n ".
"left join node_types as nt on n.type=nt.type ".
"left join node_type_attributes as a on a.type=n.type ".
"where a.attrkey='imageable' and ".
......@@ -433,9 +436,11 @@ my $types_result = DBQueryFatal($types_querystring);
# Save the valid types in a new array for later.
my %mtypes_array;
my %mtypes_arch;
if ($types_result->numrows) {
while (my ($type,$class) = $types_result->fetchrow_array()) {
while (my ($type,$class,$arch) = $types_result->fetchrow_array()) {
$mtypes_arch{$type} = $arch if (defined($arch));
$mtypes_array{$type} = $class;
$xmlfields{"mtype_$type"} = ["mtype", $SLOT_OPTIONAL];
}
......@@ -642,81 +647,67 @@ if (!$isdataset &&
#
# See what node types this image will work on. Must be at least one!
#
UserError("Node Types: Must have at least one node type")
UserError("Node Types: Must have at least one node type defined")
if (!$isdataset && !keys(%mtypes_array));
my $node_types_selected = 0;
# Check validity of mtype_* args, since the keys are dynamically generated.
my @mtype_keys = grep(/^mtype_/, keys(%newimageid_args));
foreach $key (@mtype_keys) {
my $value = $newimageid_args{$key};
print STDERR "mtype: '$key' -> '$value'\n"
if ($debug);
my $node_types_selected = 0;
my $type = $key;
$type =~ s/^mtype_//;
# Treat pcvm special for now.
if ($type eq "pcvm" ||
grep(/^${type}$/, keys(%mtypes_array))) {
$node_types_selected++
if ($value eq "1");
}
else {
$errors{$key} = "Illegal node type."
}
}
#
# When -a specified, add mappings for all pc types, does not matter if
# there are nodes of that type. Skip the stub pc/pc entry though.
# If we have architectures defined in the node_types table, and we got
# an architecture in the xml file for the image, we use those. Otherwise
# fall back to the old method.
#
if ($allpc) {
foreach my $type (keys(%mtypes_array)) {
my $class = $mtypes_array{$type};
next
if ($class ne "pc" || $type eq $class);
if (keys(%mtypes_arch) &&
(exists($newimageid_args{"architecture"}) &&
$newimageid_args{"architecture"} ne "")) {
$newimageid_args{"mtype_${type}"} = "1";
$node_types_selected++;
foreach my $arch (split(",", $newimageid_args{"architecture"})) {
if (!exists($OSImage::IMAGE_ARCHITECTURES{$arch})) {
UserError("Architecture: Not a valid Architecture: $arch");
}
}
}
else {
# Clear this since not using Architectures
delete($newimageid_args{"architecture"})
if (exists($newimageid_args{"architecture"}));
# Check validity of mtype_* args, since the keys are dynamically generated.
my @mtype_keys = grep(/^mtype_/, keys(%newimageid_args));
foreach $key (@mtype_keys) {
my $value = $newimageid_args{$key};
print STDERR "mtype: '$key' -> '$value'\n"
if ($debug);
my $type = $key;
$type =~ s/^mtype_//;
# Treat pcvm special for now.
if ($type eq "pcvm" ||
grep(/^${type}$/, keys(%mtypes_array))) {
$node_types_selected++
if ($value eq "1");
}
else {
$errors{$key} = "Illegal node type."
}
}
#
# When -a specified, add mappings for all pc types, does not matter if
# there are nodes of that type. Skip the stub pc/pc entry though.
#
if ($allpc) {
foreach my $type (keys(%mtypes_array)) {
my $class = $mtypes_array{$type};
next
if ($class ne "pc" || $type eq $class);
UserError("Node Types: Must select at least one node type")
if ($node_types_selected == 0 && !($force || $isdataset));
#
# We perform a further check for non-admins. When a node to snapshot
# has been specified, we check the OSID of the appropriate partition
# and see which node types it is appropriate for, and further restrict
# the list as necessary. This prevents creation of custom images based on
# old OSes from being checked as runnable on newer HW where they do not
# stand a chance.
#
if (!($isadmin || $isdataset) && defined($node) && !$node->isvirtnode()) {
my $query_result =
DBQueryFatal("select oi.type from osidtoimageid as oi ".
"left join partitions as p on oi.osid=p.osid ".
"where p.node_id='$node_id' and p.partition=$loadpart");
if ($query_result->numrows != 0) {
my %otypes;
while (my ($ntype) = $query_result->fetchrow_array()) {
$otypes{$ntype} = 1;
$newimageid_args{"mtype_${type}"} = "1";
$node_types_selected++;
}
my @invalid_node_types;
foreach my $ntype (@mtype_keys) {
$ntype =~ s/^mtype_//;
if (!exists($otypes{$ntype})) {
push @invalid_node_types, $ntype;
}
}
if (@invalid_node_types) {
UserError("Node Types: Current image on $node_id".
" cannot run on the following node types: ".
join(' ', @invalid_node_types));
}
} else {
UserError("Partition: No image originally loaded in partition $loadpart on $node_id; this is probably not the partition you meant to save");
}
UserError("Node Types: Must select at least one node type")
if ($node_types_selected == 0 && !($force || $isdataset));
}
# XXX Allowable OS types, OS features, and OpModes need to be
......@@ -787,13 +778,6 @@ if (!$isdataset) {
fatal("Could not create new OSID!")
if (!defined($new_osinfo));
#
# Insert a submap entry.
#
if (defined($parentos)) {
$new_osinfo->SetRunsOnParent($parentos);
}
$newimageid_args{"path"} = $ipath;
$osid = $new_osinfo->osid();
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2011-2013 University of Utah and the Flux Group.
# Copyright (c) 2011-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -72,7 +72,6 @@ use libdb;
use libtestbed;
use User;
use Project;
use OSinfo;
use EmulabFeatures;
# Protos
......
......@@ -643,14 +643,14 @@ sub LoadEstimate($)
{
my ($blockstore) = @_;
my $bsname = $blockstore->vname();
require Image;
require OSImage;
if (!exists($blockstore->{'attributes'}->{"dataset"})) {
print STDERR "No dataset attribute for $bsname\n";
return -1;
}
my $dataset = $blockstore->{'attributes'}->{"dataset"};
my $image = Image->Lookup($dataset);
my $image = OSImage->Lookup($dataset);
if (!defined($image)) {
print STDERR "No image for dataset $dataset for $bsname\n";
return -1;
......
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2014, 2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -51,7 +51,7 @@ LIB_SCRIPTS = libdb.pm Node.pm libdb.py libadminctrl.pm Experiment.pm \
EmulabFeatures.pm Port.pm BlockstoreType.pm Blockstore.pm \
IPBuddyAlloc.pm IPBuddyWrapper.pm Lease.pm Quota.pm \
libTaintStates.pm WebSession.pm WebTask.pm Brand.pm \
Reservation.pm
Reservation.pm OSImage.pm
# Stuff installed on plastic.
USERSBINS = genelists.proxy dumperrorlog.proxy backup
......
......@@ -321,6 +321,20 @@ sub field($$) {
}
return undef;
}
sub fieldExists($$) {
my ($self, $name) = @_;
return 1
if (exists($self->{'IMAGE'}->{$name}));
return 0;
}
sub fieldSet($$$) {
my ($self, $name, $value) = @_;
$self->{'IMAGE'}->{$name} = $value;
return $value;
}
sub isImageAlias($) { return 0; }
# Break circular reference someplace to avoid exit errors.
sub DESTROY {
......@@ -484,33 +498,6 @@ sub LookupByAuthorityURN($$)
return Image->Lookup($imageid);
}
#
# Get a list of all running frisbee images.
# XXX if this is actually used, it will have to be fixed; DB no longer
# tracks running frisbee daemons.
#
sub ActiveImages($)
{
my ($class) = @_;
my @result = ();
my $query_result =
DBQueryWarn("select imageid,imageid_version from frisbee_blobs ".
"where frisbee_pid!=0");
return undef
if (!defined($query_result));
while (my ($imageid,$version) = $query_result->fetchrow_array()) {
my $image = Image->Lookup($imageid, $version);
if (!defined($image)) {
print STDERR "*** Could not find DB object for image $imageid\n";
return undef;
}
push(@result, $image);
}
return \@result;
}
#
# Return a list of all images of the given format for the given pid.
# If format is NULL, return all formats. If pid is NULL, return for all pids.
......@@ -620,6 +607,8 @@ sub Create($$$$$$$$)
my $isadmin = $creator->IsAdmin();
my $isdataset = (exists($argref->{"isdataset"}) ?
$argref->{"isdataset"} : 0);
my $architecture = $argref->{'architecture'}
if (defined($argref->{'architecture'}));
# We may ignore particular partN_osid's by deleting them.
my @arg_slots = grep(/^part[1-4]_osid$/, keys(%{$argref}));
......@@ -720,9 +709,13 @@ sub Create($$$$$$$$)
$bquery .= ",pid='$pid',pid_idx='$pid_idx'";
$bquery .= ",gid='$gid',gid_idx='$gid_idx'";
# image_versions include all the images stuff.
my $query = "insert into image_versions set $bquery, ".
join(",", map("$_='" . $argref->{$_} . "'", @arg_slots));
# except for this.
$bquery .= ",architecture='$architecture'"
if (defined($architecture));
$query .= ",creator='$uid',creator_idx='$uid_idx'";
$query .= ",uuid='$version_uuid'";
$query .= ",created=now()";
......@@ -766,7 +759,8 @@ sub Create($$$$$$$$)
# Create the osidtoimageid mapping. Admins have an option to do it or not.
my $makedefault = exists($argref->{"makedefault"}) &&
$argref->{"makedefault"} eq "1";
if (!$isdataset && (!$isadmin || $makedefault)) {
if (!$isdataset && !defined($architecture) && (!$isadmin || $makedefault)){
#
# Dig out the mtypes we want to turn on. The caller has already
# sanity checked them to make sure the types actually exist, and
......@@ -851,18 +845,9 @@ sub NewVersion($$$$)
#
# Grab the current type list.
#
$query_result =
DBQueryWarn("select distinct type from osidtoimageid ".
"where imageid='$osid'");
goto bad
if (!$query_result);
my @types = ();
while (my ($type) = $query_result->fetchrow_array()) {
push(@types, $type);
}
$typelist = join(",", @types);
my @typelist = $self->TypeList();
$typelist = join(",", @typelist)
if (@typelist);
#
# Update the type list in the image being cloned. Better to do this
......@@ -1116,9 +1101,9 @@ sub Release($)
sub EditImageid($$$$)
{
my ($class, $image, $argref, $usrerr_ref) = @_;
my %mods;
my $noreport;
require NodeType;
my $imageid = $image->imageid();
......@@ -1127,47 +1112,55 @@ sub EditImageid($$$$)
# (Others above already did their own updates.)
#
my %updates;
foreach my $col ("description", "path", "mbr_version", "hash", "notes") {
foreach my $col ("description", "path", "mbr_version", "hash",
"notes") {
# Copy args we want so that others can't get through.
if (exists($argref->{$col})) {
$updates{$col} = $mods{$col} = $argref->{$col};
}
}
# See mtype_$type args below.
#
# Need a list of node types. We join this over the nodes table so that
# we get a list of just the nodes that are currently in the testbed, not
# just in the node_types table.
#
my $types_result =
DBQueryWarn("select distinct n.type from nodes as n ".
"left join node_type_attributes as a on a.type=n.type ".
"where a.attrkey='imageable' and ".
" a.attrvalue!='0'");
my @mtypes_array;
my @map_updates;
my $redo_map = 0;
while (my ($type) = $types_result->fetchrow_array()) {
push(@mtypes_array, $type);
my @map_updates;
if (exists($argref->{"architecture"})) {
# This is in the images table, so done separately.
$mods{"architecture"} = $argref->{"architecture"};
}
#
# Special hack to allow pcvm type -- see SetupReload in os_setup
#
push @mtypes_array, "pcvm";
foreach my $type (@mtypes_array) {
# Remember when we get one of the mtype_$type args. These aren't DB
# columns to update, but instead control re-creating the rows in the
# osidtoimageid table for this imageid, below.
my $mtype = "mtype_$type";
if (exists($argref->{$mtype})) {
my $value = $argref->{$mtype};
##printf "argref->{$mtype} %s\n", $value;
$mods{$mtype} = $value;
if ($value eq "1") {
push(@map_updates, $type);
$redo_map = 1;
else {
# See mtype_$type args below.
#
# Need a list of node types. We join this over the nodes table so that
# we get a list of just the nodes that are currently in the testbed, not
# just in the node_types table.
#
my $types_result =
DBQueryWarn("select distinct n.type from nodes as n ".
"left join node_type_attributes as a on a.type=n.type ".
"where a.attrkey='imageable' and ".
" a.attrvalue!='0'");
my @mtypes_array;
while (my ($type) = $types_result->fetchrow_array()) {
push(@mtypes_array, $type);
}
#
# Special hack to allow pcvm type -- see SetupReload in os_setup
#
push @mtypes_array, "pcvm";
foreach my $type (@mtypes_array) {
# Remember when we get one of the mtype_$type args. These aren't DB
# columns to update, but instead control re-creating the rows in the
# osidtoimageid table for this imageid, below.
my $mtype = "mtype_$type";
if (exists($argref->{$mtype})) {
my $value = $argref->{$mtype};
##printf "argref->{$mtype} %s\n", $value;
$mods{$mtype} = $value;
if ($value eq "1") {
push(@map_updates, $type);
$redo_map = 1;
}
}
}
}
......@@ -1180,7 +1173,7 @@ sub EditImageid($$$$)
}
}
if (keys %updates || $redo_map) {
if (keys(%mods) || $redo_map) {
DBQueryWarn("lock tables images write, image_versions write, ".
" images as i write, image_versions as v write, ".
" os_info write, os_info_versions write, ".
......@@ -1193,6 +1186,24 @@ sub EditImageid($$$$)
return undef;
}
}
if (exists($argref->{"architecture"})) {
my $arch = $argref->{"architecture"};
if (!DBQueryWarn("update images set architecture='$arch' ".
"where imageid='$imageid'")) {
$$usrerr_ref = "DB: Error updating the images table";
DBQueryWarn("unlock tables");
return undef;
}
if (0 &&
NodeType->LookupArchitectureTypes($arch) &&
!DBQueryWarn("delete from osidtoimageid ".
"where imageid='$imageid'")) {
$$usrerr_ref = "DB: Error updating the osidtoimageid table";
DBQueryWarn("unlock tables");
return undef;
}
}
if ($redo_map) {
#
# Update the osidtoimageid table too.
......@@ -1221,7 +1232,7 @@ sub EditImageid($$$$)
}
}
}
if (keys %updates || $redo_map) {
if (keys(%mods) || $redo_map) {
DBQueryWarn("unlock tables");
}
......@@ -1929,12 +1940,13 @@ sub WaitLock($$)
}
#
# Get the type list.
# Get the type list.
#
sub TypeList($;$)
{
my ($self, $osinfo) = @_;
my @result = ();
my $query_result;
require NodeType;
#
......@@ -1949,15 +1961,32 @@ sub TypeList($;$)
}
}
return @result;
}
}
my $imageid = $self->imageid();
my $clause = (defined($osinfo) ?
"and osid='" . $osinfo->osid() . "'" : "");
#
# If there is an architecture set in the image, we use that to
# find the matching types in the node_types table. This overrides
# anything found in the osidtoimageid.
#
if ($self->architecture()) {
$query_result =
DBQueryWarn("select distinct node_types.type from images ".
# The image architecture could be a short list.
"inner join node_types on ".
" FIND_IN_SET(node_types.architecture,".
" images.architecture) ".
"where images.imageid='$imageid'");
}
else {
my $clause = (defined($osinfo) ?
"and osid='" . $osinfo->osid() . "'" : "");
my $query_result =
DBQueryWarn("select distinct type from osidtoimageid ".
"where imageid='$imageid' $clause");
$query_result =
DBQueryWarn("select distinct type from osidtoimageid ".
"where imageid='$imageid' $clause");
}
return undef
if (!defined($query_result));
......@@ -1966,6 +1995,17 @@ sub TypeList($;$)
push(@result, $typeinfo)
if (defined($typeinfo));
}
if ($self->architecture()) {
require OSinfo;
my $osinfo = OSinfo->Lookup($self->imageid());
if (defined($osinfo) && defined($osinfo->def_parentosid())) {
my $pcvm = NodeType->Lookup("pcvm");
if (defined($pcvm)) {
push(@result, $pcvm);
}
}
}
return @result;
}
......
......@@ -379,7 +379,7 @@ sub Create($$$$)
my ($class, $node_id, $experiment, $argref) = @_;
my ($control_iface,$virtnode_capacity,$adminmfs,$adminmfs_osid);
my ($priority, $osid, $osid_vers, $opmode, $state);
require OSinfo;
require OSImage;
require NodeType;
# Defaults. Leave these here to avoid startup costs of libdb.
......@@ -428,15 +428,15 @@ sub Create($$$$)
if (defined($typeinfo->default_osid())) {
$osid = $typeinfo->default_osid();
my $osinfo = OSinfo->Lookup($osid);
if (!defined($osinfo)) {
my $osimage = OSImage->Lookup($osid);
if (!defined($osimage)) {
print STDERR
"*** Could not find OSinfo object for $osid!\n";
"*** Could not find OSImage object for $osid!\n";
return undef;
}
$osid = $osinfo->osid();
$osid_vers = $osinfo->vers();
$opmode = $osinfo->op_mode();
$osid = $osimage->osid();
$osid_vers = $osimage->version();
$opmode = $osimage->op_mode();
}
}
else {
......@@ -446,18 +446,18 @@ sub Create($$$$)
}
# Find object for the adminfs.
if (defined($adminmfs_osid)) {
$adminmfs = OSinfo->Lookup($adminmfs_osid);
$adminmfs = OSImage->Lookup($adminmfs_osid);
}
else {
$adminmfs = OSinfo->Lookup(TBOPSPID(), $MFS_INITIAL);
$adminmfs = OSImage->Lookup(TBOPSPID(), $MFS_INITIAL);
}
if (!defined($adminmfs)) {
pr