Commit cd4a12ff authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint profile versioning.

parent 547a94e3
......@@ -35,6 +35,7 @@ use vars qw(@ISA @EXPORT $AUTOLOAD);
use EmulabConstants;
use emdb;
use libtestbed;
use APT_Profile;
use English;
use Data::Dumper;
use overload ('""' => 'Stringify');
......@@ -275,5 +276,16 @@ sub SetManifest($$)
}
#
# Find the profile for this instance.
#
sub Profile($)
{
my ($self) = @_;
return APT_Profile->Lookup($self->profile_id(),
$self->profile_version());
}
# _Always_ make sure that this 1 is at the end of the file...
1;
This diff is collapsed.
......@@ -28,7 +28,7 @@ use XML::Simple;
use Data::Dumper;
use CGI;
use POSIX ":sys_wait_h";
use POSIX qw(setsid);
use POSIX qw(setsid close);
#
# Back-end script to manage APT profiles.
......@@ -139,7 +139,7 @@ sub DoSnapshot()
my $update_profile = 0;
my $rspec;
my $profile = APT_Profile->Lookup($instance->profile_idx());
my $profile = APT_Profile->Lookup($instance->profile_id());
if (!defined($profile)) {
fatal("Could not lookup profile for instance");
}
......@@ -243,6 +243,10 @@ sub DoSnapshot()
}
# Let parent exit;
sleep(2);
# Close our descriptors so web server thinks we are disconnected.
for (my $i = 0; $i < 1024; $i++) {
POSIX:close($i);
}
POSIX::setsid();
}
my $seconds = 1200;
......
......@@ -35,17 +35,20 @@ use POSIX qw(setsid);
#
sub usage()
{
print("Usage: manage_profile [-u | -s uuid] <xmlfile>\n");
print("Usage: manage_profile -r profile\n");
print("Usage: manage_profile [-u uuid | -s uuid] <xmlfile>\n");
print("Usage: manage_profile [-r | -p] profile\n");
exit(-1);
}
my $optlist = "durs:t:";
my $optlist = "du:rs:t:fp";
my $debug = 0;
my $verify = 0; # Check data and return status only.
my $update = 0;
my $snap = 0;
my $delete = 0;
my $skipadmin = 0;
my $snapuuid;
my $force = 0; # With delete.
my $uuid;
my $profile;
my $instance;
my $webtask;
my $webtask_id;
......@@ -86,8 +89,9 @@ use libaudit;
# Protos
sub fatal($);
sub UserError();
sub UserError(;$);
sub DeleteProfile($);
sub PublishProfile($);
#
# Parse command arguments. Once we return from getopts, all that should be
......@@ -100,14 +104,19 @@ if (! getopts($optlist, \%options)) {
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"f"})) {
$force = 1;
}
if (defined($options{"v"})) {
$verify = 1;
}
if (defined($options{"u"})) {
$update = 1;
$uuid = $options{"u"};
}
if (defined($options{"s"})) {
$snapuuid = $options{"s"};
$snap = 1;
$uuid = $options{"s"};
}
if (defined($options{"t"})) {
$webtask_id = $options{"t"};
......@@ -128,6 +137,10 @@ if (! defined($this_user)) {
if (defined($options{"r"})) {
exit(DeleteProfile($ARGV[0]));
}
elsif (defined($options{"p"})) {
exit(PublishProfile($ARGV[0]));
}
my $xmlfile = shift(@ARGV);
#
......@@ -255,10 +268,10 @@ elsif (!$project->AccessCheck($this_user, TB_PROJECT_MAKEIMAGEID())) {
# Are we going to snapshot a node in an experiment? If so we
# sanity check to make sure there is just one node.
#
if (defined($snapuuid)) {
$instance = APT_Instance->Lookup($snapuuid);
if ($snap) {
$instance = APT_Instance->Lookup($uuid);
if (!defined($instance)) {
fatal("Could not look up instance $snapuuid");
fatal("Could not look up instance $uuid");
}
my $manifest = GeniXML::Parse($instance->manifest());
if (! defined($manifest)) {
......@@ -270,30 +283,45 @@ if (defined($snapuuid)) {
UserError();
}
}
my $profile = APT_Profile->Lookup($new_args{"pid"}, $new_args{"name"});
if ($update) {
$profile = APT_Profile->Lookup($uuid);
if (!defined($profile)) {
$errors{"profile_name"} = "No such profile exists";
UserError();
fatal("Could not lookup profile for update $uuid");
}
# Kill the description.. No longer used.
$update_args{"description"} = "";
$profile->Update(\%update_args) == 0 or
delete($update_args{"description"});
#
# If the rspec changed, then make a new version of the profile.
# Everything else is metadata.
#
if (exists($update_args{"rspec"})) {
if ($update_args{"rspec"} ne $profile->rspec()) {
$profile = $profile->NewVersion($this_user);
if (!defined($profile)) {
fatal("Could not create new version of the profile");
}
$profile->UpdateVersion({"rspec" => $update_args{"rspec"}});
}
delete($update_args{"rspec"});
}
$profile->UpdateMetaData(\%update_args) == 0 or
fatal("Could not update profile record");
# Bump the modtime.
$profile->MarkModified();
}
else {
my $usererror;
$profile = APT_Profile->Lookup($new_args{"pid"}, $new_args{"name"});
if (defined($profile)) {
$errors{"profile_name"} = "Already in use";
UserError();
}
$profile =
APT_Profile->Create($project, $this_user, \%new_args, \$usererror);
APT_Profile->Create($instance, $project,
$this_user, \%new_args, \$usererror);
if (!defined($profile)) {
if (defined($usererror)) {
$errors{"profile_name"} = $usererror;
......@@ -322,12 +350,12 @@ if (defined($instance)) {
#
$webtask = WebTask->Create($profile->uuid(), $webtask_id);
if (!defined($webtask)) {
$profile->Delete();
$profile->Delete(1);
}
$webtask->AutoStore(1);
if ($profile->Lock()) {
$profile->Delete();
$profile->Delete(1);
fatal("Could not lock new profile");
}
......@@ -341,7 +369,7 @@ if (defined($instance)) {
#
my $output = emutil::ExecQuiet($command);
if ($?) {
$profile->Delete();
$profile->Delete(1);
$webtask->Delete();
fatal("Failed to create disk image!");
}
......@@ -354,19 +382,13 @@ if (defined($instance)) {
$image_urn = $1;
}
else {
$profile->Delete();
$profile->Delete(1);
fatal("Could not find image urn in:\n$output");
}
my $rspec = GeniXML::Parse($profile->rspec());
if (! defined($rspec)) {
$profile->Delete();
fatal("Could not parse rspec");
}
($node) = GeniXML::FindNodes("n:node", $rspec)->get_nodelist();
GeniXML::SetDiskImage($node, $image_urn);
if ($profile->Update({"rspec" => GeniXML::Serialize($rspec)})) {
$profile->Delete();
fatal("Could not update rspec");
# Again, this only makes sense for single node profiles.
if ($profile->UpdateDiskImage($image_urn)) {
$profile->Delete(1);
fatal("Could not update image URN in rspec");
}
#
......@@ -397,7 +419,7 @@ if (defined($instance)) {
if (defined($webtask->exited()));
}
if ($webtask->exitcode()) {
$profile->Delete();
$profile->Delete(1);
exit(1);
}
$profile->Unlock();
......@@ -420,8 +442,13 @@ sub fatal($)
# relies on using the same name attributes for the errors, as for
# the incoming values.
#
sub UserError()
sub UserError(;$)
{
my ($msg) = @_;
if (defined($msg)) {
$errors{"error"} = $msg;
}
if (keys(%errors)) {
print "<errors>\n";
foreach my $key (keys(%errors)) {
......@@ -452,7 +479,44 @@ sub DeleteProfile($)
if (!defined($profile)) {
fatal("No such profile exists");
}
$profile->Delete() == 0 or
fatal("Could not delete profile");
#
# Not allowed to delete a published profile, yet. Needs thought.
#
if (defined($profile->published())) {
UserError("Not allowed to delete a published profile");
}
if (!$profile->IsHead()) {
UserError("Only allowed to delete the most recent profile");
}
#
# Version zero is special of course.
#
if ($profile->version()) {
$profile->DeleteVersion(0) == 0 or
fatal("Could not delete profile version");
}
else {
# Purge it. At some point we want to save them.
$profile->Delete(1) == 0 or
fatal("Could not delete profile");
}
return 0;
}
#
# Publish a profile.
#
sub PublishProfile($)
{
my ($name) = @_;
my $profile = APT_Profile->Lookup($name);
if (!defined($profile)) {
fatal("No such profile exists");
}
if (!$profile->IsHead()) {
UserError("Only allowed to publish the most recent profile");
}
$profile->Publish() == 0 or
fatal("Could not publish profile");
return 0;
}
......@@ -86,6 +86,8 @@ sub InitOpenflowAttributes($$$$)
if ($query_result->numrows);
$ofcontroller = ""
if (!$ofenabled || !defined($ofcontroller));
return 1
if (!$ofenabled);
#
# Firstly check if the attribuets are there, if no values,
......
......@@ -297,7 +297,8 @@ foreach my $key ("username", "email", "profile") {
#
# Gather up args and sanity check.
#
my ($value, $user_urn, $user_uid, $user_hrn, $user_email, $sshkey, $profile);
my ($value, $user_urn, $user_uid, $user_hrn, $user_email,
$sshkey, $profile, $version);
#
# Username and email has to be acceptable to Emulab user system.
......@@ -328,7 +329,8 @@ if (!defined($profile_object)) {
fatal("No such profile: $value");
}
my $rspecstr = $profile_object->CheckFirewall(!$localuser);
$profile = $profile_object->idx();
$profile = $profile_object->profileid();
$version = $profile_object->version();
#
# Use ssh-keygen to see if the key is valid and convertable. We first
......@@ -540,7 +542,8 @@ if (!defined($quickvm_uuid)) {
fatal("Could not generate a new uuid");
}
my $instance = APT_Instance->Create({'uuid' => $quickvm_uuid,
'profile_idx' => $profile,
'profile_id' => $profile,
'profile_version' => $version,
'slice_uuid' => $slice_uuid,
'creator' => $geniuser->uid(),
'creator_idx' => $geniuser->idx(),
......@@ -888,6 +891,10 @@ sub SnapShot($$$)
{
my ($uuid, $sliver_urn, $imagename) = @_;
my $this_user = User->ThisUser();
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
my $instance = APT_Instance->Lookup($uuid);
if (! defined($instance)) {
fatal("No such quick VM: $uuid");
......@@ -896,6 +903,10 @@ sub SnapShot($$$)
if ($old_status ne "ready") {
fatal("Instance must be in the ready state to take a snapshot");
}
my $profile = $instance->Profile();
if (! defined($profile)) {
fatal("No profile for $instance");
}
if (defined($instance->aggregate_urn()) &&
$instance->aggregate_urn() ne $CMURN) {
$CMURN = $instance->aggregate_urn();
......@@ -976,19 +987,20 @@ sub SnapShot($$$)
fatal("CreateImage failed: ".
(defined($response) ? $response->output() : "") . "\n");
}
#
# Lets print the URN and URL to stdout for the caller to grab.
# Could be better.
#
my ($image_urn, $image_url) = @{ $response->value() };
my ($image_urn, $image_url,
$version_urn, $version_url) = @{ $response->value() };
if (!defined($version_urn)) {
$version_urn = $image_urn;
$version_url = $image_url
}
#
# We got a webtask ID, which means we want to create a web task
# structure to track its progress.
#
if (defined($webtask)) {
$webtask->image_urn($image_urn);
$webtask->image_url($image_url);
$webtask->image_urn($version_urn);
$webtask->image_url($version_url);
$webtask->Store();
}
......@@ -1123,7 +1135,21 @@ sub SnapShot($$$)
}
else {
# Leave the slice locked if it failed; need to think about
# what we should do.
# what we should do.
#
# If successful, we create a new version of the profile and
# update the rspec to reflect the new image version. Note
# that we expect the CM is doing image versioning, so do not
# bother to check if the image version is actually new.
#
$profile = $profile->NewVersion($this_user);
if (!defined($profile)) {
print STDERR "Could not create new profile version\n";
$webtask->Exited(70)
if (defined($webtask));
exit(1);
}
$profile->UpdateDiskImage($image_urn);
$slice->UnLock();
$instance->SetStatus("ready");
# We garbage collect these later, so anyone waiting has a chance
......
......@@ -61,7 +61,8 @@ CREATE TABLE `active_checkups` (
DROP TABLE IF EXISTS `apt_instances`;
CREATE TABLE `apt_instances` (
`uuid` varchar(40) NOT NULL default '',
`profile_idx` int(10) unsigned NOT NULL default '0',
`profile_id` int(10) unsigned NOT NULL default '0',
`profile_version` int(10) unsigned NOT NULL default '0',
`slice_uuid` varchar(40) NOT NULL default '',
`creator` varchar(8) NOT NULL default '',
`creator_idx` mediumint(8) unsigned NOT NULL default '0',
......@@ -74,6 +75,31 @@ CREATE TABLE `apt_instances` (
PRIMARY KEY (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `apt_profile_versions`
--
CREATE TABLE `apt_profile_versions` (
`name` varchar(64) NOT NULL default '',
`profileid` int(10) unsigned NOT NULL default '0',
`version` int(8) unsigned NOT NULL default '0',
`pid` varchar(48) NOT NULL default '',
`pid_idx` mediumint(8) unsigned NOT NULL default '0',
`creator` varchar(8) NOT NULL default '',
`creator_idx` mediumint(8) unsigned NOT NULL default '0',
`created` datetime default NULL,
`published` datetime default NULL,
`deleted` datetime default NULL,
`uuid` varchar(40) NOT NULL,
`parent_profileid` int(8) unsigned default NULL,
`parent_version` int(8) unsigned default NULL,
`status` varchar(32) default NULL,
`rspec` mediumtext,
PRIMARY KEY (`profileid`,`version`),
UNIQUE KEY `pidname` (`pid_idx`,`name`,`version`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `apt_profiles`
--
......@@ -81,25 +107,17 @@ CREATE TABLE `apt_instances` (
DROP TABLE IF EXISTS `apt_profiles`;
CREATE TABLE `apt_profiles` (
`name` varchar(64) NOT NULL default '',
`idx` int(10) unsigned NOT NULL auto_increment,
`creator` varchar(8) NOT NULL default '',
`creator_idx` mediumint(8) unsigned NOT NULL default '0',
`profileid` int(10) unsigned NOT NULL default '0',
`version` int(8) unsigned NOT NULL default '0',
`pid` varchar(48) NOT NULL default '',
`pid_idx` mediumint(8) unsigned NOT NULL default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
`uuid` varchar(40) NOT NULL,
`public` tinyint(1) NOT NULL default '0',
`shared` tinyint(1) NOT NULL default '0',
`listed` tinyint(1) NOT NULL default '0',
`locked` datetime default NULL,
`status` varchar(32) default NULL,
`weburi` tinytext,
`description` mediumtext,
`rspec` mediumtext,
PRIMARY KEY (`idx`),
UNIQUE KEY `pidname` (`pid_idx`,`name`),
UNIQUE KEY `uuid` (`uuid`)
`locker_pid` int(11) default '0',
PRIMARY KEY (`profileid`),
UNIQUE KEY `pidname` (`pid_idx`,`name`,`version`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
......
#
# Add profile versioning.
#
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBSlotExists("apt_instances", "profile_id")) {
DBQueryFatal("alter table apt_instances change `profile_idx` ".
" `profile_id` int(10) unsigned NOT NULL default '0'");
}
if (!DBSlotExists("apt_instances", "profile_version")) {
DBQueryFatal("alter table apt_instances add ".
" `profile_version` int(10) unsigned NOT NULL ".
" default '0' after profile_id");
}
if (!DBTableExists("apt_profile_versions")) {
DBQueryFatal("CREATE TABLE `apt_profile_versions` ( ".
" `name` varchar(64) NOT NULL default '', ".
" `profileid` int(10) unsigned NOT NULL default 0, ".
" `version` int(8) unsigned NOT NULL default '0', ".
" `pid` varchar(48) NOT NULL default '', ".
" `pid_idx` mediumint(8) unsigned NOT NULL default '0', ".
" `creator` varchar(8) NOT NULL default '', ".
" `creator_idx` mediumint(8) ".
" unsigned NOT NULL default '0', ".
" `created` datetime default NULL, ".
" `published` datetime default NULL, ".
" `deleted` datetime default NULL, ".
" `uuid` varchar(40) NOT NULL, ".
" `parent_profileid` int(8) unsigned default NULL, ".
" `parent_version` int(8) unsigned default NULL, ".
" `status` varchar(32) default NULL, ".
" `rspec` mediumtext, ".
" PRIMARY KEY (`profileid`,`version`), ".
" UNIQUE KEY `pidname` (`pid_idx`,`name`,`version`), ".
" UNIQUE KEY `uuid` (`uuid`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
DBQueryFatal("insert into apt_profile_versions ".
"select name,idx,0,pid,pid_idx,creator,creator_idx, ".
" created,created,NULL,uuid,NULL,NULL,NULL,rspec ".
" from apt_profiles");
DBQueryFatal("rename table apt_profiles to apt_profiles_old");
}
if (!DBTableExists("apt_profiles")) {
DBQueryFatal("CREATE TABLE `apt_profiles` ( ".
" `name` varchar(64) NOT NULL default '', ".
" `profileid` int(10) unsigned NOT NULL default 0, ".
" `version` int(8) unsigned NOT NULL default '0', ".
" `pid` varchar(48) NOT NULL default '', ".
" `pid_idx` mediumint(8) unsigned NOT NULL default '0', ".
" `public` tinyint(1) NOT NULL default '0', ".
" `shared` tinyint(1) NOT NULL default '0', ".
" `listed` tinyint(1) NOT NULL default '0', ".
" `locked` datetime default NULL, ".
" `locker_pid` int(11) default '0', ".
" PRIMARY KEY (`profileid`), ".
" UNIQUE KEY `pidname` (`pid_idx`,`name`,`version`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
DBQueryFatal("insert into apt_profiles ".
"select name,idx,0,pid,pid_idx,public,shared,listed, ".
" NULL,0 ".
" from apt_profiles_old");
DBQueryFatal("replace into emulab_indicies ".
"select 'next_profile',max(profileid)+1 ".
" from apt_profiles");
}
return 0;
}
# Local Variables:
# mode:perl
# End:
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2013 University of Utah and the Flux Group.
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -367,6 +367,12 @@ sub AuditFork()
$libaudit::SAVE_STDERR = 0;
}
#
# We have to disconnect STDIN from the caller too.
#
open(STDIN, "< /dev/null") or
die("opening /dev/null for STDIN: $!");
#
# Create a new session to ensure we are clear of any process group.
#
......
......@@ -1786,6 +1786,10 @@ sub LoadVirtLans($)
$virtlan->_vpath(undef);
$virtlan->_bridged(0);
$virtlan->_wiretype("ethernet");
if ($vlanmember->ofenabled()) {
$virtlan->_ofenabled(1);
$virtlan->_ofcontroller($vlanmember->ofcontroller());
}
if (defined($encap) &&
($encap eq "vtun" || $encap eq "gre" || $encap eq "egre")) {
......@@ -8450,6 +8454,21 @@ sub UploadVlans($)
return -1;
}
$linkedlanid = $linkedlan->lanid();
#
# XXX This is a fix for emulated vlans; We need to insert the
# openflow attributes that exist for the emulated vlan,
# into the linked vlan. At some point we need to fix the
# interp code above, to not create an encapsulation vlan
# when the encap style is vlan (which is how links are done
# already).
#
if ($linkedlan->type() eq "vlan" && $virtlan->ofenabled()) {
$linkedlan->SetAttribute("ofenabled", "1");
$linkedlan->SetAttribute("ofcontroller",
$virtlan->ofcontroller());
$linkedlan->SetAttribute("oflistener", "");
}
}
DBQueryWarn("update vinterfaces set vlanid='$linkedlanid' ".
"where virtlanidx='$virtlanidx' and ".
......
......@@ -22,6 +22,12 @@
# }}}
#
#
$am_array = array('Utah DDC' =>
"urn:publicid:IDN+utahddc.geniracks.net+authority+cm",
'Utah PG' =>
"urn:publicid:IDN+emulab.net+authority+cm");
class Instance
{
var $instance;
......@@ -52,7 +58,8 @@ class Instance
function creator_idx() { return $this->field('creator_idx'); }
function creator_uuid() { return $this->field('creator_uuid'); }
function created() { return $this->field('created'); }
function profile_idx() { return $this->field('profile_idx'); }
function profile_id() { return $this->field('profile_id'); }
function profile_version() { return $this->field('profile_version'); }
function status() { return $this->field('status'); }
function manifest() { return $this->field('manifest'); }
......
......@@ -56,9 +56,7 @@ function Do_GetProfile()
# I like this ...
#
SPITAJAX_RESPONSE(array('rspec' => $profile->rspec(),
'name' => $profile->name(),
'idx' => $profile->idx(),
'description' => $profile->description()));
'name' => $profile->name()));
}
# Local Variables:
# mode:php
......
......@@ -43,6 +43,7 @@ $this_user = CheckLogin($check_status);
#
$optargs = OptionalPageArguments("create", PAGEARG_STRING,
"profile", PAGEARG_STRING,
"version", PAGEARG_INTEGER,
"stuffing", PAGEARG_STRING,
"verify", PAGEARG_STRING,
"project", PAGEARG_PROJECT,
......@@ -50,10 +51,6 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
$profile_default = "OneVM";
$profile_array = array();
$am_array = array('Utah DDC' =>
"urn:publicid:IDN+utahddc.geniracks.net+authority+cm",
'Utah PG' =>
"urn:publicid:IDN+emulab.net+authority+cm");
#
# if using the super secret URL, make sure the profile exists, and
......@@ -67,7 +64,7 @@ if (isset($profile)) {
# deal with the version at some point.
#
if (isset($project) && isset($profile)) {
$obj = Profile::LookupByName($project, $profile);
$obj = Profile::LookupByName($project, $profile, $version);
}
elseif ($this_user || IsValidUUID($profile)) {
$obj = Profile::Lookup($profile);
......@@ -105,7 +102,10 @@ if (isset($profile)) {
# users.
#
$query_result =
DBQueryFatal("select * from apt_profiles ".
DBQueryFatal("select * from apt_profiles as p ".
"left join apt_profile_versions as v on ".
" v.profileid=p.profileid and ".
" v.version=p.version ".
"where locked is null and (public=1 " .
($this_user ? "or creator_idx=" . $this_user->uid_idx() : "").
")");
......@@ -186,7 +186,10 @@ function SPITFORM($formfields, $newuser, $errors)
# If linked to a specific profile, description goes here
#
if ($profile) {
echo " <p>Fill out the form below to run an experiment using this profile:</p>\n";
if (!$this_user) {
echo " <p>Fill out the form below to run an experiment ".
"using this profile:</p>\n";
}
# Note: Following line is also duplicated below
echo " <blockquote><p><span id='selected_profile_description'></span></p></blockquote>\n";
echo " <p>When you click the &quot;Create&quot; button, the virtual or
......
......@@ -21,8 +21,6 @@ define(['underscore', 'js/quickvm_sup', 'filesize',
var callback = function(json) {
var value = json.value;
//console.log(json);
if (json.code) {
if (imaging_modal_active) {
sup.HideModal("#imaging-modal");
......
......@@ -7,19 +7,21 @@ require(window.APT_OPTIONS.configObject,
'js/lib/text!template/oops-modal.html',
'js/lib/text!template/rspectextview-modal.html',
'js/lib/text!template/guest-instantiate.html',
'js/lib/text!template/publish-modal.html',
'js/lib/text!template/instantiate-modal.html',
// jQuery modules
'filestyle','marked','jquery-ui','jquery-grid'],
function (_, sup, filesize, ShowImagingModal,
manageString, waitwaitString,
rendererString, showtopoString, oopsString, rspectextviewString,
guestInstantiateString)
guestInstantiateString, publishString, instantiateString)
{
'use strict';
var editing = 0;
var uuid = null;
var snapping= 0;
var gotrspec = 0;