instantiate.php 35.7 KB
Newer Older
Leigh B Stoller's avatar
Leigh B Stoller committed
1 2
<?php
#
Leigh B Stoller's avatar
Leigh B Stoller committed
3
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
Leigh B Stoller's avatar
Leigh B Stoller committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# 
# {{{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/>.
# 
# }}}
#
chdir("..");
include("defs.php3");
include_once("osinfo_defs.php");
include_once("geni_defs.php");
Leigh B Stoller's avatar
Leigh B Stoller committed
28
include_once("webtask.php");
29
chdir("apt");
Leigh B Stoller's avatar
Leigh B Stoller committed
30
include("quickvm_sup.php");
31 32
include_once("instance_defs.php");
include_once("profile_defs.php");
Robert Ricci's avatar
Robert Ricci committed
33
$page_title = "Instantiate a Profile";
Leigh B Stoller's avatar
Leigh B Stoller committed
34
$dblink = GetDBLink("sa");
Leigh B Stoller's avatar
Leigh B Stoller committed
35

36
#
37 38
# Get current user but make sure coming in on SSL. Guest users allowed
# via APT Portal.
39 40 41
#
RedirectSecure();
$this_user = CheckLogin($check_status);
42
if (isset($this_user)) {
43
    CheckLoginOrDie(CHECKLOGIN_NONLOCAL|CHECKLOGIN_WEBONLY);
44 45
}
elseif ($ISCLOUD) {
46
    RedirectLoginPage();
47
}
48

Leigh B Stoller's avatar
Leigh B Stoller committed
49 50 51
#
# Verify page arguments.
#
52 53
$optargs = OptionalPageArguments("create",        PAGEARG_STRING,
				 "profile",       PAGEARG_STRING,
54
				 "version",       PAGEARG_INTEGER,
55 56 57
				 "stuffing",      PAGEARG_STRING,
				 "verify",        PAGEARG_STRING,
				 "project",       PAGEARG_PROJECT,
58
				 "asguest",       PAGEARG_BOOLEAN,
59
				 "default",       PAGEARG_STRING,
60
				 "formfields",    PAGEARG_ARRAY);
61

62
if ($ISAPT && !$this_user) {
63 64 65 66 67 68 69 70 71 72 73 74 75
    #
    # If user appears to have an account, go to login page.
    # Continue as guest on that page.
    #
    if (REMEMBERED_ID()) {
	if (isset($asguest) && $asguest) {
	    # User clicked on continue as guest. If we do not delete the
	    # cookie, then user will go through the same loop next time
            # they click the Home button, since that points here. So delete
	    # the UID cookie. Not sure I like this.
	    ClearRememberedID();
	}
	else {
Leigh B Stoller's avatar
Leigh B Stoller committed
76 77
            header("Location: login.php?from=instantiate&referrer=".
                   urlencode($_SERVER['REQUEST_URI']));
78 79 80
	}
    }
}
81 82
if ($this_user) {
    $projlist = $this_user->ProjectAccessList($TB_PROJECT_CREATEEXPT);
83 84 85 86 87 88
    if (count($projlist) == 0) {
	SPITUSERERROR("You do not belong to any projects with permission to ".
                      "create new experiments. Please contact your project ".
                      "leader to grant you the neccessary privilege.");
	exit();
    }
89
}
90
if ($ISCLOUD) {
Leigh B Stoller's avatar
Leigh B Stoller committed
91 92 93 94
#    $profile_default     = "ARM64OpenStack";
#    $profile_default_pid = "emulab-ops";
    $profile_default     = "Tutorial-OpenStack";
    $profile_default_pid = "CloudLab";
95 96 97 98 99
}
else {
    $profile_default     = "OneVM";
    $profile_default_pid = $TBOPSPID;
}
100 101
$profile_array  = array();
$am_array       = Instance::DefaultAggregateList();
Leigh B Stoller's avatar
Leigh B Stoller committed
102

