4 require_once 'plc_login.php';
6 // Get session and API handles
7 require_once 'plc_session.php';
8 global $plc, $api, $adm;
11 require_once 'plc_functions.php';
14 $_person= $plc->person;
15 $_roles= $_person['role_ids'];
17 //require 'sirius_func.php';
20 require_once 'plc_drupal.php';
21 drupal_set_title('Sirius');
22 include 'plc_header.php';
26 <h2>Sirius Calendar Service</h2>
28 You can choose to run your experiment at the earliest available
29 time or give a specific time. The number of repetitions has
30 the following meaning: if you want your job re-inserted on the
31 schedule list (in the earliest slot available) directly after it
32 gets a priority increase, you can do so (up to 4 times). Currently,
33 our admission control policy is that we
34 allow only one slice per time slot. <p>
37 time slots are only allocated on the granularity
39 only CPU is increased.
49 define("TOO_MANY_UNITS", 2);
50 define("NO_UNITS_LEFT", 3);
51 define("TIME_ALREADY_OCCURRED", 4);
52 define("NO_SUCH_SLICE", 5);
53 define("NOT_SLICE_OWNER", 6);
54 define("TOO_CLOSE_TO_DEADLINE", 7);
56 define("DELETE_THRESHOLD", 600);
57 define("MAX_JOBS", 1);
59 function authorizeSlice($sn) {
61 $_api = new xmlrpc_client('/PLCAPI/', 'planet-lab.org', 443);
63 $username = $_SESSION['username'];
64 $password = $_SESSION['password'];
68 $_api_auth = new xmlrpcval(array(
69 "AuthMethod" => new xmlrpcval("password"),
70 "Username" => new xmlrpcval($username),
71 "AuthString" => new xmlrpcval($password),
72 "Role" => new xmlrpcval("user")), "struct");
74 $func = new xmlrpcmsg("SliceInfo");
75 $func->addParam($_api_auth);
77 $result = $_api->send($func, 15, 'https');
80 // printf("problem: %s\n", $_api->errstring);
83 else if($result->faultCode() != 0) {
84 // printf("server problem: %s\n", $result->errstr);
88 $result_value = $result->value();
90 if( !$result_value ) {
91 // printf( "didn't get value back from api\n" );
95 $arr = xmlrpc_decode($result_value);
96 // printf( "return value success, value %s\n", $val);
98 $numElements = count($arr);
100 while ($i < $numElements) {
101 if ($sn == $arr[$i][name])
112 $slice_list= array();
113 $result= $api->GetSlices( $slice_list, array( "name" ) );
115 foreach ( $result AS $slice )
117 if ( $slice["name"] == $sn )
126 //can a request be satisfied? Currently, answer is yes unless
127 //either this time is taken or you asked for more than one unit
128 //probably will need to change this.
129 function validateRequest ($units, $timesOccupied, $requestedTime, $currentTime) {
131 return TOO_MANY_UNITS;
133 // buffer so we aren't too close to deadline, if your request is late
134 // OR if it's within 1 minute of deadline, it's too late
135 if ($requestedTime - 60 <= $currentTime)
136 return TIME_ALREADY_OCCURRED;
138 if (array_key_exists($requestedTime, $timesOccupied)) {
139 if ($timesOccupied[$requestedTime] == MAX_JOBS)
145 //can a request be satisfied? Currently, answer is yes unless
146 //either this time is taken or you asked for more than one unit
147 //probably will need to change this.
148 function validateAndMarkRequest ($units, &$timesOccupied, $requestedTime, $currentTime, $sn, $jobArray) {
149 // buffer so we aren't too close to deadline, if your request is late
150 // OR if it's within 1 minute of deadline, it's too late
151 if ($requestedTime - 60 <= $currentTime)
152 return TIME_ALREADY_OCCURRED;
154 if (array_key_exists($requestedTime, $timesOccupied)) {
155 if ($timesOccupied[$requestedTime] == MAX_JOBS)
158 $timesOccupied[$requestedTime]++;
161 $timesOccupied[$requestedTime] = 1;
162 if (array_key_exists($sn, $jobArray)) {
163 $ts = $jobArray[$sn]["timestamp"];
164 $timesOccupied[$ts]--;
169 return TOO_MANY_UNITS;
174 function findNextFreeSlot($units, $timesOccupied) {
175 $currYear = gmdate("y");
176 $currMonth = gmdate("m");
177 $currDate = gmdate("d");
178 $currHour = gmdate("H") + 1;
179 $currentTime = gmmktime();
180 $reqTime = gmmktime($currHour, 0, 0, $currMonth, $currDate, $currYear);
182 while ($retVal != SUCCESS) {
183 $retVal = validateRequest($units, $timesOccupied, $reqTime, $currentTime);
184 if ($retVal == NO_ROOM || $retVal == TIME_ALREADY_OCCURRED) { // advance timestamp one hour (3600 seconds)
185 $reqTime = $reqTime + 3600;
191 function dumpToFile($fileName, $buffer, $which, $timesOccupied) {
192 //open file, and dump newly list into it (buffer is the list)
193 //we're just currently overwriting (fopen with "w" truncates)
195 $fileHandle = fopen($fileName, "w");
196 //periodically, updateSliceUnits program will update the slices file
197 //this function is general, works for schedule or slices file
199 //lock in case of concurrent accesses
200 flock($fileHandle, LOCK_EX);
202 if ($which == "schedule") { // need to write timestamp in this case
204 fwrite($fileHandle, $s[sec]);
205 fwrite($fileHandle, "\n");
209 foreach ($buffer as $value) {
211 if ($which == "schedule") {
212 if (strcmp($value["timestamp"], mktime()) > 0) {
213 $numReps = $value["reps"];
214 $ts = $value["timestamp"];
215 $t = $value["sliceName"]." ".$value["id"]." ".$value["timestamp"]." ".$value["units"]." ".$value["reps"]." \n";
217 else { // job expired, does it need be run again?
218 if ($value["reps"] > 0) {
219 $ts = findNextFreeSlot($value["units"], $timesOccupied);
221 $numReps = $value["reps"] - 1;
224 $t = $value["sliceName"]." ".$value["id"]." ".$ts." ".$value["units"]." ".$numReps." \n";
226 else if ($which == "slices") {
227 $t = $value["sliceName"]." ".$value["units"]." \n";
231 fwrite($fileHandle, $t);
235 flock($fileHandle, LOCK_UN);
240 //update the slice file, takes a slice name (name) and number of units
241 function updateSliceFile($name, $units) {
242 $dummyArray = array();
244 $sliceFile = fopen("/var/www/html/planetlab/sirius/slices.txt", "rw");
246 echo "<p>Unable to open remote file.</p>";
250 flock($sliceFile, LOCK_EX);
252 //we'll construct a new list here, will be current slice file except
253 //the slice in question will have it's units decreased, if there are any...
254 while (!feof($sliceFile)) {
255 $num = fscanf($sliceFile, "%s %d\n", $sliceName, $unitsAvailable);
256 //for some reason feof seems to not quite work
257 //precisely, the last entry in the file is read twice (!?!), so hack here
261 $newArray["sliceName"] = $sliceName;
262 if ($name == $sliceName) {
263 $newUnits = $unitsAvailable - $units;
264 if ($newUnits < 0) // error, slice has no more units
267 $newArray["units"] = $newUnits;
270 $newArray["units"] = $unitsAvailable;
271 //append this tuple to the entire array
272 $sliceArray[] = $newArray;
274 flock($sliceFile, LOCK_UN);
276 //do the dump to new file
277 dumpToFile("/var/www/html/planetlab/sirius/slices.txt", $sliceArray, "slices", dummyArray);
282 //pretty obvious what this does; basically, does the slice exist in
283 //the slice file yet? (New user of calendar service user may not have
285 function isFirstSliceRequest($name) {
286 $sliceFile = fopen("/var/www/html/planetlab/sirius/slices.txt", "r");
288 echo "<p>Unable to open remote file.</p>";
292 flock($sliceFile, LOCK_EX);
294 while (!feof($sliceFile)) {
295 $num = fscanf($sliceFile, "%s %d\n", $sliceName, $unitsAvailable);
296 //for some reason feof seems to not quite work
297 //precisely, the last entry in the file is read twice (!?!), so hack here
301 if ($name == $sliceName) {
302 flock($sliceFile, LOCK_UN);
308 flock($sliceFile, LOCK_UN);
314 function cmp ($a, $b) {
315 if ($a["timestamp"] == $b["timestamp"])
318 return ($a["timestamp"] < $b["timestamp"]) ? -1 : 1;
321 function checkForErrors($requestStatus) {
322 if ($requestStatus == NO_ROOM) {
323 printf("<b> Error: Cannot add your request; that time slot is currently full. </b> <p>");
325 else if ($requestStatus == TOO_MANY_UNITS) {
326 printf("<b> Error: Cannot add your request; only 1 extra unit is allowed.</b> <p>");
328 else if ($requestStatus == NO_UNITS_LEFT) {
329 printf("<b> Error: Cannot add your request; no more units remaining.</b> <p>");
331 else if ($requestStatus == TIME_ALREADY_OCCURRED) {
332 printf("<b> Error: Cannot add your request; that time has already occurred, or is too close to the current time.</b> <p>");
334 else if ($requestStatus == NO_SUCH_SLICE) {
335 printf("<b> Error: Cannot delete nonexistent slice.</b> <p>");
337 else if ($requestStatus == NOT_SLICE_OWNER) {
338 printf("<b> Error: Only authorized user can manipulate slice.</b> <p>");
340 else if ($requestStatus == TOO_CLOSE_TO_DEADLINE) {
341 printf("<b> Error: Cannot delete your request; it is too close to
342 the time that your slice will receive its priority increase.</b> <p>");
346 function getCurrentSchedule (&$jobArray, &$timesOccupied, &$maxId) {
348 $schedFile = fopen("/var/www/html/planetlab/sirius/schedule.txt", "r");
350 echo "<p>Unable to open remote file.</p>";
354 flock($schedFile, LOCK_EX);
356 //first line is timestamp, throw it away.
357 fscanf($schedFile, "%s\n", $str);
359 //read in current file into array
362 $timesOccupied = array();
364 while (!feof($schedFile)) {
365 $num = fscanf($schedFile, "%s %d %s %d %d\n", $sliceName, $id, $timestamp, $units, $reps);
370 //for some reason feof seems to not quite work
371 //precisely, the last entry in the file is read twice (!?!), so hack here
374 $newArray["sliceName"] = $sliceName;
375 $newArray["id"] = $id;
376 $newArray["units"] = $units;
377 $newArray["timestamp"] = $timestamp;
378 $newArray["reps"] = $reps;
379 $jobArray[$sliceName] = $newArray;
381 if (array_key_exists($timestamp, $timesOccupied)) {
382 $timesOccupied[$timestamp]++;
385 $timesOccupied[$timestamp] = 1;
390 flock($schedFile, LOCK_UN);
395 // Reid: after below function call, you have the current schedule.
396 // It is stored in $jobArray, which is an array of arrays.
397 // Layout: each element of $jobArray is an array with
398 // the following fields.
399 // "sliceName": the name of the slice that occupies the slot
400 // "id": the id of the slice, currently not used; do not display
401 // "units": another field that will be used eventually...but not yet
402 // "reps": indicates how many repetitions slice has specified; for
403 // each repetition, the slice is automatically rescheduled
404 // after running for the earliest available slot
405 // I don't know if you want to pring the schedule out as part of the
406 // queue here, or wait to see if there was a submitted job.
407 // See my comments below for more details.
409 getCurrentSchedule ($jobArray, $timesOccupied, $maxId);
413 // Reid: here, we see if a new request is submitted (which would
414 // be done now as: did the user click into the queue and
415 // select a slice to get the next slot. I think you can just
416 // skip to the end of this if statement (see comment below)
418 // Reid: The problem here is, this is based on the current submission
419 // procedure, which is: click the submit button (or the delete
420 // button). I'm not sure how to change or modularize this
421 // function because I don't know how the queue would precisely
422 // be implemented. I've commented the code below to try to help.
424 //if form was submitted with new job, process it
425 if (isset($_POST['action']) && $_POST['action'] == 'submitted') {
426 $sname = $_POST['sliceName'];
428 if (!authorizeSlice($sname)) {
429 $requestStatus = NOT_SLICE_OWNER;
431 else if ($_POST['add_delete'] == "delete") {
432 // delete request. Make sure it exists and is early enough to delete,
434 if (array_key_exists($sname, $jobArray)) {
436 $ts = $jobArray[$sname]["timestamp"];
437 if ($ts - mktime() < DELETE_THRESHOLD)
438 $requestStatus = TOO_CLOSE_TO_DEADLINE;
440 $timesOccupied[$ts]--;
441 unset($jobArray[$sname]);
445 $requestStatus = NO_SUCH_SLICE;
449 // it's an add request
450 // grab all the data from the user.
452 // $minute = $_POST['minute'];
453 $reps = $_POST['reps'];
454 // $u = $_POST['units'];
457 $currentTime = mktime();
459 if ($_POST['whenToRun'] == "asap") {
460 $requestedTime = findNextFreeSlot($u, $timesOccupied);
463 // $requestedTime = gmmktime($hour, $minute, 0, $month, $date, $year);
464 if (!isset( $_POST['queue_time'] )) {
465 $year = $_POST['year'];
466 $month = $_POST['month'];
467 $date = $_POST['date'];
468 $hour = $_POST['hour'];
469 $requestedTime = gmmktime($hour, 0, 0, $month, $date, $year);
472 $currYear = gmdate("y");
473 $currMonth = gmdate("m");
474 $currDate = gmdate("d");
475 $currHour = gmdate("H");
476 $hour = $_POST['queue_time'];
477 if ($hour < $currHour) {
478 $requestedTime = gmmktime($hour, 0, 0, $currMonth, $currDate+1, $currYear);
481 $requestedTime = gmmktime($hour, 0, 0, $currMonth, $currDate, $currYear);
487 $requestStatus = validateAndMarkRequest($u, $timesOccupied, $requestedTime, $currentTime, $sname, $jobArray);
488 if ($requestStatus == SUCCESS) {
489 // ignore below, it is for future work anyways.
490 if (isFirstSliceRequest($sname)) {
491 $sliceFile = fopen("/var/www/html/planetlab/sirius/slices.txt", "a");
492 if ($sliceFile == 0) {
493 echo "<p>Unable to open file.</p>";
496 flock($sliceFile, LOCK_EX);
498 // should be max number of units, not 5
500 fwrite($sliceFile, $sname." "."6");
501 flock($sliceFile, LOCK_UN);
504 // if (updateSliceFile($sname, 1) < 0)
505 // temporarily not looking at units...
507 $requestStatus = NO_UNITS_LEFT;
509 // here, pretty simple, just stick all data into
510 // array element, then stick array into $jobArray.
511 $newArray["sliceName"] = $sname;
512 $newArray["id"] = $id;
513 $newArray["timestamp"] = $requestedTime;
514 $newArray["units"] = $u;
515 $newArray["reps"] = $reps;
516 $jobArray[$sname] = $newArray;
520 // header("Location: planetcal.php");
523 //sort job array by earliest time first ("cmp" function does this)
524 usort($jobArray, "cmp");
526 // Reid: after this above line, $jobArray holds a sorted list that
527 // you can output as the queue.
529 // Reid: below is the current printing of the schedule, which would
530 // certainly be deleted when you have the better representation
531 // of the schedule (the visual queue). It starts here and ends
532 // where I've marked below.
534 //print current job list as table on screen
535 printf("<table cellspacing=0 cellpadding=2>");
536 if (count($jobArray) > 0) {
538 printf("<th style='border: 1px black solid'> Slice name </th> <th style='border: 1px black solid'> Repetitions </th><th style='border: 1px black solid'> Time of priority </th>");
543 printf("<td>No jobs currently on queue </td><td></td><td></td>");
547 $deletedExpiredJob = 0;
551 foreach ($jobArray as $value) {
552 if (strcmp($value["timestamp"], mktime()) > 0) {
554 printf("<td> %s </td><td align=center> %d </td><td> %s </td>\n", $value["sliceName"], $value["reps"], gmdate("r", $value["timestamp"]));
555 $arr[$n]= $value["sliceName"];
560 $deletedExpiredJob = 1;
563 printf("</table>\n");
565 // Reid: end of current printing of the schedule.
567 // Reid: here is where we put the data back to the schedule file.
568 // It's already a function,
570 function findNextQueue($units, $timesOccupied, $arr) {
572 $currYear = gmdate("y");
573 $currMonth = gmdate("m");
574 $currDate = gmdate("d");
575 $currHour = gmdate("H") + 1;
576 $currentTime = gmmktime();
577 $reqTime = gmmktime($currHour, 0, 0, $currMonth, $currDate, $currYear);
583 // outputting table to display the queue
584 // green background will mean slot is open, and red will mean the slot is used
586 echo "<table cellspacing=\"2\" cellpadding=\"1\" border=\"0\" width=550>\n";
587 echo "<tr><td colspan=\"3\"><strong>24 hour Queue:</strong> Choose the GMT time slot you desire (<font color=\"#339933\">green</font> slots are open, <font color=\"#CC3333\">red</font> are taken) <p></td></tr>\n";
588 echo "<tr><td width=\"47%\" align=\"right\"><table cellspacing=1 cellpadding=1 border=0 width=130>\n";
590 // here's what this does below: it goes through each hour, and sees if the slot is occupied
591 // if so, it outputs in red, w/ slice name ($arr[$x], where $x is the number request, i.e.
592 // earlier when we dump out the list of slices on the schedule, we do $arr[$x++] = $slicename
594 // while ($reqTime < ( $reqTime + ( 24 * 3600 ) ) ) {
596 $retVal = validateRequest($units, $timesOccupied, $reqTime, $currentTime);
597 if ($retVal == SUCCESS) { // advance timestamp one hour (3600 seconds)
599 echo "<tr bgcolor=\"#339933\"><td><input type=\"radio\" name=\"queue_time\" value=\"" . gmdate("H:i:s", $reqTime) . "\"> " . gmdate("H:i:s", $reqTime) . " </td></tr>\n";
602 echo"<tr bgcolor=\"#CC3333\"><td align=center> " . $arr[$x] . " </td></tr>\n";
606 $reqTime = $reqTime + 3600;
609 echo "</table></td><td width=\"6%\"> </td><td><table cellspacing=1 cellpadding=1 border=0 width=130>\n";
611 while ($i < 24 && $i > 11) {
612 $retVal = validateRequest($units, $timesOccupied, $reqTime, $currentTime);
613 if ($retVal == SUCCESS) { // advance timestamp one hour (3600 seconds)
615 echo "<tr bgcolor=\"#339933\"><td><input type=\"radio\" name=\"queue_time\" value=\"" . gmdate("H:i:s", $reqTime) . "\"> " . gmdate("H:i:s", $reqTime) . " </td></tr>\n"; }
617 echo"<tr bgcolor=\"#CC3333\"><td align=center> " . $arr[$x] . " </td></tr>\n";
621 $reqTime = $reqTime + 3600;
624 echo "</table></td></tr>\n";
630 function sliceDropDown() {
632 $_api = new xmlrpc_client('/PLCAPI/', 'planet-lab.org', 443);
634 $username = $_SESSION['username'];
635 $password = $_SESSION['password'];
639 $_api_auth = new xmlrpcval(array(
640 "AuthMethod" => new xmlrpcval("password"),
641 "Username" => new xmlrpcval($username),
642 "AuthString" => new xmlrpcval($password),
643 "Role" => new xmlrpcval("user")), "struct");
645 $func = new xmlrpcmsg("SliceInfo");
646 $func->addParam($_api_auth);
648 $result = $_api->send($func, 15, 'https');
651 // printf("problem: %s\n", $_api->errstring);
654 else if($result->faultCode() != 0) {
655 // printf("server problem: %s\n", $result->errstr);
659 $result_value = $result->value();
661 if( !$result_value ) {
662 // printf( "didn't get value back from api\n" );
666 $arr = xmlrpc_decode($result_value);
667 // printf( "return value success, value %s\n", $val);
669 $numElements = count($arr);
671 while ($i < $numElements) {
672 echo "<option value='" . $arr[$i][name] . "'>" . $arr[$i][name] . "</option>\n";
682 $slice_list= array();
683 $result= $api->GetSlices( Null, array( "name" ) );
685 // sort_slices( $result ); --> slice sort on name
686 function __cmp_slices($a, $b) {
687 return strcasecmp($a['name'], $b['name']);
689 usort($result, '__cmp_slices');
691 foreach ( $result AS $slice )
693 echo "<option value='" . $slice["name"] . "'>" . $slice["name"] . "\n";
698 //reopen schedule file, and dump newly sorted job list into it
699 //note that current timestamp is put in at beginning
700 //note also: only do this dump if a change has been made
702 if ($deletedExpiredJob || ($changeMade && $requestStatus == SUCCESS)) {
703 dumpToFile("/var/www/html/planetlab/sirius/schedule.txt", $jobArray, "schedule", $timesOccupied);
705 // hack here...the problem is that the file might not be sorted
706 // when it should, because of the stupid way it was designed. this
707 // happens when reps is not 0, and the next entry should go after
708 // another entry. what does happen is that it goes before, which is
709 // fine for displaying, but the sirius service code expects it to
710 // always be sorted, "it" being the schedule file
712 $hackArray = array();
713 getCurrentSchedule ($hackArray, $timesOccupied, $maxId);
714 usort($hackArray, "cmp");
715 dumpToFile("/var/www/html/planetlab/sirius/schedule.txt", $hackArray, "schedule", $timesOccupied);
718 checkForErrors($requestStatus);
725 <form action="/db/sirius/index.php" method="post">
726 <p>Choose your slice name:
727 <select name="sliceName">
728 <?php sliceDropDown(); ?>
732 Either Add a new time slot or remove a previously taken slot:
734 <b>Add</b> <input type=radio name="add_delete" value="add" checked/>
735 <b>Delete</b> <input type=radio name="add_delete" value="delete" />
737 ASAP will just select the next availible time, choose specific time if you want to specify a slot in the queue:
739 <b>ASAP</b> <input type=radio name="whenToRun" value="asap" checked/>
740 <b>Specific Time</b> <input type=radio name="whenToRun" value="specific" />
742 Choose a number of times you need CPU priority:
744 <b>Number of Repetitions</b>:
745 0 <input type=radio name="reps" value="0" checked/>
746 1 <input type=radio name="reps" value="1"/>
747 2 <input type=radio name="reps" value="2"/>
748 3 <input type=radio name="reps" value="3"/>
749 4 <input type=radio name="reps" value="4"/>
753 <?php findNextQueue( 1, $timesOccupied, $arr ); ?>
756 Only enter a time/date here if your request is for a time more than 24 hours from now.<br>
757 Year (two digits) <input type=text maxlength=2 size=2 name="year"/>
758 Month (1-12) <input type=text maxlength=2 size=2 name="month"/>
759 Date (1-31) <input type=text maxlength=2 size=2 name="date"/>
760 Hour (0-23) <input type=text maxlength=2 size=2 name="hour"/>
762 <!--Units <input type=text maxlength=2 size=2 name="units"/>-->
764 <input type="hidden" name="action" value="submitted" />
765 <input type="submit" name="submit" value="Submit" />
766 <input type="reset" name="reset" value="Reset" />