diff --git a/apt/APT_Profile.pm.in b/apt/APT_Profile.pm.in index e6ebdf61f45cc3d8dad64c0e23136cfa1452c529..123c5d6999a68a638d4e57f2583b54c0a022445f 100644 --- a/apt/APT_Profile.pm.in +++ b/apt/APT_Profile.pm.in @@ -209,6 +209,8 @@ sub Create($$$$$) $query .= ",rspec=$rspec"; $query .= ",public=1" if (exists($argref->{'public'}) && $argref->{'public'}); + $query .= ",listed=1" + if (exists($argref->{'listed'}) && $argref->{'listed'}); if (! DBQueryWarn($query)) { DBQueryWarn("unlock tables"); @@ -272,5 +274,25 @@ sub Delete($) return 0; } +# +# Mark the update time. +# +sub MarkModified() +{ + my ($self) = @_; + + # Must be a real reference. + return -1 + if (! ref($self)); + + my $idx = $self->idx(); + + DBQueryWarn("update apt_profiles set modified=now() ". + "where idx='$idx'") + or return -1; + + return 0; +} + # _Always_ make sure that this 1 is at the end of the file... 1; diff --git a/apt/manage_profile.in b/apt/manage_profile.in index 980ec680c643fef43829c254c8440e72681da71b..3b613092b911308e997b06ce28b6e2a6d5a96001 100644 --- a/apt/manage_profile.in +++ b/apt/manage_profile.in @@ -129,6 +129,7 @@ my %xmlfields = "profile_pid" => ["pid", $SLOT_REQUIRED], "profile_creator" => ["creator", $SLOT_OPTIONAL], "profile_description" => ["description", $SLOT_REQUIRED|$SLOT_UPDATE], + "profile_listed" => ["listed", $SLOT_OPTIONAL|$SLOT_UPDATE], "profile_public" => ["public", $SLOT_OPTIONAL|$SLOT_UPDATE], "rspec" => ["rspec", $SLOT_REQUIRED|$SLOT_UPDATE], ); @@ -241,6 +242,8 @@ if ($update) { } $profile->Update(\%update_args) == 0 or fatal("Could not update profile record"); + # Bump the modtime. + $profile->MarkModified(); } else { my $usererror; diff --git a/protogeni/scripts/aptssh-setup.in b/protogeni/scripts/aptssh-setup.in index 0dea7cadcc2c7f30947256e8245df605faf79f37..e66865063efcffa04cfb17c02598e0edbc1576d8 100644 --- a/protogeni/scripts/aptssh-setup.in +++ b/protogeni/scripts/aptssh-setup.in @@ -1,6 +1,6 @@ #!/usr/bin/perl -wT # -# Copyright (c) 2000-2013 University of Utah and the Flux Group. +# Copyright (c) 2000-2014 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -62,6 +62,7 @@ my $user; use lib "@prefix@/lib"; use GeniDB; use GeniUser; +use emutil; # Connect to the SA DB. GeniDB::DBConnect(GeniDB::GENISA_DBNAME()); @@ -136,18 +137,14 @@ system("$SSH -host $CONTROL '/usr/sbin/chown -R nobody:nobody $dir'") == 0 or fatal("Could not mkdir $dir: $!"); system("$SSH -host $CONTROL '/bin/chmod -R 700 $dir'") == 0 or fatal("Could not mkdir $dir: $!"); - -system("/usr/bin/fsync $udir"); -system("/usr/bin/fsync $udir/id_rsa.pub"); -sleep(5); -$UID = $SAVEUID; - # Grab a copy for the DB. -my $pubkey = `cat $udir/id_rsa.pub`; +my $pubkey = + emutil::ExecQuiet("$SSH -host $CONTROL '/bin/cat $dir/id_dsa.pub'"); if ($?) { fatal("Could not read new key from file"); } chomp($pubkey); +$UID = $SAVEUID; # Only one. $geniuser->DeleteInternalKeys(); diff --git a/protogeni/scripts/quickvm.in b/protogeni/scripts/quickvm.in index 1148b34f4da2734cc59f263639fdab6037e01e60..943ad0f7b3d9a5f5601d141ba6e60a05e3a6f21b 100755 --- a/protogeni/scripts/quickvm.in +++ b/protogeni/scripts/quickvm.in @@ -503,6 +503,7 @@ my $response = if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) { $slice->Delete(); + SetQuickVMStatus($quickvm_uuid, "failed"); fatal("CreateSliver failed: ". (defined($response) ? $response->output() : "") . "\n"); } @@ -605,6 +606,7 @@ sub Terminate($) fatal("No such quick VM: $uuid"); } my $row = $query_result->fetchrow_hashref(); + my $status = $row->{"status"}; my $geniuser = GeniUser->Lookup($row->{'creator_uuid'}, 1); if (!defined($geniuser)) { @@ -612,6 +614,9 @@ sub Terminate($) } my $slice = GeniSlice->Lookup($row->{'slice_uuid'}); if (!defined($slice)) { + if ($status eq "failed") { + goto done; + } fatal("No slice for quick VM: $uuid"); } # Create a slice credential @@ -660,6 +665,7 @@ sub Terminate($) (defined($response) ? $response->output() : "") . "\n"); } $slice->Delete(); + done: DBQueryWarn("delete from quickvms where uuid=$safe_uuid"); exit(0); } diff --git a/sql/database-create.sql b/sql/database-create.sql index a8f036653dc1e581b779b32fcaaa0342ad9a2b4b..1c9ce8081253bbde9f9d5e786d98c5501f3fe3ab 100644 --- a/sql/database-create.sql +++ b/sql/database-create.sql @@ -67,8 +67,12 @@ CREATE TABLE `apt_profiles` ( `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', + `weburi` tinytext, `description` mediumtext, `rspec` mediumtext, PRIMARY KEY (`idx`), diff --git a/sql/database-fill.sql b/sql/database-fill.sql index 0b532cb1ca09878e20ac7abe0a3ca1a7800f8af7..9f420664040f67fe32c583317d3ea5039f5f1fdb 100644 --- a/sql/database-fill.sql +++ b/sql/database-fill.sql @@ -1273,6 +1273,8 @@ REPLACE INTO table_regex VALUES ('apt_profiles','pid','text','redirect','project REPLACE INTO table_regex VALUES ('apt_profiles','creator','text','redirect','users:uid',0,0,NULL); REPLACE INTO table_regex VALUES ('apt_profiles','name','text','redirect','images:imagename',0,64,NULL); REPLACE INTO table_regex VALUES ('apt_profiles','public','int','redirect','default:boolean',0,0,NULL); +REPLACE INTO table_regex VALUES ('apt_profiles','listed','int','redirect','default:boolean',0,0,NULL); +REPLACE INTO table_regex VALUES ('apt_profiles','shared','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('apt_profiles','description','text','redirect','default:html_fulltext',0,512,NULL); REPLACE INTO table_regex VALUES ('apt_profiles','rspec','text','redirect','default:html_fulltext',0,8192,NULL); diff --git a/sql/updates/4/380 b/sql/updates/4/380 new file mode 100644 index 0000000000000000000000000000000000000000..297fbb35907c8c7fd428f7c894b63233a1e0d841 --- /dev/null +++ b/sql/updates/4/380 @@ -0,0 +1,43 @@ +# +# Modify APT profiles table. +# +use strict; +use libdb; + +sub DoUpdate($$$) +{ + my ($dbhandle, $dbname, $version) = @_; + + if (!DBSlotExists("apt_profiles", "modified")) { + DBQueryFatal("alter table apt_profiles add ". + " `modified` datetime default NULL ". + " after created"); + } + if (!DBSlotExists("apt_profiles", "shared")) { + DBQueryFatal("alter table apt_profiles add ". + " `shared` tinyint(1) NOT NULL default '0' ". + " after public"); + } + if (!DBSlotExists("apt_profiles", "listed")) { + DBQueryFatal("alter table apt_profiles add ". + " `listed` tinyint(1) NOT NULL default '0' ". + " after shared"); + DBQueryFatal("update apt_profiles set listed=public"); + } + if (!DBSlotExists("apt_profiles", "weburi")) { + DBQueryFatal("alter table apt_profiles add ". + " `weburi` tinytext ". + " after listed"); + } + DBQueryFatal("REPLACE INTO table_regex VALUES ". + "('apt_profiles','listed','int','redirect',". + "'default:boolean',0,0,NULL)"); + DBQueryFatal("REPLACE INTO table_regex VALUES ". + "('apt_profiles','shared','int','redirect',". + "'default:boolean',0,0,NULL)"); + return 0; +} + +# Local Variables: +# mode:perl +# End: diff --git a/tbsetup/gentopofile.in b/tbsetup/gentopofile.in index cd118162c67cf72382e17650fb970a06d74d7216..84d2171d079f574de37dc4aebdb9a6967b238c9c 100644 --- a/tbsetup/gentopofile.in +++ b/tbsetup/gentopofile.in @@ -601,12 +601,16 @@ while (my ($sim) = $query_result->fetchrow_array) { # we need to convert port:ip above to lan:ip. # $query_result = - DBQueryFatal("select v.vname,member,mask,cost,vlink from virt_lans as v ". + DBQueryFatal("select v.vname,member,mask,cost,vlink,s.capkey ". + " from virt_lans as v ". + "left join virt_lan_settings as s on ". + " s.exptidx=v.exptidx and s.vname=v.vname and ". + " s.capkey='portvlan' ". "left join virt_bridges as b on b.vname=v.bridge_vname and ". " b.pid=v.pid and b.eid=v.eid ". "where v.pid='$pid' and v.eid='$eid' "); -while (my ($vname,$member,$mask,$cost,$bridgelink) = +while (my ($vname,$member,$mask,$cost,$bridgelink,$lantype) = $query_result->fetchrow_array()) { my ($node,$port) = split(":", $member); @@ -620,7 +624,12 @@ while (my ($vname,$member,$mask,$cost,$bridgelink) = $lans{$vname} = {}; $lans{$vname}->{"mask"} = $mask; $lans{$vname}->{"cost"} = $cost; + $lans{$vname}->{"noroute"} = 0; $lans{$vname}->{"members"} = {}; + + if (defined($lantype)) { + $lans{$vname}->{"noroute"} = 1; + } } # Store lan:ip into the portlist for the node. @@ -664,8 +673,9 @@ if ($experiment->geniflags()) { last; } $lans{$vname} = {}; - $lans{$vname}->{"mask"} = $mask; - $lans{$vname}->{"cost"} = 1; + $lans{$vname}->{"mask"} = $mask; + $lans{$vname}->{"cost"} = 1; + $lans{$vname}->{"noroute"} = 1; } if ($member->GetAttribute("tunnel_ip", \$ip)) { print STDERR "Could not get ip for $member\n"; @@ -730,12 +740,14 @@ foreach my $node (sort keys(%nodes)) { # # Then spit out the lans. As above, ALWAYS print the header. # -print $OUT "# lans: vname,mask,cost\n"; +print $OUT "# lans: vname,mask,cost,noroute\n"; foreach my $lan (sort keys(%lans)) { - my $cost = $lans{$lan}->{"cost"}; - my $mask = $lans{$lan}->{"mask"}; - print $OUT "$lan,$mask,$cost\n"; + my $cost = $lans{$lan}->{"cost"}; + my $mask = $lans{$lan}->{"mask"}; + my $noroute = $lans{$lan}->{"noroute"}; + + print $OUT "$lan,$mask,$cost,$noroute\n"; } # diff --git a/tbsetup/libaudit.pm.in b/tbsetup/libaudit.pm.in index c9df676b47bb7391b8fd8089ce89585d67df5184..b8e3caac859cd77acb3a1ec355f575228c27c05c 100644 --- a/tbsetup/libaudit.pm.in +++ b/tbsetup/libaudit.pm.in @@ -30,7 +30,7 @@ use Exporter; @EXPORT = qw ( AuditStart AuditEnd AuditAbort AuditFork AuditSetARGV AuditGetARGV AddAuditInfo - LogStart LogEnd LogAbort + LogStart LogEnd LogAbort AuditDisconnect LIBAUDIT_NODAEMON LIBAUDIT_DAEMON LIBAUDIT_LOGONLY LIBAUDIT_NODELETE LIBAUDIT_FANCY LIBAUDIT_LOGTBOPS LIBAUDIT_LOGTBLOGS ); @@ -285,6 +285,20 @@ sub AuditGetARGV() return @SAVEARGV; } +sub AuditDisconnect() +{ + if ($auditing) { + if (!$daemon && $PERL_VERSION >= 5.008 && $libaudit::SAVE_STDOUT) { + close($libaudit::SAVE_STDOUT); + close($libaudit::SAVE_STDERR); + + open(FOO, "> /dev/null"); + $libaudit::SAVE_STDOUT = *FOO; + $libaudit::SAVE_STDERR = *FOO; + } + } +} + # # Abort an Audit. Dump the log file and do not send email. # @@ -293,7 +307,7 @@ sub AuditAbort() if ($auditing) { $auditing = 0; - if (!$daemon && $PERL_VERSION >= 5.008) { + if (!$daemon && $PERL_VERSION >= 5.008 && $libaudit::SAVE_STDOUT) { eval("open(STDOUT, \">&\", \$libaudit::SAVE_STDOUT); ". "open(STDERR, \">&\", \$libaudit::SAVE_STDERR);"); } diff --git a/utils/grantnodetype.in b/utils/grantnodetype.in index efa53f2623e92db5b5b76abf03d7a48ca9f22c53..e61f9dbf4354c8c041206dfbfe845336741214c3 100644 --- a/utils/grantnodetype.in +++ b/utils/grantnodetype.in @@ -1,7 +1,7 @@ #!/usr/bin/perl -wT # -# Copyright (c) 2003-2012 University of Utah and the Flux Group. +# Copyright (c) 2003-2012, 2014 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -35,16 +35,19 @@ use Getopt::Std; sub usage() { print STDERR "Usage: grantnodetype [-h] [-r] [-m] -p <pid> <type> [count]\n"; + print STDERR " grantnodetype [-h] -R <type>\n"; print STDERR " -h This message\n"; print STDERR " -r Revoke access instead of grant\n"; print STDERR " -m Force insert of missing minus policy\n"; + print STDERR " -R Remove all projects for type, and minus policy\n"; print STDERR " count Overdide default count of 999999\n"; exit(-1); } -my $optlist = "hp:dnrm"; +my $optlist = "hp:dnrmR"; my $impotent = 0; my $debug = 0; my $revoke = 0; +my $revokeall= 0; my $addminus = 0; my %newtypes = (); my $pid; @@ -106,13 +109,17 @@ if (defined($options{n})) { if (defined($options{r})) { $revoke = 1; } +if (defined($options{R})) { + $revokeall = 1; +} if (defined($options{d})) { $debug = 1; } if (defined($options{p})) { $pid = $options{p}; } -if (scalar(@ARGV) < 1 || scalar(@ARGV) > 2 || !defined($pid)) { +if (!$revokeall && + (scalar(@ARGV) < 1 || scalar(@ARGV) > 2 || !defined($pid))) { usage(); } my $type = $ARGV[0]; @@ -128,6 +135,19 @@ else { die("Tainted type name: $type"); } +# +# Clear the type from the group_policies table, including the minus policy, +# which allows the type to be used by everyone. +# +if ($revokeall) { + DBQueryFatal("delete from group_policies ". + "where policy='type' and auxdata='$type'") + if (!$impotent); + + system($update_perms); + exit($? >> 8); +} + if ($pid =~ /^([-\w]+)$/) { $pid = $1; } diff --git a/www/aptui/js/manage_profile.js b/www/aptui/js/manage_profile.js index e1b2ca5169ddfd1a15afae03178733b825ecad9c..eae2826d59eba5d1728938eccc7a6ebf4c8e7572 100644 --- a/www/aptui/js/manage_profile.js +++ b/www/aptui/js/manage_profile.js @@ -43,9 +43,10 @@ function ($, sup) console.info(topo); sup.ShowModal("#quickvm_topomodal"); - - sup.maketopmap("#showtopo_div", - ($("#showtopo_dialog").outerWidth()) - 90, + + // Subtract -2 cause of the border. + sup.maketopmap("#showtopo_nopicker", + ($("#showtopo_nopicker").outerWidth() - 2), 300, topo); } diff --git a/www/aptui/js/myprofiles.js b/www/aptui/js/myprofiles.js index 3cb631c203147f24e0ad26aabfc390d05bc6ad94..c42565d7bbdd50abc54b0ffcaba0a77c0198989c 100644 --- a/www/aptui/js/myprofiles.js +++ b/www/aptui/js/myprofiles.js @@ -16,7 +16,6 @@ function ($, sup) $('#quickvm_topomodal').on('hidden.bs.modal', function() { sup.ShowProfileList($('.current')) }); - $('button#profile').click(function (event) { event.preventDefault(); sup.ShowModal('#quickvm_topomodal'); @@ -30,6 +29,12 @@ function ($, sup) sup.UpdateProfileSelection($('.selected')); sup.HideModal('#quickvm_topomodal'); }); + // We have to get the selected profile from the hidden form variable. + $('a#instantiate').click(function (event) { + event.preventDefault(); + var profile = $('#selected_profile').attr('value'); + window.location.replace("quickvm.php?profile=" + profile); + }); } $(document).ready(initialize); diff --git a/www/aptui/js/quickvm_sup.js b/www/aptui/js/quickvm_sup.js index 04c0a7226ee08e17ee9450aa0c72b221ff7c84ca..8972e29dd7b6600f68f16dc84e69bd20504bce9e 100755 --- a/www/aptui/js/quickvm_sup.js +++ b/www/aptui/js/quickvm_sup.js @@ -130,8 +130,9 @@ function ShowTopo(uuid) console.info(topo); $("#showtopo_container").removeClass("invisible"); + // Subtract -2 cause of the border. maketopmap("#showtopo_container", - $("#showtopo_div").width() - 30, + $("#showtopo_statuspage").outerWidth() - 2, 300, topo); } @@ -568,6 +569,10 @@ function StartCountdownClock(when) // Clock reset if (StartCountdownClock.reset != when) { when = StartCountdownClock.reset; + if (when === "n/a") { + StartCountdownClock.stop = 1; + return; + } // Reformat in local time and show the user. var local_date = new Date(when); @@ -818,11 +823,22 @@ function ConvertManifestToJSON(name, xml) "nodes": [], "links": [], }; + var interfaces = new Array(); + var count = 0; $(xml).find("node").each(function(){ var client_id = $(this).attr("client_id"); var jobj = {"name" : client_id}; + $(this).find("interface").each(function() { + var interface_id = $(this).attr("client_id"); + var interface = new Object(); + interface.client_id = interface_id; + interface.node_id = client_id; + interface.node_index = count; + interfaces.push(interface); + }); + var login = $(this).find("login"); if (login) { var user = login.attr("username"); @@ -834,18 +850,22 @@ function ConvertManifestToJSON(name, xml) jobj.hostport = host + ":" + port; jobj.sshurl = sshurl; } - json.nodes.push(jobj); + json.nodes[count] = jobj; + count++; }); $(xml).find("link").each(function(){ var client_id = $(this).attr("client_id"); var link_type = $(this).find("link_type"); + var ifacerefs = $(this).find("interface_ref"); - if (link_type && $(link_type).attr("name") == "lan") { - console.info("Oops, a lan"); + if (ifacerefs.length < 2) { + console.info("Oops, not enough interfaces in " + client_id); + } + else if (ifacerefs.length > 2) { + console.info("Oops, too many interfaces in " + client_id); } else { - var ifacerefs = $(this).find("interface_ref"); var source = ifacerefs[0]; var target = ifacerefs[1]; @@ -854,20 +874,26 @@ function ConvertManifestToJSON(name, xml) var source_ifname = source.attr("client_id"); var target_ifname = target.attr("client_id"); - var source_ifpair = source_ifname.split(":"); - var target_ifpair = target_ifname.split(":"); - var source_name = source_ifpair[0]; - var target_name = target_ifpair[0]; + var source_name = null; + var target_name = null; var source_index = null; var target_index = null; - - // Javascript does not do dictionaries. Too bad. - for (i = 0; i < json.nodes.length; i++) { - if (json.nodes[i].name == source_name) { - source_index = i; + + /* + * First we have map the client_ids to the node by + * searching all of the interfaces we put into the + * list above. + * + * Javascript does not do dictionaries. Too bad. + */ + for (i = 0; i < interfaces.length; i++) { + if (interfaces[i].client_id == source_ifname) { + source_name = interfaces[i].node_id; + source_index = interfaces[i].node_index; } - if (json.nodes[i].name == target_name) { - target_index = i; + if (interfaces[i].client_id == target_ifname) { + target_name = interfaces[i].node_id; + target_index = interfaces[i].node_index; } } json.links.push({"name" : client_id, @@ -916,11 +942,16 @@ function LoginByModal() else { // Clear previous error. $("#quickvm_login_form_error").html(""); - HideModal("#quickvm_login_modal"); $("#loginstatus").html("<a>" + uid + " logged in</a>"); $("#loginstatus").removeClass("hidden"); $("#quickvm_actions_menu").removeClass("hidden"); $("#loginbutton").addClass("hidden"); + $("#quickvm_login_form_error").html( + "<center>" + "Login successful</center><br>"); + setTimeout(function() { + HideModal("#quickvm_login_modal"); + $("#quickvm_login_form_error").html(""); + }, 2000); } } var xmlthing = $.ajax({ diff --git a/www/aptui/login.php b/www/aptui/login.php index a6b52bebd87fefa5e281a0e37fb606fd013a38ae..153f5c9e25ba237f63c3f0f9e948115f3a520bba 100644 --- a/www/aptui/login.php +++ b/www/aptui/login.php @@ -27,6 +27,11 @@ chdir("apt"); include("quickvm_sup.php"); $page_title = "Login"; +# +# Get current user in case we need an error message. +# +$this_user = CheckLogin($check_status); + # # Verify page arguments. # @@ -71,6 +76,11 @@ function SPITFORM($uid, $referrer, $error) { global $TBDB_UIDLEN, $TBBASE, $refer; + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Cache-Control: no-cache, max-age=0, must-revalidate, no-store"); + header("Pragma: no-cache"); + SPITHEADER(); echo "<div class='row'> @@ -98,6 +108,9 @@ function SPITFORM($uid, $referrer, $error) case "timedout": echo "Your login has timed out!"; break; + case "alreadyloggedin": + echo "You are already logged in. Logout first?"; + break; default: echo "Unknown Error ($error)!"; } @@ -140,7 +153,8 @@ function SPITFORM($uid, $referrer, $error) # If not clicked, then put up a form. # if (!$ajax_request && !isset($login)) { - SPITFORM(REMEMBERED_ID(), $referrer, null); + SPITFORM(REMEMBERED_ID(), $referrer, + ($this_user ? "alreadyloggedin" : null)); return; } @@ -182,6 +196,11 @@ else { } } +header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Cache-Control: no-cache, max-age=0, must-revalidate, no-store"); +header("Pragma: no-cache"); + # # Failed, then try again with an error message. # diff --git a/www/aptui/logout.php b/www/aptui/logout.php index e2830dabb37f6ac1c4574de58d1f064ed84939f3..e0d5f5609676c56d2ff5698d10cf53696820507d 100644 --- a/www/aptui/logout.php +++ b/www/aptui/logout.php @@ -53,5 +53,5 @@ if ($ajax_request) { SPITAJAX_RESPONSE(""); exit(); } -header("Location: quickvm.php"); +header("Location: login.php"); ?> diff --git a/www/aptui/manage_profile.php b/www/aptui/manage_profile.php index f85e46c55d7bb5282c3f8708e11d170a235ab8a5..5a99bd501c3a1d39d95f207a55f3e8c4e92da216 100644 --- a/www/aptui/manage_profile.php +++ b/www/aptui/manage_profile.php @@ -25,7 +25,9 @@ chdir(".."); include("defs.php3"); chdir("apt"); include("quickvm_sup.php"); +include("profile_defs.php"); $page_title = "Manage Profile"; +$notifyupdate = 0; # # Get current user. @@ -38,6 +40,7 @@ $this_user = CheckLogin($check_status); $optargs = OptionalPageArguments("create", PAGEARG_STRING, "action", PAGEARG_STRING, "idx", PAGEARG_INTEGER, + "finished", PAGEARG_BOOLEAN, "formfields", PAGEARG_ARRAY); # @@ -45,7 +48,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING, # function SPITFORM($formfields, $errors) { - global $this_user, $projlist, $action, $idx; + global $this_user, $projlist, $action, $idx, $notifyupdate; $editing = 0; if ($action == "edit") { @@ -118,7 +121,11 @@ function SPITFORM($formfields, $errors) # Look for non-specific error. # if ($errors && array_key_exists("error", $errors)) { - echo "<font color=red>" . $errors["error"] . "</font>"; + echo "<font color=red><center>" . $errors["error"] . "</center></font>"; + } + # Did we just complete an update. + if ($notifyupdate) { + echo "<font color=green><center>Update Successful!</center></font>"; } # Mark as editing mode on post. if ($editing) { @@ -215,11 +222,11 @@ function SPITFORM($formfields, $errors) } $formatter("profile_rspec", "Your rspec", $rspec_html); - $formatter("profile_public", "Public?", + $formatter("profile_listed", "Listed?", "<div class='checkbox'> - <label><input name=\"formfields[profile_public]\" ". - $formfields["profile_public"] . - " id='profile_public' value=checked + <label><input name=\"formfields[profile_listed]\" ". + $formfields["profile_listed"] . + " id='profile_listed' value=checked type=checkbox> ". "List on the public page for anyone to use?</label></div>"); @@ -227,15 +234,15 @@ function SPITFORM($formfields, $errors) echo "<div class='form-group'> <div class='col-sm-offset-2 col-sm-10'> - <button class='btn btn-primary btm-xs pull-right' + <button class='btn btn-primary btn-sm pull-right' id='profile_submit_button' type='submit' name='create'>$button_label</button>\n"; - echo " <a class='btn btn-primary btm-xs pull-right' + if ($editing) { + echo " <a class='btn btn-primary btn-sm pull-right' style='margin-right: 10px;' href='quickvm.php?profile=$idx' type='submit' name='create'>Instantiate</a>\n"; - if ($editing) { - echo " <a class='btn btn-danger btm-xs pull-left' + echo " <a class='btn btn-danger btn-sm pull-left' style='margin-right: 10px;' href='manage_profile.php?action=delete&idx=$idx' type='button' name='delete'>Delete</a>\n"; @@ -292,7 +299,7 @@ function SPITFORM($formfields, $errors) <div class='panel panel-default' id='showtopo_container'> <div class='panel-body'> - <div id='showtopo_div'></div> + <div id='showtopo_nopicker'></div> </div> </div> </div> @@ -336,11 +343,9 @@ if (! isset($create)) { $errors["error"] = "No profile specified for edit/delete!"; } else { - $query_result = - DBQueryFatal("select * from apt_profiles ". - "where idx='$idx' and creator_idx='$this_idx'"); - - if (!$query_result || !mysql_num_rows($query_result)) { + $profile = Profile::Lookup($idx); + + if (!$profile || $this_idx != $profile->creator_idx()) { $errors["error"] = "No such profile!"; } else if ($action == "delete") { @@ -349,13 +354,30 @@ if (! isset($create)) { return; } else { - $row = mysql_fetch_array($query_result); - $defaults["profile_pid"] = $row["pid"]; - $defaults["profile_description"] = $row["description"]; - $defaults["profile_name"] = $row["name"]; - $defaults["profile_rspec"] = $row["rspec"]; - $defaults["profile_public"] = - ($row["public"] ? "checked" : ""); + $defaults["profile_pid"] = $profile->pid(); + $defaults["profile_description"] = $profile->description(); + $defaults["profile_name"] = $profile->name(); + $defaults["profile_rspec"] = $profile->rspec(); + $defaults["profile_listed"] = + ($profile->listed() ? "checked" : ""); + + # + # If we are displaying after a successful edit, and it + # just happened (by looking at the modify time), show + # a message that the update was successful. This is pretty + # crappy, but I do not want to go for a fancy thing (popover) + # just yet, maybe later. + # + if (isset($finished) && $profile->modified()) { + $mod = new DateTime($profile->modified()); + if ($mod) { + $now = new DateTime("now"); + $diff = $now->getTimestamp() - $mod->getTimestamp(); + if ($diff < 2) { + $notifyupdate = 1; + } + } + } } } } @@ -470,12 +492,15 @@ else { fwrite($fp, "<attribute name='rspec'>"); fwrite($fp, " <value>" . htmlspecialchars($rspec) . "</value>"); fwrite($fp, "</attribute>\n"); - if (isset($formfields["profile_public"]) && - $formfields["profile_public"] == "checked") { - fwrite($fp, "<attribute name='profile_public'>"); - fwrite($fp, " <value>1</value>"); - fwrite($fp, "</attribute>\n"); + fwrite($fp, "<attribute name='profile_listed'><value>"); + if (isset($formfields["profile_listed"]) && + $formfields["profile_listed"] == "checked") { + fwrite($fp, "1"); + } + else { + fwrite($fp, "0"); } + fwrite($fp, "</value></attribute>\n"); fwrite($fp, "</profile>\n"); fclose($fp); chmod($xmlname, 0666); @@ -533,7 +558,8 @@ if (!$query_result || !mysql_num_rows($query_result)) { else { $row = mysql_fetch_array($query_result); $idx = $row["idx"]; - header("Location: $APTBASE/manage_profile.php?action=edit&idx=$idx"); + header("Location: $APTBASE/manage_profile.php?action=edit&idx=$idx". + "&finished=1"); } ?> diff --git a/www/aptui/myprofiles.php b/www/aptui/myprofiles.php index 639254226a98b47c0385c9be0d94fd3b8e7698ae..6f37f1c1111aa5a0bf12a4621e0fb59287d7277f 100644 --- a/www/aptui/myprofiles.php +++ b/www/aptui/myprofiles.php @@ -99,6 +99,8 @@ $query_result = if (mysql_num_rows($query_result) == 0) { echo "<b>No profiles to show you. Maybe you want to ". "<a href='manage_profile.php'>create one?</a></b>\n"; + echo "<script src='js/lib/require.js' data-main='js/null'> + </script>\n"; SPITFOOTER(); exit(); } @@ -135,8 +137,12 @@ echo " <div class='panel panel-default'> type='button' name='profile_button'> Select a Profile</button>\n"; echo " </div> - <button class='btn btn-primary btn-xs pull-right' - type='submit' name='submit'>Go</button> + <button class='btn btn-primary btn-sm pull-right' + type='submit' name='submit'>Modify</button> + <a id='instantiate' + class='btn btn-primary btn-sm pull-right' + style='margin-right: 10px;' + type='button'>Instantiate</a> </form> </div> </div> diff --git a/www/aptui/profile_defs.php b/www/aptui/profile_defs.php new file mode 100644 index 0000000000000000000000000000000000000000..8e587987abc3793ddafd0400c14ccb912226ba42 --- /dev/null +++ b/www/aptui/profile_defs.php @@ -0,0 +1,111 @@ +<?php +# +# Copyright (c) 2006-2014 University of Utah and the Flux Group. +# +# {{{EMULAB-LICENSE +# +# This file is part of the Emulab network testbed software. +# +# This file is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# This file is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public +# License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this file. If not, see <http://www.gnu.org/licenses/>. +# +# }}} +# +# +class Profile +{ + var $profile; + var $project; + + # + # Constructor by lookup on unique index. + # + function Profile($idx) { + $safe_idx = addslashes($idx); + + $query_result = + DBQueryWarn("select * from apt_profiles ". + "where idx='$safe_idx'"); + + if (!$query_result || !mysql_num_rows($query_result)) { + $this->profile = null; + return; + } + $this->profile = mysql_fetch_array($query_result); + + # Load lazily; + $this->project = null; + } + # accessors + function field($name) { + return (is_null($this->profile) ? -1 : $this->profile[$name]); + } + function name() { return $this->field('name'); } + function idx() { return $this->field('idx'); } + function creator() { return $this->field('creator'); } + function creator_idx() { return $this->field('creator_idx'); } + function pid() { return $this->field('pid'); } + function pid_idx() { return $this->field('pid_idx'); } + function created() { return $this->field('created'); } + function modified() { return $this->field('modified'); } + function uuid() { return $this->field('uuid'); } + function ispublic() { return $this->field('public'); } + function shared() { return $this->field('shared'); } + function listed() { return $this->field('listed'); } + function weburi() { return $this->field('weburi'); } + function description() { return $this->field('description'); } + function rspec() { return $this->field('rspec'); } + + # Hmm, how does one cause an error in a php constructor? + function IsValid() { + return !is_null($this->profile); + } + + # Lookup up a single profile by idx. + function Lookup($idx) { + $foo = new Profile($idx); + + if ($foo->IsValid()) { + # Insert into cache. + return $foo; + } + return null; + } + + # Lookup by name/version. If no version, then return highest + # numbered version. + function LookupByName() {} + + # + # Refresh an instance by reloading from the DB. + # + function Refresh() { + if (! $this->IsValid()) + return -1; + + $idx = $this->idx(); + + $query_result = + DBQueryWarn("select * from apt_profiles where idx='$idx'"); + + if (!$query_result || !mysql_num_rows($query_result)) { + $this->profile = NULL; + $this->project = null; + return -1; + } + $this->profile = mysql_fetch_array($query_result); + $this->project = null; + return 0; + } +} +?> diff --git a/www/aptui/quickvm.css b/www/aptui/quickvm.css index 42f54696020909d13cf3afdc6c6163d5a5b8ecda..7e00c073f5106ed1c9468f3bcfa0cbe01fba21bf 100644 --- a/www/aptui/quickvm.css +++ b/www/aptui/quickvm.css @@ -119,14 +119,22 @@ body { padding: 5px; } +#showtopo_nopicker { + border: 1px solid #000; +} + @media (min-width: 970px) { .modal-dialog { width: 950px; } +#showtopo_description, #showtopo_div { width: 643px; } +#showtopo_nopicker { + width: 850px; +} } @media (max-width: 969px) { @@ -136,38 +144,62 @@ body { } @media (min-width: 910px) and (max-width: 969px) { + #showtopo_description, #showtopo_div { width: 610px; +} + #showtopo_nopicker { + width: 800px; } } @media (min-width: 850px) and (max-width: 909px) { + #showtopo_description, #showtopo_div { width: 560px; +} + #showtopo_nopicker { + width: 750px; } } @media (min-width: 790px) and (max-width: 849px) { + #showtopo_description, #showtopo_div { width: 510px; +} + #showtopo_nopicker { + width: 710px; } } @media (min-width: 768px) and (max-width: 789px) { + #showtopo_description, #showtopo_div { width: 500px; +} + #showtopo_nopicker { + width: 700px; } } @media (min-width: 740px) and (max-width: 767px) { + #showtopo_description, #showtopo_div { width: 470px; +} + #showtopo_nopicker { + width: 670px; } } @media (min-width: 690px) and (max-width: 739px) { + #showtopo_description, #showtopo_div { width: 430px; +} + #showtopo_nopicker { + width: 630px; } } diff --git a/www/aptui/quickvm.php b/www/aptui/quickvm.php index 073ac8056f33a1556ea678583ce5dc6d71bf6dfc..3257c01cd2f314ddb880e78680adf9f0b3062b3a 100755 --- a/www/aptui/quickvm.php +++ b/www/aptui/quickvm.php @@ -46,7 +46,6 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING, "stuffing", PAGEARG_STRING, "verify", PAGEARG_STRING, "sshkey", PAGEARG_STRING, - "profile", PAGEARG_INTEGER, "ajax_request", PAGEARG_BOOLEAN, "ajax_method", PAGEARG_STRING, "ajax_argument", PAGEARG_STRING); @@ -92,10 +91,6 @@ while ($row = mysql_fetch_array($query_result)) { $profile_default = $row["idx"]; } } -# URL specified profile to use. -if (isset($profile) && array_key_exists($profile, $profile_array)) { - $profile_default = $profile; -} function SPITFORM($username, $email, $sshkey, $profile, $newuser, $errors) { @@ -275,7 +270,7 @@ if (!isset($create)) { $username = $this_user->uid(); $email = $this_user->email(); } - SPITFORM($username, $email, $sshkey, null, false, null); + SPITFORM($username, $email, $sshkey, $profile, false, null); SPITFOOTER(); return; } diff --git a/www/aptui/quickvm_status.php b/www/aptui/quickvm_status.php index 5d18226c99e501a3cd11224f9587575ffa85fc74..5efd98c4971c0f075ff91c24f79b9902950475af 100644 --- a/www/aptui/quickvm_status.php +++ b/www/aptui/quickvm_status.php @@ -98,21 +98,6 @@ if (!$creator) { return; } $slice = GeniSlice::Lookup("sa", $quickvm->slice_uuid()); -if (!$slice) { - if ($ajax_request) { - SPITAJAX_ERROR(1, "no slice for quickvm "); - exit(); - } - SPITHEADER(1); - echo "<div class='align-center'> - <p class='lead text-center'> - Hmm, there seems to be a problem. - </p> - </div>\n"; - SPITFOOTER(); - TBERROR("No slice for quickvm $uuid", 0); - return; -} # # Deal with ajax requests. @@ -133,6 +118,10 @@ if (isset($ajax_request)) { SPITAJAX_RESPONSE(SSHAuthObject($creator->uid(), $ajax_argument)); } elseif ($ajax_method == "request_extension") { + if (!isset($slice)) { + SPITAJAX_ERROR(1, "Nothing to extend!"); + return; + } # Only extend for 24 hours. More later. $expires_time = strtotime($slice->expires()); if ($expires_time > time() + (3600 * 36)) { @@ -171,8 +160,12 @@ if (isset($ajax_request)) { SPITHEADER(1); $style = "style='border: none;'"; -$slice_urn = $slice->urn(); -$slice_expires = gmdate("Y-m-d H:i:s", strtotime($slice->expires())); +$slice_urn = "n/a"; +$slice_expires = "n/a"; +if (isset($slice)) { + $slice_urn = $slice->urn(); + $slice_expires = gmdate("Y-m-d H:i:s", strtotime($slice->expires())); +} $quickvm_status = $quickvm->status(); $creator_uid = $creator->uid(); $creator_email = $creator->email(); @@ -271,7 +264,7 @@ echo "<div class='row'> echo "<div class='panel panel-default invisible' id='showtopo_container'>\n"; echo "<div class='panel-body'>\n"; echo "<div id='quicktabs_div'>\n"; -echo "<div id='showtopo_div'></div>\n"; +echo "<div id='showtopo_statuspage'></div>\n"; SpitToolTip("Click on a node to SSH to that node.\n". "Click and drag on a node to move things around."); echo "</div>\n"; # showtopo