103 104 105 106
#
# if using the super secret URL, make sure the profile exists, and
# add to the array now since it might not be public or belong to the user.
#
107
if (isset($profile)) {
108 109
    #
    # Guest users must use the uuid, but logged in users may use the
110
    # internal index. But, we have to support simple the URL too, which
111
    # is /p/project/profilename, but only for public profiles.
112
    #
113
    if (isset($project) && isset($profile)) {
114
	$obj = Profile::LookupByName($project, $profile, $version);
115 116 117 118 119
    }
    elseif ($this_user || IsValidUUID($profile)) {
	$obj = Profile::Lookup($profile);
    }
    else {
120 121 122 123 124 125 126 127
	SPITUSERERROR("Illegal profile for guest user: $profile");
	exit();
    }
    if (! $obj) {
	SPITUSERERROR("No such profile: $profile");
	exit();
    }
    if (IsValidUUID($profile)) {
128 129 130 131 132
	#
	# If uuid was to profile, then find the most recently published
	# version and instantiate that, since what we have is the most
	# recent version, but might not be published.
	#
133
	if (0 && $profile == $obj->profile_uuid() && !$obj->published()) {
134 135 136 137 138 139
	    $obj = $obj->LookupMostRecentPublished();
	    if (! $obj) {
		SPITUSERERROR("No published version for profile");
		exit();
	    }
	}
140 141 142
        $profile = $obj;
	$profile_array[$profile->uuid()] = $profile->name();
	$profilename = $profile->name();
143 144
    }
    else {
145 146 147 148 149
	#
	# If no version provided, then find the most recently published
	# version and instantiate that, since what we have is the most
	# recent version, but might not be published.
	#
150
	if (0 && !isset($version) && !$obj->published()) {
151 152 153 154 155 156 157
	    $obj = $obj->LookupMostRecentPublished();
	    if (! $obj) {
		SPITUSERERROR("No published version for profile");
		exit();
	    }
	}
	 
158
	#
Leigh B Stoller's avatar
Leigh B Stoller committed
159
	# Must be public or pass the permission test for the user.
160 161
	#
	if (! ($obj->ispublic() ||
162
	       (isset($this_user) && $obj->CanInstantiate($this_user)))) {
163 164 165
	    SPITUSERERROR("No permission to use profile: $profile");
	    exit();
	}
166 167 168
	$profile = $obj;
	$profile_array[$profile->uuid()] = $profile->name();
	$profilename = $profile->name();
169
    }
170
}
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
else {
    #
    # Find all the public and user profiles. We use the UUID instead of
    # indicies cause we do not want to leak internal DB state to guest
    # users. Need to decide on what clause to use, depending on whether
    # a guest user or not.
    #
    $joinclause   = "";
    $whereclause  = "";
    if (!isset($this_user)) {
	$whereclause = "p.public=1";
    }
    else {
	$this_idx = $this_user->uid_idx();
	$joinclause =
	    "left join group_membership as g on ".
	    "     g.uid_idx='$this_idx' and ".
	    "     g.pid_idx=v.pid_idx and g.pid_idx=g.gid_idx";
	$whereclause =
	    "p.public=1 or p.shared=1 or v.creator_idx='$this_idx' or ".
	    "g.uid_idx is not null ";
192
    }
193 194 195 196 197 198 199

    $query_result =
	DBQueryFatal("select p.*,v.* from apt_profiles as p ".
		     "left join apt_profile_versions as v on ".
		     "     v.profileid=p.profileid and ".
		     "     v.version=p.version ".
		     "$joinclause ".
200 201
		     "where locked is null and ($whereclause) ".
		     "order by p.topdog desc");
202 203
    while ($row = mysql_fetch_array($query_result)) {
	$profile_array[$row["uuid"]] = $row["name"];
204 205
        if ($row["pid"] == $profile_default_pid &&
            $row["name"] == $profile_default) {
206
	    $profile_default = $row["uuid"];
207
	}
208
    }
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    #
    # A specific profile, but we still want to give the user the selection
    # list above, but the profile might not be in the list if it is not
    # the highest numbered version.
    #
    if (isset($default)) {
        if (IsValidUUID($default)) {
            $obj = Profile::Lookup($default);
            if (!$obj) {
                SPITUSERERROR("Unknown default profile: $default");
                exit();
            }
            if (! ($obj->ispublic() ||
                   (isset($this_user) && $obj->CanInstantiate($this_user)))) {
                SPITUSERERROR("No permission to use profile: $default");
                exit();
            }
            $profile_array[$obj->uuid()] = $obj->name();
            $profile_default = $obj->uuid();
        }
        else {
            SPITUSERERROR("Illegal default profile: $default");
            exit();
        }
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
234
}
Leigh B Stoller's avatar
Leigh B Stoller committed
235

236
function SPITFORM($formfields, $newuser, $errors)
Leigh B Stoller's avatar
Leigh B Stoller committed
237
{
238
    global $TBBASE, $APTMAIL, $ISCLOUD;
239
    global $profile_array, $this_user, $profilename, $profile, $am_array;
240
    global $projlist;
241
    $skipsteps  = "false";
242
    $amlist     = array();
Leigh B Stoller's avatar
Leigh B Stoller committed
243
    $showabout  = (!$ISCLOUD && !$this_user ? 1 : 0);
244
    $registered = (isset($this_user) ? "true" : "false");
Leigh B Stoller's avatar
Leigh B Stoller committed
245 246
    $webonly    = (isset($this_user) &&
                   $this_user->webonly() ? "true" : "false");
247
    $nopprspec  = (!isset($this_user) ? "true" : "false");
248 249 250 251 252 253 254 255 256
    $portal     = "";
    # Gack.
    if (isset($this_user) && $this_user->IsNonLocal()) {
        if (preg_match("/^[^+]*\+([^+]+)\+([^+]+)\+(.+)$/",
                       $this_user->nonlocal_id(), $matches) &&
            $matches[1] == "ch.geni.net") {
            $portal = "https://portal.geni.net/";
        }
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
257

258 259 260
    # XSS prevention.
    while (list ($key, $val) = each ($formfields)) {
	$formfields[$key] = CleanString($val);
Leigh B Stoller's avatar
Leigh B Stoller committed
261
    }
262
    # XSS prevention.
Leigh B Stoller's avatar
Leigh B Stoller committed
263
    if ($errors) {
264
        $skipsteps = "true";
265
	while (list ($key, $val) = each ($errors)) {
266
	    # Skip internal error, we want the html in those errors
267
	    # and we know it is safe.
268 269 270
	    if ($key == "error") {
		continue;
	    }
271
	    $errors[$key] = CleanString($val);
Leigh B Stoller's avatar
Leigh B Stoller committed
272 273
	}
    }
274 275 276 277 278 279 280 281 282

    $formatter = function($field, $html) use ($errors) {
	$class = "form-group";
	if ($errors && array_key_exists($field, $errors)) {
	    $class .= " has-error";
	}
	echo "<div class='$class'>\n";
	echo "     $html\n";
	if ($errors && array_key_exists($field, $errors)) {
283
	    echo "<label class='control-label small' for='$field'>" .
284 285 286 287 288
		$errors[$field] . "</label>\n";
	}
	echo "</div>\n";
    };

Leigh B Stoller's avatar
Leigh B Stoller committed
289
    SPITHEADER(1);
290
    echo "<div id='waitwait_div'></div>\n";
291
    echo "<div id='ppviewmodal_div'></div>\n";
Keith Downie's avatar
Keith Downie committed
292 293 294 295 296
    # Placeholder for the "about" panel, which is now a template file.
    echo "<div id='about_div' class='col-lg-8  col-lg-offset-2
                                      col-md-8  col-md-offset-2
                                      col-sm-10  col-sm-offset-1
                                      col-xs-12 col-xs-offset-0'></div>\n";
297
    echo "<div id='stepsContainer'>
Keith Downie's avatar
Keith Downie committed
298
          <h3>Select a Profile</h3>
Keith Downie's avatar
Keith Downie committed
299 300 301
          <div class='col-lg-8  col-lg-offset-2
                      col-md-8  col-md-offset-2
                      col-sm-10  col-sm-offset-1
Leigh B Stoller's avatar
Leigh B Stoller committed
302
                      col-xs-12 col-xs-offset-0'>\n";
Jonathon Duerig's avatar
Jonathon Duerig committed
303 304


305
    echo "<form id='quickvm_form' role='form'
306
            enctype='multipart/form-data'
307
            method='post' action='instantiate.php'>\n";
308 309 310 311
    if (!$this_user) {
	echo "<div class='panel panel-default'>
                <div class='panel-heading'>
                   <h3 class='panel-title'>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
312
    echo "<center>Start Experiment";
313 314 315
    if (isset($profilename)) {
        echo " using profile &quot;$profilename&quot";
    }
316
    echo "</center></h3>\n";
Keith Downie's avatar
Keith Downie committed
317 318 319 320 321 322 323 324 325 326 327
    }
    else {
    # Will only show header when linked to a profile
    if (isset($profilename)) {
        echo "<h3 style='margin: 0px;'>";
        echo "<center>Start Experiment";
        echo " using profile &quot;$profilename&quot";
        echo "</center></h3>\n";
    }
    }

328 329 330 331
    if (!$this_user) {
	echo "   </div>
	      <div class='panel-body'>\n";
    }
332 333 334 335 336
    
    #
    # If linked to a specific profile, description goes here
    #
    if ($profile) {
337 338
	$cluster = ($ISCLOUD ? "Cloudlab" : "APT");
	
339 340 341 342
	if (!$this_user) {
	    echo "  <p>Fill out the form below to run an experiment ".
		"using this profile:</p>\n";
	}
343
        # Note: Following line is also duplicated below
344 345 346
        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
                   physical machines described in the profile will be booted
347
                   on ${cluster}'s hardware<p>\n";
348 349
    }

350 351
    echo "   <fieldset>\n";

352 353 354
    #
    # Ask for user information
    #
355 356 357 358 359 360 361 362 363 364 365 366
    if (!isset($this_user)) {
	$formatter("username", 
		  "<input name=\"formfields[username]\"
		          value='" . $formfields["username"] . "'
                          class='form-control'
                          placeholder='Pick a user name'
                          autofocus type='text'>");
   
	$formatter("email", 
		  "<input name=\"formfields[email]\"
                          type='text'
                          value='" . $formfields["email"] . "'
367
                          class='form-control'
368
                          placeholder='Your email address' type='text'>");
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
    }
    # We put the ssh stuff in two different places, so make it a function.
    $spitsshkeystuff = function() use ($formfields, $formatter) {
	if ($formfields["sshkey"] == "") {
	    $title_text = "<span class='text-warning'>
                    No SSH key, browser shell only!<span>";
	    $expand_text = "Add Key";
	}
	else {
	    $title_text = "<span class='text-info'>
                    Your SSH key</span>";
	    $expand_text = "Update";
	}
        echo "<div class='form-group row' style='margin-bottom: 0px;'>";
        echo "  <div class='col-md-12'>
                  <div class='panel panel-default'>\n";
        echo "      <div class='panel-heading'>$title_text
                     <a class='pull-right'
                        data-toggle='collapse' href='#mysshkey'>
                      $expand_text</a>\n";
        echo "      </div>\n";
	echo "      <div id='mysshkey' class='panel-collapse collapse'>\n";
        echo "       <div class='panel-body'>";
	
393 394
	$formatter("keyfile",
		   "<span class='help-block'>
395 396 397
                     Upload a file or paste it in the text box. This will ".
		   "allow you to login using your favorite ssh client. Without ".
		   "a SSH key, you will be limited to using a shell window in ".
398 399 400
		   "your browser. If you already see a key here, you can ".
		   "change it and we will remember your new key for next time. ".
                   "Don't know how to generate your SSH key? ".
401
		   "See <a href='https://help.github.com/articles/generating-ssh-keys'>this tutorial.</a></span>".
402 403
		   "<input type=file name='keyfile'>");

404 405
	$formatter("sshkey", 
		  "<textarea name=\"formfields[sshkey]\" 
406
                             placeholder='Paste in your ssh public key.'
407 408 409
                             class='form-control'
                             rows=4 cols=45>" . $formfields["sshkey"] .
                  "</textarea>");
410 411 412 413 414 415 416 417 418

        echo "       </div>";
        echo "       <div class='clearfix'></div>";
        echo "      </div>";
        echo "    </div>";
        echo "</div></div>"; # End of panel/row.
    };
    if (!isset($this_user)) {
	$spitsshkeystuff();
419
    }
420 421 422 423 424 425

    #
    # Only print profile selection box if we weren't linked to a specific
    # profile
    #
    if (!isset($profile)) {
426
        echo "<div class='form-group row' style='margin-bottom: 0px;'>";
427 428
        echo "<input id='selected_profile' type='hidden' 
                       name='formfields[profile]'/>";
429
        echo "<div class='col-md-12'><div class='panel panel-default'>\n";
430
        echo "<div class='panel-heading'>
Robert Ricci's avatar
Robert Ricci committed
431
                  <span class='panel-title'><strong>Selected Profile:</strong> 
432
                  <span id='selected_profile_text'>
Robert Ricci's avatar
Robert Ricci committed
433
                  </span></span>\n";
434
        if ($errors && array_key_exists("profile", $errors)) {
435
            echo "<label class='control-label small' for='inputError'>" .
436 437 438 439
                $errors["profile"] .
                " </label>\n";
        }
        echo " </div>\n";
440
        # Note: Following line is also duplicated above
441 442 443 444
        echo "<div class='panel-body'>";
        echo "  <div id='selected_profile_description'></div>\n";
        echo "</div>";
        echo "<div class='panel-footer'>";
445
        if (isset($this_user) && !$this_user->webonly()) {
446
            echo "<button class='btn btn-default btn-sm pull-left' 
447
                         type='button' id='profile_copy_button'
Leigh B Stoller's avatar
Leigh B Stoller committed
448 449 450 451 452 453 454 455 456 457
                         style='margin-right: 10px;'
		    data-toggle='popover'
		    data-delay='{hide:1500, show:500}'
		    data-html='true'
		    data-content='When you copy a profile
		    you are creating a new profile that
		    uses the same source code and metadata (description,
		    instructions) as the original profile, but without
		    creating a new disk image. Instead, the new profile uses
		    whatever images the original profile uses.'>Copy Profile
458
                  </button>";
459 460 461 462
            echo "<button class='btn btn-default btn-sm pull-left'
                          type='button' id='profile_show_button'>
                    Show Profile
                  </button>";
463 464
        }
        echo "<button id='profile' class='btn btn-primary btn-sm pull-right' 
465
                         type='button' name='profile_button'>
466 467 468 469
                    Change Profile
                  </button>";
        echo "<div class='clearfix'></div>";
        echo "</div>";
470
        echo "</div></div></div>"; # End of panel/row.
471
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
472
    else {
473 474 475 476 477 478
	echo "<input id='selected_profile' type='hidden'
                     name='formfields[profile]'
                     value='" . $formfields["profile"] . "'>\n";

	# Send the original argument for the initial array stuff above.
        # Needs more work.
479 480
        $thisuuid = $profile->uuid();
	echo "<input type='hidden' name='profile' value='$thisuuid'>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
481
    }
482

Keith Downie's avatar
Keith Downie committed
483 484 485 486
    #
    # Container for the inputs that get copied to the last step of the wizard
    #
    echo "<div id='experiment_options' class='hidden'>\n";
487 488 489 490
    #
    # Spit out an experient name box, which is optional and prefilled
    # with a default.
    #
491
    if ($this_user) {
492 493 494 495
        $thisclass = "form-group";
        if ($errors && array_key_exists("name", $errors)) {
            $thisclass .= " has-error";
        }
Keith Downie's avatar
Keith Downie committed
496
        echo "<div id='name_selector' class='form-horizontal experiment_option'>
497 498 499 500 501
                <div class='$thisclass'>
                  <label class='col-sm-4 control-label'
                         style='text-align: right;'>Name:</label>
                  <div class='col-sm-6'
                       data-toggle='popover'
Keith Downie's avatar
Keith Downie committed
502 503 504
               data-delay='{hide:1500, show:500}'
               data-html='true'
               data-content='Provide a unique name to identity your
505 506 507 508 509 510 511 512 513 514 515
                           new experiment. If you are in
                           the habit of starting more then one experiment at
                           a time this is really handy when trying to tell
                           one experiment from another, or when referring to
                           an experiment when asking for help.'>
                    <input id='experiment_name'
                           placeholder='Optional'
                           class='form-control'
                           name='formfields[name]'
                           value='" . $formfields["name"] . "'>\n";
        if ($errors && array_key_exists("name", $errors)) {
516 517
            echo "  <label class='control-label small' for='experiment_name'>".
                $errors["name"] . "</label>";
518 519 520 521 522 523
        }
        echo "    </div>
                </div>
              </div>\n";
    }
     
524 525 526
    #
    # Spit out a project selection list if more then one project membership
    #
527
    if ($this_user && !$this_user->webonly()) {
528
        if (count($projlist) == 1) {
529 530 531
            # We need this to look like the selector below, for the JS code.
            echo "<div class='hidden' id='project_selector'>
                   <input id='profile_pid' type='hidden'
532
                     name='formfields[pid]'
533
                     value='" . $formfields["pid"] . "'></div>\n";
534 535 536 537 538 539 540 541 542 543 544 545
        }
        else {
            $project_options = "";
            while (list($pid) = each($projlist)) {
                $selected = "";
                if ($formfields["pid"] == $pid) {
                    $selected = "selected";
                }
                $project_options .= 
                    "<option $selected value='$pid'>$pid</option>\n";
            }
            $html =
546 547 548
                "<div class='form-horizontal experiment_option'
                      id='project_selector'>
                  <div class='form-group'>
549 550 551 552 553 554 555 556 557 558
                   <label class='col-sm-4 control-label'
                      style='text-align: right;'>Project:</label>
                   <div class='col-sm-6'>
                       <select name=\"formfields[pid]\"
		              id='profile_pid' class='form-control'>
                       $project_options</select></div></div></div>\n";
            echo $html;
        }
    }

559 560
    if (isset($this_user) && !$this_user->webonly() && 
        ($ISCLOUD || ISADMIN() || STUDLY())) {
561 562
	$am_options = "";
	while (list($am, $urn) = each($am_array)) {
563
	    $amlist[$urn] = $am;
564 565 566 567 568 569 570
	    $selected = "";
	    if ($formfields["where"] == $am) {
		$selected = "selected";
	    }
	    $am_options .= 
		"<option $selected value='$am'>$am</option>\n";
	}
Keith Downie's avatar
Keith Downie committed
571

572
        $html = "<div id='aggregate_selector'>
Keith Downie's avatar
Keith Downie committed
573
                  <div class='form-horizontal experiment_option' id='nosite_selector'>
Leigh B Stoller's avatar
Leigh B Stoller committed
574
                   <div class='form-group'>
575 576 577
                     <label class='col-sm-4 control-label'
                            style='text-align: right;'>Cluster:</label>
                     <div class='col-sm-6'>
578 579
                       <select name=\"formfields[where]\"
		              id='profile_where' class='form-control'>
580
		       <option value=''>Please Select</option>
581
                       $am_options</select><br>
582 583 584 585
                     </div>
                     <div class='col-sm-10 col-sm-offset-1'
                          style='text-align: center'>
		       <div class='alert alert-warning' id='where-warning' style='display: none; margin-bottom: 5px'>This profile only works on some clusters. Incompatible clusters are unselectable.</div>
586
		       <div class='alert alert-danger' id='where-nowhere' style='display: none; margin-bottom: 5px'>This profile <b>will not work on any clusters</b>. All clusters are unselectable.</div>
587 588 589 590 591
                     </div>
                   </div>
                  </div>
                  <div id='site_selector' class='hidden'></div>
                </div>\n";
592
        echo $html;
593
    }
Keith Downie's avatar
Keith Downie committed
594
    echo "</div>\n";
595
    echo "</fieldset>
Keith Downie's avatar
Keith Downie committed
596
           <div class='form-group row hidden'>
597
           <div class='col-sm-6 col-sm-offset-3'>
Keith Downie's avatar
Keith Downie committed
598
           
599 600 601 602
           <button class='btn btn-primary btn-block hidden'
              id='configurator_button'
              type='button'>Configure
           </button>
Keith Downie's avatar
Keith Downie committed
603
           <button class='btn btn-success btn-block hidden' id='instantiate_submit'
Keith Downie's avatar
Keith Downie committed
604
              type='submit' name='create'>Create!
605
           </button>
606
           </div>
Keith Downie's avatar
Keith Downie committed
607 608 609 610 611 612
           </div>\n";

    # This is for a PP rspec.
    echo "<textarea name='formfields[pp_rspec]'
            id='pp_rspec_textarea'
                    class='form-control hidden'
613 614 615
                    type='textarea'>".
        (isset($formfields['pp_rspec']) ? $formfields['pp_rspec'] : "") .
        "</textarea>\n";
Keith Downie's avatar
Keith Downie committed
616 617
    echo "</form>\n
          </div>\n";
618
    if (!isset($this_user)) {
619 620
        echo "</div>
              </div>\n";
Keith Downie's avatar
Keith Downie committed
621
    }
Keith Downie's avatar
Keith Downie committed
622 623 624
    echo "<h3>Parameterize</h3><div class='col-lg-8  col-lg-offset-2
                                    col-md-8  col-md-offset-2
                                    col-sm-10  col-sm-offset-1
Keith Downie's avatar
Keith Downie committed
625
                                    col-xs-12 col-xs-offset-0'></div>\n";
Keith Downie's avatar
Keith Downie committed
626 627 628
    echo "<h3>Finalize</h3><div class='col-lg-8  col-lg-offset-2
                                    col-md-8  col-md-offset-2
                                    col-sm-10  col-sm-offset-1
Keith Downie's avatar
Keith Downie committed
629 630 631 632
                                    col-xs-12 col-xs-offset-0'>
                            <div id='finalize_container' class='col-lg-8 col-md-8 col-sm-8 col-xs-12'>
                                <div class='panel panel-default'>
                                    <div class='panel-heading'>Please review the selections below and then click Finish.</div>
633 634 635 636 637 638 639 640 641
                                    <div class='panel-body'>\n";
    #
    # Look for non-specific error.
    #
    if ($errors && array_key_exists("error", $errors)) {
    echo "<div style='margin-bottom: 10px;'><font color=red><center>" . $errors["error"] .
        "</center></font></div>";
    }
    echo "<div id='finalize_options'></div>
Keith Downie's avatar
Keith Downie committed
642 643 644 645
                                    </div>
                                </div>
                            </div>
                            <div id='inline_container' class='col-lg-4 col-md-4 col-sm-4 col-xs-12'>
Keith Downie's avatar
Keith Downie committed
646
                                <a id='inline_overlay' href='#'><span class='glyphicon glyphicon-fullscreen' aria-hidden='true'></span></a> 
Keith Downie's avatar
Keith Downie committed
647 648 649
                                <div id='inline_jacks'></div>
                            </div>
                                    </div>\n";
650 651


Keith Downie's avatar
Keith Downie committed
652 653 654 655 656 657
    echo "</div>\n";
    echo "<button class='btn btn-primary btn-sm'
        style='display:block;visibility:hidden;'
                id='modal_profile_continue_button'
                type='button' name='create'>Continue</button>\n";
    if (!isset($this_user)) {
658
	SpitVerifyModal("verify_modal", "Create");
659
    
660 661 662 663 664 665 666 667
	if ($newuser) {
	    if (is_string($newuser)) {
		$stuffing = $newuser;
	    }
	    else {
		$stuffing = substr(GENHASH(), 0, 16);
	    }
	    mail($formfields["email"],
668
		 "aptlab.net: Verification code for creating your experiment",
669 670 671
		 "Here is your user verification code. Please copy and\n".
		 "paste this code into the box on the experiment page.\n\n".
		 "      $stuffing\n",
Leigh B Stoller's avatar
Leigh B Stoller committed
672
		 "From: $APTMAIL");
673
	    echo "<input type='hidden' name='stuffing' value='$stuffing' />";
Leigh B Stoller's avatar
Leigh B Stoller committed
674 675 676
	}
    }

677
    SpitTopologyViewModal("quickvm_topomodal", $profile_array);
678 679 680 681
    echo "<div id='ppmodal_div'></div>\n";
    echo "<div id='instantiate_div'></div>\n";
    echo "<div id='editmodal_div'></div>\n";
    SpitOopsModal("oops");
Keith Downie's avatar
Keith Downie committed
682

683 684
    if (isset($this_user) && !$this_user->webonly() &&
        ($ISCLOUD || ISADMIN() || STUDLY())) {
685 686 687 688
	echo "<script type='text/plain' id='amlist-json'>\n";
	echo htmlentities(json_encode($amlist));
	echo "</script>\n";
    }
689
    echo "<script type='text/javascript'>\n";
690 691 692
    echo "    window.PROFILE    = '" . $formfields["profile"] . "';\n";
    echo "    window.AJAXURL    = 'server-ajax.php';\n";
    echo "    window.SHOWABOUT  = $showabout;\n";
693
    echo "    window.NOPPRSPEC  = $nopprspec;\n";
694
    echo "    window.REGISTERED = $registered;\n";
695 696
    echo "    window.WEBONLY    = $webonly;\n";
    echo "    window.PORTAL     = '$portal';\n";
697
    echo "    window.SKIPSTEPS  = $skipsteps;\n";
698 699 700
    if ($newuser) {
	echo "window.APT_OPTIONS.isNewUser = true;\n";
    }
701 702
    $isadmin = (isset($this_user) && ISADMIN() ? 1 : 0);
    echo "    window.ISADMIN    = $isadmin;\n";
703
    $multisite = (isset($this_user) ? 1 : 0);
704
    echo "    window.MULTISITE  = $multisite;\n";
705
    echo "</script>\n";
706 707 708
    echo "<script src='js/lib/jquery-2.0.3.min.js?nocache=asdfasdf'></script>\n";
    echo "<script src='js/lib/bootstrap.js?nocache=asdfasdf'></script>\n";
    echo "<script src='js/lib/require.js?nocache=asdfasdf' data-main='js/instantiate?nocache=asdfasdf'></script>";
Leigh B Stoller's avatar
Leigh B Stoller committed
709 710 711
}

if (!isset($create)) {
712 713 714 715
    $defaults = array();
    $defaults["username"] = "";
    $defaults["email"]    = "";
    $defaults["sshkey"]   = "";
716 717
    $defaults["profile"]  = (isset($profile) ?
                             $profile->uuid() : $profile_default);
718
    $defaults["where"]    = $DEFAULT_AGGREGATE;
719 720 721 722 723 724 725 726
    if ($this_user && count($projlist)) {
	list($project, $grouplist) = each($projlist);
        $defaults["pid"] = $project;
        reset($projlist);
    }
    else {
        $defaults["pid"] = "";
    }
727

728
    # 
729
    # Look for current user or cookie that tells us who the user is. 
730
    #
731
    if ($this_user) {
732 733
	$defaults["username"] = $this_user->uid();
	$defaults["email"]    = $this_user->email();
734 735 736 737 738 739 740
	#
	# Look for an key marked as an APT uploaded key and use that.
	# If no APT key, use any uploaded key; if the user leaves this
	# key in the form, it will become the official APT key.
	#
	$sshkey = $this_user->GetAPTSSHKey();
	if (!$sshkey) {
741 742
	    $sshkeys = $this_user->GetSSHKeys();
	    if (count($sshkeys)) {
743
		$sshkey = $sshkeys[0];
744 745
	    }
	}
746 747 748
	if ($sshkey) {
	    $defaults["sshkey"] = $sshkey;
	}
749 750
    }
    elseif (isset($_COOKIE['quickvm_user'])) {
Leigh B Stoller's avatar
Leigh B Stoller committed
751 752 753 754 755 756
	$geniuser = GeniUser::Lookup("sa", $_COOKIE['quickvm_user']);
	if ($geniuser) {
	    #
	    # Look for existing quickvm. User not allowed to create
	    # another one.
	    #
Leigh B Stoller's avatar
Leigh B Stoller committed
757 758
	    $instance = Instance::LookupByCreator($geniuser->uuid());
	    if ($instance && $instance->status() != "terminating") {
759 760
		header("Location: status.php?oneonly=1&uuid=" .
		       $instance->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
761 762
		return;
	    }
763 764 765 766 767 768 769 770
            #
            # Watch for too many instances by guest user and redirect
            # to the signup page.
            #
            if (Instance::GuestInstanceCount($geniuser) > $MAXGUESTINSTANCES) {
		header("Location: signup.php?toomany=1");
		return;
            }
771 772 773
	    $defaults["username"] = $geniuser->name();
	    $defaults["email"]    = $geniuser->email();
	    $defaults["sshkey"]   = $geniuser->SSHKey();
Leigh B Stoller's avatar
Leigh B Stoller committed
774 775
	}
    }
776
    SPITFORM($defaults, false, array());
777
    echo "<div style='display: none'><div id='jacks-dummy'></div></div>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
778 779 780 781 782 783 784 785 786
    SPITFOOTER();
    return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
$args   = array();

787 788 789 790
if (!$this_user) {
    #
    # These check do not matter for a logged in user; we ignore the values.
    #
791
    if (!isset($formfields["email"]) || $formfields["email"] == "") {
792 793
	$errors["email"] = "Missing Field";
    }
794
    elseif (! TBvalid_email($formfields["email"])) {
795 796
	$errors["email"] = TBFieldErrorString();
    }
797
    if (!isset($formfields["username"]) || $formfields["username"] == "") {
798 799
	$errors["username"] = "Missing Field";
    }
800
    elseif (! TBvalid_uid($formfields["username"])) {
801 802
	$errors["username"] = TBFieldErrorString();
    }
803
    elseif (User::LookupByUid($formfields["username"])) {
804
        # Do not allow uid overlap with real users.
805
	$errors["username"] = "Already in use - if you have an Emulab account, log in first";
806
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
807
}
808
if (!isset($formfields["profile"]) || $formfields["profile"] == "") {
Leigh B Stoller's avatar
Leigh B Stoller committed
809
    $errors["profile"] = "No selection made";
Leigh B Stoller's avatar
Leigh B Stoller committed
810
}
811 812
elseif (! array_key_exists($formfields["profile"], $profile_array)) {
    $errors["profile"] = "Invalid Profile: " . $formfields["profile"];
Leigh B Stoller's avatar
Leigh B Stoller committed
813
}
814
else {
815 816
    $temp = Profile::Lookup($formfields["profile"]);
    if (!$temp) {
817 818
	$errors["profile"] = "No such profile in the database";
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
819
    #
820
    # Real/Geni users are allowed to use Paramterized Profiles, which means
Leigh B Stoller's avatar
Leigh B Stoller committed
821 822
    # we could get an rspec.
    #
823
    if ($temp->isParameterized() && $this_user &&
Leigh B Stoller's avatar
Leigh B Stoller committed
824 825 826
        isset($formfields["pp_rspec"]) && $formfields["pp_rspec"] != "") {
        $args["rspec"] = $formfields["pp_rspec"];
    }
827
}
Leigh B Stoller's avatar
Leigh B Stoller committed
828

829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
if ($this_user) {
    #
    # Project has to exist.  
    #
    $project = Project::LookupByPid($formfields["pid"]);
    if (!$project) {
        $errors["pid"] = "No such project";
    }
    # User better be a member.
    elseif (!ISADMIN() &&
        (!$project->IsMember($this_user, $isapproved) || !$isapproved)) {
        $errors["pid"] = "Illegal project";
    }
    else {
        $args["pid"] = $project->pid_idx();
    }
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861

    # Experiment name is optional, we generate one later.
    if (isset($formfields["name"]) && $formfields["name"] != "") {
        if (strlen($formfields["name"]) > 16) {
            $errors["name"] = "Too long; must be <= 16 characters";
        }
        elseif (!TBvalid_eid($formfields["name"])) {
            $errors["name"] = TBFieldErrorString();
        }
        elseif ($project &&
                Instance::LookupByName($project, $formfields["name"])) {
            $errors["name"] = "Already in use by another experiment";
        }
        else {
            $args["instance_name"] = $formfields["name"];
        }
    }
862 863
}

Leigh B Stoller's avatar
Leigh B Stoller committed
864 865 866
#
# More sanity checks. 
#
867
if (!$this_user) {
868
    $geniuser = GeniUser::LookupByEmail("sa", $formfields["email"]);
869
    if ($geniuser) {
870
	if ($geniuser->name() != $formfields["username"]) {    
Leigh B Stoller's avatar
Leigh B Stoller committed
871
            $errors["email"] = "Already in use by another guest user";
872 873
	    unset($geniuser);
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
874 875
    }
}
876

877 878 879 880
#
# Allow admin users to select the Aggregate. Experimental.
#
$aggregate_urn = "";
881
$sitemap = array();
882

883
if ($this_user) {
884 885 886 887 888 889 890 891 892 893 894 895 896 897
    if (isset($formfields["sites"]) && is_array($formfields["sites"])) {
        while (list($siteid, $am) = each($formfields["sites"])) {
            if (array_key_exists($am, $am_array)) {
                $sitemap[$siteid] = $am_array[$am];
            }
            else {
                $errors["sites"] = "Invalid Aggregate";
                break;
            }
        }
    }
    elseif (isset($formfields["where"]) &&
            $formfields["where"] != "" &&
            array_key_exists($formfields["where"], $am_array)) {
898
	    $aggregate_urn = $am_array[$formfields["where"]];
899
    }
900 901 902
    # A fully bound rspec will not have aggregate settings, but we let
    # the backend deal with it. If the JS code is working, this should
    # never happen by mistake.
903
}
904 905 906 907 908 909
if (count($errors)) {
    SPITFORM($formfields, false, $errors);
    SPITFOOTER();
    return;
}

910 911
#
# SSH keys are now optional for guest users; they just have to
912 913 914 915
# use the web based ssh window.
#
# Backend verifies pubkey and returns error. We first look for a 
# file and then fall back to an inline field.
916
#
917 918 919 920 921 922
if (isset($_FILES['keyfile']) &&
    $_FILES['keyfile']['name'] != "" &&
    $_FILES['keyfile']['name'] != "none") {

    $localfile = $_FILES['keyfile']['tmp_name'];
    $args["sshkey"] = file_get_contents($localfile);
Leigh B Stoller's avatar
Leigh B Stoller committed
923 924 925 926 927
    #
    # The filename will be lost on another trip through the browser.
    # So stick the key into the box.
    #
    $formfields["sshkey"] = $args["sshkey"];
928 929
}
elseif (isset($formfields["sshkey"]) && $formfields["sshkey"] != "") {
930
    $args["sshkey"] = $formfields["sshkey"];
Leigh B Stoller's avatar
Leigh B Stoller committed
931 932 933
}

if (count($errors)) {
934
    SPITFORM($formfields, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
935 936 937
    SPITFOOTER();
    return;
}
938
# Silently ignore the form for a logged in user. 
939 940 941
$args["username"] = ($this_user ? $this_user->uid() : $formfields["username"]);
$args["email"]    = ($this_user ? $this_user->email() : $formfields["email"]);
$args["profile"]  = $formfields["profile"];
Leigh B Stoller's avatar
Leigh B Stoller committed
942 943 944 945 946

#
# See if user exists and is verified. We send email with a code, which
# they have to paste back into a box we add to the form. See above.
#
Leigh B Stoller's avatar
Leigh B Stoller committed
947 948 949 950 951
# We also get here if the user exists, but the browser did not have
# the tokens, as will happen if switching to another browser. We
# force the user to repeat the verification with the same code we
# have stored in the DB.
#
952 953 954
if (!$this_user &&
    (!$geniuser || !isset($_COOKIE['quickvm_authkey']) ||
     $_COOKIE['quickvm_authkey'] != $geniuser->auth_token())) {
Leigh B Stoller's avatar
Leigh B Stoller committed
955 956
    if (isset($stuffing) && $stuffing != "") {
	if (! (isset($verify) && $verify == $stuffing)) {
957
	    SPITFORM($formfields, $stuffing, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
958 959 960
	    SPITFOOTER();
	    return;
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
961 962 963 964 965
	#
	# If this is an existing user and they give us the right code,
	# we can check again for an existing VM and redirect to the
	# status page, like we do above.
	#
966
	if ($geniuser) {
Leigh B Stoller's avatar
Leigh B Stoller committed
967 968
	    $instance = Instance::LookupByCreator($geniuser->uuid());
	    if ($instance && $instance->status() != "terminating") {
969 970 971 972 973
		# Reset this cookie so status page is happy and so we
                # will stop asking.
		setcookie("quickvm_user",
			  $geniuser->uuid(), time() + (24 * 3600 * 30),
			  "/", $TBAUTHDOMAIN, 0);
974 975
		header("Location: status.php?oneonly=1&uuid=" .
		       $instance->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
976 977
		return;
	    }
978 979 980 981 982 983 984 985
            #
            # Watch for too many instances by guest user and redirect
            # to the signup page.
            #
            if (Instance::GuestInstanceCount($geniuser) > $MAXGUESTINSTANCES) {
		header("Location: signup.php?toomany=1");
		return;
            }
Leigh B Stoller's avatar
Leigh B Stoller committed
986
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
987 988 989 990
	# Pass to backend to save in user object.
	$args["auth_token"] = $stuffing;
    }
    else {
Leigh B Stoller's avatar
Leigh B Stoller committed
991 992
	# Existing user, use existing auth token.
	# New user, we create a new one.
993
	$token = ($geniuser ? $geniuser->auth_token() : true);
Leigh B Stoller's avatar
Leigh B Stoller committed
994

995
	SPITFORM($formfields, $token, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
996 997 998 999 1000
	SPITFOOTER();
	return;
    }
}

1001
# Admins can change aggregate.
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
$options = "";

if ($aggregate_urn != "") {
    $options = " -a '$aggregate_urn'";
}
elseif (count($sitemap)) {
    while (list($siteid, $urn) = each($sitemap)) {
        $options .= "--site 'site:${siteid}=${urn}' ";
    }
}
Leigh B Stoller's avatar
Leigh B Stoller committed
1012 1013

#
1014
# Invoke the backend.
Leigh B Stoller's avatar
Leigh B Stoller committed
1015
#
1016 1017
list ($instance, $creator) =
    Instance::Instantiate($this_user, $options, $args, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
1018

Leigh B Stoller's avatar
Leigh B Stoller committed
1019
if (!$instance) {
1020
    SPITFORM($formfields, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
1021 1022 1023
    SPITFOOTER();
    return;
}
1024
    
1025
#
Leigh B Stoller's avatar
Leigh B Stoller committed
1026
# Remember the user and auth key so that we can verify.
1027 1028 1029 1030 1031 1032
#
# The cookie handling is a pain since we run this under the aptlab
# virtual host, but the config uses a different domain, and so the
# cookies do not work. So, we have to look at our SERVER_NAME and
# set the cookie appropriately. 
#
1033
if (!$this_user) {
Leigh B Stoller's avatar
Leigh B Stoller committed
1034 1035
    $cookiedomain = $TBAUTHDOMAIN;