function XMLRPCprocessBlockTime($blockTimesid, $ignoreprivileges = 0) { global $requestInfo, $user, $xmlrpcBlockAPIUsers; if (!in_array($user['id'], $xmlrpcBlockAPIUsers)) { return array('status' => 'error', 'errorcode' => 34, 'errormsg' => 'access denied for managing block allocations'); } $return = array('status' => 'success'); $query = "SELECT bt.start, " . "bt.end, " . "br.imageid, " . "br.numMachines, " . "br.groupid, " . "br.expireTime " . "FROM blockRequest br, " . "blockTimes bt " . "WHERE bt.blockRequestid = br.id AND " . "bt.id = {$blockTimesid}"; $qh = doQuery($query, 101); if (!($rqdata = mysql_fetch_assoc($qh))) { return array('status' => 'error', 'errorcode' => 8, 'errormsg' => 'unknown blockTimesid'); } if (datetimeToUnix($rqdata['expireTime']) < time()) { return array('status' => 'error', 'errorcode' => 9, 'errormsg' => 'expired block reservation'); } $images = getImages(0, $rqdata['imageid']); if (empty($images)) { return array('status' => 'error', 'errorcode' => 10, 'errormsg' => 'invalid image associated with block request'); } # check to see if all computers have been allocated $query = "SELECT COUNT(computerid) AS allocated " . "FROM blockComputers " . "WHERE blockTimeid = {$blockTimesid}"; $qh = doQuery($query, 101); if (!($row = mysql_fetch_assoc($qh))) { return array('status' => 'error', 'errorcode' => 15, 'errormsg' => 'failure to communicate with database'); } $compCompleted = $row['allocated']; $compsPerRequest = 1 + count($images[$rqdata['imageid']]['subimages']); $toallocate = $rqdata['numMachines'] * $compsPerRequest - $compCompleted; if ($toallocate == 0) { return array('status' => 'completed'); } $reqToAlloc = $toallocate / $compsPerRequest; if (!$ignoreprivileges) { # get userids in user group $tmp = getUserGroupMembers($rqdata['groupid']); if (empty($tmp)) { return array('status' => 'error', 'errorcode' => 11, 'errormsg' => 'empty user group and ignoreprivileges set to 0'); } $userids = array_keys($tmp); # make length of $userids match $reqToAlloc by duplicating or trimming some users while ($reqToAlloc > count($userids)) { $userids = array_merge($userids, $userids); } if ($reqToAlloc < count($userids)) { $userids = array_splice($userids, 0, $reqToAlloc); } } # staggering: stagger start times for this round (ie, don't worry about # previous processing of this block time) such that there is 1 minute # between the start times for each request $stagExtra = $reqToAlloc * 60; # determine estimated load time $imgLoadTime = getImageLoadEstimate($rqdata['imageid']); if ($imgLoadTime == 0) { $imgLoadTime = $images[$rqdata['imageid']]['reloadtime'] * 60; } $loadtime = $imgLoadTime + 10 * 60; # add 10 minute fudge factor $unixstart = datetimeToUnix($rqdata['start']); if (time() + $loadtime + $stagExtra > $unixstart) { $return['status'] = 'warning'; $return['warningcode'] = 13; $return['warningmsg'] = 'possibly insufficient time to load machines'; } $start = unixToDatetime($unixstart - $loadtime); $unixend = datetimeToUnix($rqdata['end']); $userid = 0; $allocated = 0; $vclreloadid = getUserlistID('vclreload@Local'); $revisionid = getProductionRevisionid($rqdata['imageid']); $blockCompVals = array(); # FIXME (maybe) - if some subset of users in the user group have available # computers, but others do not, $allocated will be less than the desired # number of machines; however, calling this function enough times will # result in enough machines being allocated because they will continue to be # allocated based on the ones with machines available; this seems like odd # behavior $stagCnt = 0; for ($i = 0; $i < $reqToAlloc; $i++) { $stagunixstart = $unixstart - $loadtime - $stagCnt * 60; $stagstart = unixToDatetime($stagunixstart); if (!$ignoreprivileges) { $userid = array_pop($userids); } # use end of block time to find available computers, but... $rc = isAvailable($images, $rqdata['imageid'], $stagunixstart, $unixend, 0, 0, $userid, $ignoreprivileges); if ($rc < 1) { continue; } $compid = $requestInfo['computers'][0]; # ...use start of block time as end of reload reservation $reqid = simpleAddRequest($compid, $rqdata['imageid'], $revisionid, $stagstart, $rqdata['start'], 19, $vclreloadid); if ($reqid == 0) { continue; } $stagCnt++; $allocated++; $blockCompVals[] = "({$blockTimesid}, {$compid}, {$rqdata['imageid']})"; # process any subimages for ($key = 1; $key < count($requestInfo['computers']); $key++) { $subimageid = $requestInfo['images'][$key]; $subrevid = getProductionRevisionid($subimageid); $compid = $requestInfo['computers'][$key]; $mgmtnodeid = $requestInfo['mgmtnodes'][$key]; $blockCompVals[] = "({$blockTimesid}, {$compid}, {$subimageid})"; $query = "INSERT INTO reservation " . "(requestid, " . "computerid, " . "imageid, " . "imagerevisionid, " . "managementnodeid) " . "VALUES " . "({$reqid}, " . "{$compid}, " . "{$subimageid}, " . "{$subrevid}, " . "{$mgmtnodeid})"; doQuery($query, 101); } semUnlock(); $blockComps = implode(',', $blockCompVals); $query = "INSERT INTO blockComputers " . "(blockTimeid, computerid, imageid) " . "VALUES {$blockComps}"; doQuery($query, 101); $blockCompVals = array(); } if ($allocated == 0) { $return['status'] = 'warning'; $return['warningcode'] = 14; $return['warningmsg'] = 'unable to allocate any machines'; } $return['allocated'] = $compCompleted / $compsPerRequest + $allocated; $return['unallocated'] = $rqdata['numMachines'] - $return['allocated']; return $return; }
function detailStatusHTML($reqid) { $requests = getUserRequests("all"); $found = 0; foreach ($requests as $request) { if ($request['id'] == $reqid) { $found = 1; break; } } if (!$found) { $text = i("The selected reservation is no longer available. You can request a new reservation or select another one that is available."); return $text; } if ($request['currstateid'] == 11 || $request['currstateid'] == 12 && $request['laststateid'] == 11) { return "<br><span class=\"rederrormsg\">" . i("The selected reservation has timed out and is no longer available.") . "</span>"; } if ($request['imageid'] == $request['compimageid']) { $nowreq = 1; } else { $nowreq = 0; } $flow = getCompStateFlow($request['computerid']); # cluster reservations not supported here yet # info on reboots/reinstalls not available yet if (empty($flow) || count($request['reservations']) > 0 || $request['currstateid'] == 14 && $request['laststateid'] == 26 || $request['currstateid'] == 14 && $request['laststateid'] == 24 || $request['currstateid'] == 14 && $request['laststateid'] == 28) { $noinfo = i("No detailed loading information is available for this reservation."); return $noinfo; } $logdata = getCompLoadLog($request['resid']); # determine an estimated load time for the image $imgLoadTime = getImageLoadEstimate($request['imageid']); if ($imgLoadTime == 0) { $images = getImages(0, $request['imageid']); $imgLoadTime = $images[$request['imageid']]['reloadtime'] * 60; } $time = 0; $now = time(); $text = "<table summary=\"displays a list of states the reservation must " . "go through to become ready and how long each state will take or " . "has already taken\" id=resStatusTable>"; $text .= "<colgroup>"; $text .= "<col class=resStatusColState />"; $text .= "<col class=resStatusColEst />"; $text .= "<col class=resStatusColTotal />"; $text .= "</colgroup>"; $text .= "<tr>"; $text .= "<th align=right><br>" . i("State") . "</th>"; $text .= "<th>Est/Act<br>" . i("Time") . "</th>"; $text .= "<th>Total<br>" . i("Time") . "</th>"; $text .= "</tr>"; $slash = "<font color=black>/</font>"; $total = 0; $id = ""; $last = array(); $logstateids = array(); $skippedstates = array(); # loop through all states in the log data foreach ($logdata as $data) { # keep track of the states in the log data array_push($logstateids, $data['loadstateid']); # keep track of any skipped states if (!empty($last) && $last['loadstateid'] != $flow['repeatid'] && $data['loadstateid'] != $flow['data'][$last['loadstateid']]['nextstateid']) { array_push($skippedstates, $flow['data'][$last['loadstateid']]['nextstateid']); } // if we reach a repeat state, include a message about having to go back if ($data['loadstateid'] == $flow['repeatid']) { if (empty($id)) { return $noinfo; } $text .= "<tr>"; $text .= "<td colspan=3><hr>" . i("problem at state"); $text .= " \"{$flow['data'][$id]['nextstate']}\""; $query = "SELECT additionalinfo " . "FROM computerloadlog " . "WHERE loadstateid = {$flow['repeatid']} AND " . "reservationid = {$request['resid']} AND " . "timestamp = '" . unixToDatetime($data['ts']) . "'"; $qh = doQuery($query, 101); if ($row = mysql_fetch_assoc($qh)) { $reason = $row['additionalinfo']; $text .= "<br>" . i("retrying at state") . " \"{$reason}\""; } $text .= "<hr></td></tr>"; $total += $data['time']; $last = $data; continue; } $id = $data['loadstateid']; // if in post config state, compute estimated time for the state if ($flow['data'][$id]['statename'] == 'loadimagecomplete') { $addtime = 0; foreach ($skippedstates as $stateid) { $addtime += $flow['data'][$stateid]['statetime']; } # this state's time is (avg image load time - all other states time + # state time for any skipped states) $tmp = $imgLoadTime - $flow['totaltime'] + $addtime; if ($tmp < 0) { $flow['data'][$id]['statetime'] = 0; } else { $flow['data'][$id]['statetime'] = $tmp; } } $total += $data['time']; $text .= "<tr>"; $text .= "<td nowrap align=right><font color=green>"; $text .= i($flow['data'][$id]['state']) . "({$id})</font></td>"; $text .= "<td nowrap align=center><font color=green>"; $text .= secToMinSec($flow['data'][$id]['statetime']) . $slash; $text .= secToMinSec($data['time']) . "</font></td>"; $text .= "<td nowrap align=center><font color=green>"; $text .= secToMinSec($total) . "</font></td>"; $text .= "</tr>"; $last = $data; } # $id will be set if there was log data, use the first state in the flow // if it isn't set if (!empty($id)) { $id = $flow['nextstates'][$id]; } else { $id = $flow['stateids'][0]; } # determine any skipped states $matchingstates = array(); foreach ($flow['stateids'] as $stateid) { if ($stateid == $id) { break; } array_push($matchingstates, $stateid); } $skippedstates = array_diff($matchingstates, $logstateids); $addtime = 0; foreach ($skippedstates as $stateid) { $addtime += $flow['data'][$stateid]['statetime']; } $first = 1; $count = 0; # loop through the states in the flow that haven't been reached yet # $count is included to protect against an infinite loop while (!is_null($id) && $count < 100) { $count++; // if in post config state, compute estimated time for the state if ($flow['data'][$id]['statename'] == 'loadimagecomplete') { # this state's time is (avg image load time - all other states time + # state time for any skipped states) $tmp = $imgLoadTime - $flow['totaltime'] + $addtime; if ($tmp < 0) { $flow['data'][$id]['statetime'] = 0; } else { $flow['data'][$id]['statetime'] = $tmp; } } // if first time through this loop, this is the current state if ($first) { // if request has failed, it was during this state, get reason if ($request['currstateid'] == 5) { $query = "SELECT additionalInfo, " . "UNIX_TIMESTAMP(timestamp) AS ts " . "FROM computerloadlog " . "WHERE loadstateid = (SELECT id " . "FROM computerloadstate " . "WHERE loadstatename = 'failed') AND " . "reservationid = {$request['resid']} " . "ORDER BY id " . "LIMIT 1"; $qh = doQuery($query, 101); if ($row = mysql_fetch_assoc($qh)) { $reason = $row['additionalInfo']; if (!empty($data)) { $currtime = $row['ts'] - $data['ts']; } else { $currtime = $row['ts'] - datetimeToUnix($request['daterequested']); } } else { $text = i("No detailed information is available for this reservation."); return $text; } $text .= "<tr>"; $text .= "<td nowrap align=right><font color=red>"; $text .= i($flow['data'][$id]['state']) . "({$id})</font></td>"; $text .= "<td nowrap align=center><font color=red>"; $text .= secToMinSec($flow['data'][$id]['statetime']); $text .= $slash . secToMinSec($currtime) . "</font></td>"; $text .= "<td nowrap align=center><font color=red>"; $text .= secToMinSec($total + $currtime) . "</font></td>"; $text .= "</tr>"; $text .= "</table>"; if (strlen($reason)) { $text .= "<br><font color=red>" . i("failed:") . " {$reason}</font>"; } return $text; } else { if (!empty($data)) { $currtime = $now - $data['ts']; } else { $currtime = $now - datetimeToUnix($request['daterequested']); } $text .= "<td nowrap align=right><font color=#CC8500>"; $text .= i($flow['data'][$id]['state']) . "({$id})</font></td>"; $text .= "<td nowrap align=center><font color=#CC8500>"; $text .= secToMinSec($flow['data'][$id]['statetime']); $text .= $slash . secToMinSec($currtime) . "</font></td>"; $text .= "<td nowrap align=center><font color=#CC8500>"; $text .= secToMinSec($total + $currtime) . "</font></td>"; $text .= "</tr>"; $first = 0; } } else { $text .= "<td nowrap align=right>"; $text .= i($flow['data'][$id]['state']) . "({$id})</td>"; $text .= "<td nowrap align=center>"; $text .= secToMinSec($flow['data'][$id]['statetime']) . "</td>"; $text .= "<td></td>"; $text .= "</tr>"; } $id = $flow['nextstates'][$id]; } $text .= "</table>"; return $text; }
function XMLRPCprocessBlockTime($blockTimesid, $ignoreprivileges = 0) { global $requestInfo, $user, $xmlrpcBlockAPIUsers; if (!in_array($user['id'], $xmlrpcBlockAPIUsers)) { return array('status' => 'error', 'errorcode' => 34, 'errormsg' => 'access denied for managing block allocations'); } # validate $blockTimesid if (!is_numeric($blockTimesid)) { return array('status' => 'error', 'errorcode' => 77, 'errormsg' => 'Invalid blockTimesid specified'); } # validate ignoreprivileges if (!is_numeric($ignoreprivileges) || $ignoreprivileges < 0 || $ignoreprivileges > 1) { return array('status' => 'error', 'errorcode' => 86, 'errormsg' => 'ignoreprivileges must be 0 or 1'); } $return = array('status' => 'success'); $query = "SELECT bt.start, " . "bt.end, " . "br.imageid, " . "br.numMachines, " . "br.groupid, " . "br.expireTime " . "FROM blockRequest br, " . "blockTimes bt " . "WHERE bt.blockRequestid = br.id AND " . "bt.id = {$blockTimesid}"; $qh = doQuery($query, 101); if (!($rqdata = mysql_fetch_assoc($qh))) { return array('status' => 'error', 'errorcode' => 8, 'errormsg' => 'unknown blockTimesid'); } if (datetimeToUnix($rqdata['expireTime']) < time()) { return array('status' => 'error', 'errorcode' => 9, 'errormsg' => 'expired block allocation'); } $images = getImages(0, $rqdata['imageid']); if (empty($images)) { return array('status' => 'error', 'errorcode' => 10, 'errormsg' => 'invalid image associated with block allocation'); } $unixstart = datetimeToUnix($rqdata['start']); $unixend = datetimeToUnix($rqdata['end']); $revisionid = getProductionRevisionid($rqdata['imageid']); $imgLoadTime = getImageLoadEstimate($rqdata['imageid']); if ($imgLoadTime == 0) { $imgLoadTime = $images[$rqdata['imageid']]['reloadtime'] * 60; } $vclreloadid = getUserlistID('vclreload@Local'); $groupmembers = getUserGroupMembers($rqdata['groupid']); $userids = array_keys($groupmembers); # add any computers from future reservations users in the group made if (!empty($groupmembers)) { ## find reservations by users $allids = implode(',', $userids); $query = "SELECT rq.id AS reqid, " . "UNIX_TIMESTAMP(rq.start) AS start, " . "rq.userid " . "FROM request rq, " . "reservation rs " . "WHERE rs.requestid = rq.id AND " . "rq.userid IN ({$allids}) AND " . "rq.start < '{$rqdata['end']}' AND " . "rq.end > '{$rqdata['start']}' AND " . "rs.imageid = {$rqdata['imageid']} AND " . "rs.computerid NOT IN (SELECT computerid " . "FROM blockComputers " . "WHERE blockTimeid = {$blockTimesid})"; $qh = doQuery($query); $donereqids = array(); $blockCompVals = array(); $checkstartbase = $unixstart - $imgLoadTime - 300; $reloadstartbase = unixToDatetime($checkstartbase); $rows = mysql_num_rows($qh); while ($row = mysql_fetch_assoc($qh)) { if (array_key_exists($row['reqid'], $donereqids)) { continue; } $donereqids[$row['reqid']] = 1; if ($row['start'] < datetimeToUnix($rqdata['start'])) { $checkstart = $row['start'] - $imgLoadTime - 300; $reloadstart = unixToDatetime($checkstart); $reloadend = unixToDatetime($row['start']); } else { $checkstart = $checkstartbase; $reloadstart = $reloadstartbase; $reloadend = $rqdata['start']; } # check to see if computer is available for whole block $rc = isAvailable($images, $rqdata['imageid'], $revisionid, $checkstart, $unixend, 1, $row['reqid'], $row['userid'], $ignoreprivileges, 0, '', '', 1); // if not available for whole block, just skip this one if ($rc < 1) { continue; } $compid = $requestInfo['computers'][0]; # create reload reservation $reqid = simpleAddRequest($compid, $rqdata['imageid'], $revisionid, $reloadstart, $reloadend, 19, $vclreloadid); if ($reqid == 0) { continue; } # add to blockComputers $blockCompVals[] = "({$blockTimesid}, {$compid}, {$rqdata['imageid']}, {$reqid})"; # process any subimages for ($key = 1; $key < count($requestInfo['computers']); $key++) { $subimageid = $requestInfo['images'][$key]; $subrevid = getProductionRevisionid($subimageid); $compid = $requestInfo['computers'][$key]; $mgmtnodeid = $requestInfo['mgmtnodes'][$key]; $blockCompVals[] = "({$blockTimesid}, {$compid}, {$subimageid}, {$reqid})"; $query = "INSERT INTO reservation " . "(requestid, " . "computerid, " . "imageid, " . "imagerevisionid, " . "managementnodeid) " . "VALUES " . "({$reqid}, " . "{$compid}, " . "{$subimageid}, " . "{$subrevid}, " . "{$mgmtnodeid})"; doQuery($query, 101); } } if (count($blockCompVals)) { $blockComps = implode(',', $blockCompVals); $query = "INSERT INTO blockComputers " . "(blockTimeid, computerid, imageid, reloadrequestid) " . "VALUES {$blockComps}"; doQuery($query); } cleanSemaphore(); } # check to see if all computers have been allocated $query = "SELECT COUNT(computerid) AS allocated " . "FROM blockComputers " . "WHERE blockTimeid = {$blockTimesid}"; $qh = doQuery($query, 101); if (!($row = mysql_fetch_assoc($qh))) { return array('status' => 'error', 'errorcode' => 15, 'errormsg' => 'failure to communicate with database'); } $compCompleted = $row['allocated']; if (array_key_exists('subimages', $images[$rqdata['imageid']])) { $compsPerAlloc = 1 + count($images[$rqdata['imageid']]['subimages']); } else { $compsPerAlloc = 1; } $toallocate = $rqdata['numMachines'] * $compsPerAlloc - $compCompleted; if ($toallocate == 0) { if (count($blockCompVals)) { return array('status' => 'success', 'allocated' => $rqdata['numMachines'], 'unallocated' => 0); } return array('status' => 'completed'); } $reqToAlloc = $toallocate / $compsPerAlloc; if (!$ignoreprivileges) { # get userids in user group if (empty($groupmembers)) { return array('status' => 'error', 'errorcode' => 11, 'errormsg' => 'empty user group and ignoreprivileges set to 0'); } # make length of $userids match $reqToAlloc by duplicating or trimming some users while ($reqToAlloc > count($userids)) { $userids = array_merge($userids, $userids); } if ($reqToAlloc < count($userids)) { $userids = array_splice($userids, 0, $reqToAlloc); } } # staggering: stagger start times for this round (ie, do not worry about # previous processing of this block time) such that there is 1 minute # between the start times for each allocation $stagExtra = $reqToAlloc * 60; # determine estimated load time $loadtime = $imgLoadTime + 10 * 60; # add 10 minute fudge factor if (time() + $loadtime + $stagExtra > $unixstart) { $return['status'] = 'warning'; $return['warningcode'] = 13; $return['warningmsg'] = 'possibly insufficient time to load machines'; } $start = unixToDatetime($unixstart - $loadtime); $userid = 0; $allocated = 0; $blockCompVals = array(); # FIXME (maybe) - if some subset of users in the user group have available # computers, but others do not, $allocated will be less than the desired # number of machines; however, calling this function enough times will # result in enough machines being allocated because they will continue to be # allocated based on the ones with machines available; this seems like odd # behavior $stagCnt = 0; $stagTime = 60; # stagger reload reservations by 1 min if ($imgLoadTime > 840) { // if estimated load time is > 14 min $stagTime = 120; } # stagger reload reservations by 2 min for ($i = 0; $i < $reqToAlloc; $i++) { $stagunixstart = $unixstart - $loadtime - $stagCnt * $stagTime; $stagstart = unixToDatetime($stagunixstart); if (!$ignoreprivileges) { $userid = array_pop($userids); } # use end of block time to find available computers, but... $rc = isAvailable($images, $rqdata['imageid'], $revisionid, $stagunixstart, $unixend, 1, 0, $userid, $ignoreprivileges); if ($rc < 1) { continue; } $compid = $requestInfo['computers'][0]; # ...use start of block time as end of reload reservation $reqid = simpleAddRequest($compid, $rqdata['imageid'], $revisionid, $stagstart, $rqdata['start'], 19, $vclreloadid); if ($reqid == 0) { continue; } $stagCnt++; $allocated++; $blockCompVals[] = "({$blockTimesid}, {$compid}, {$rqdata['imageid']}, {$reqid})"; # process any subimages for ($key = 1; $key < count($requestInfo['computers']); $key++) { $subimageid = $requestInfo['images'][$key]; $subrevid = getProductionRevisionid($subimageid); $compid = $requestInfo['computers'][$key]; $mgmtnodeid = $requestInfo['mgmtnodes'][$key]; $blockCompVals[] = "({$blockTimesid}, {$compid}, {$subimageid}, {$reqid})"; $query = "INSERT INTO reservation " . "(requestid, " . "computerid, " . "imageid, " . "imagerevisionid, " . "managementnodeid) " . "VALUES " . "({$reqid}, " . "{$compid}, " . "{$subimageid}, " . "{$subrevid}, " . "{$mgmtnodeid})"; doQuery($query, 101); } $blockComps = implode(',', $blockCompVals); $query = "INSERT INTO blockComputers " . "(blockTimeid, computerid, imageid, reloadrequestid) " . "VALUES {$blockComps}"; doQuery($query, 101); cleanSemaphore(); $blockCompVals = array(); } if ($allocated == 0) { $return['status'] = 'warning'; $return['warningcode'] = 14; $return['warningmsg'] = 'unable to allocate any machines'; } $return['allocated'] = $compCompleted / $compsPerAlloc + $allocated; $return['unallocated'] = $rqdata['numMachines'] - $return['allocated']; return $return; }