Modified: vcl/trunk/web/.ht-inc/utils.php
URL: 
http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/utils.php?rev=1624325&r1=1624324&r2=1624325&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/utils.php (original)
+++ vcl/trunk/web/.ht-inc/utils.php Thu Sep 11 16:01:48 2014
@@ -20,6 +20,7 @@ require_once(".ht-inc/secrets.php");
 @include_once("itecsauth/itecsauth.php");
 require_once(".ht-inc/authentication.php");
 require_once(".ht-inc/phpseclib/Crypt/AES.php");
+require_once(".ht-inc/spyc-0.5/spyc.php");
 if(file_exists(".ht-inc/vcldocs.php"))
        require_once(".ht-inc/vcldocs.php");
 
@@ -35,14 +36,10 @@ define("ARG_STRING", 1 << 1);
 define("ARG_MULTINUMERIC", 1 << 2);
 /// used for processInputVar, means the input variable should be an array of 
strings
 define("ARG_MULTISTRING", 1 << 3);
-/// define semaphore key
-define("SEMKEY", 192365819256598);
 
 /// global array used to hold request information between calling isAvailable
 /// and addRequest
 $requestInfo = array();
-#$requestInfo["freeComputerids"] = array();
-#$requestInfo["imageids"] = array();
 
 /// global array to cache arrays of node parents for getNodeParents
 $nodeparents = array();
@@ -62,20 +59,25 @@ $printedHTMLheader = 0;
 
////////////////////////////////////////////////////////////////////////////////
 function initGlobals() {
        global $mode, $user, $remoteIP, $authed, $oldmode, $semid;
-       global $semislocked, $days, $phpVer, $keys, $pemkey, $AUTHERROR;
+       global $days, $phpVer, $keys, $pemkey, $AUTHERROR;
        global $passwdArray, $skin, $contdata, $lastmode, $inContinuation;
-       global $totalQueries, $ERRORS, $queryTimes, $actions;
+       global $ERRORS, $actions;
        global $affilValFunc, $addUserFunc, $updateUserFunc, $addUserFuncArgs;
+       global $uniqid;
 
        define("SECINDAY", 86400);
        define("SECINWEEK", 604800);
        define("SECINMONTH", 2678400);
        define("SECINYEAR", 31536000);
+       # TODO validate security of this
+       if(array_key_exists("PATH_INFO", $_SERVER)) {
+               $pathdata = explode("/", $_SERVER["PATH_INFO"]);
+               $tmp = explode('.', $pathdata[1]);
+               $_GET["mode"] = $tmp[0];
+       }
        $mode = processInputVar("mode", ARG_STRING, 'main');
-       $totalQueries = 0;
        $inContinuation = 0;
        $contdata = array();
-       $queryTimes = array();
        $contuserid = '';
        $continuation = processInputVar('continuation', ARG_STRING);
        if(! empty($continuation)) {
@@ -101,6 +103,7 @@ function initGlobals() {
        $days = array(_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), 
_('Thursday'), _('Friday'), _('Saturday'));
        $phpVerArr = explode('.', phpversion());
        $phpVer = $phpVerArr[0];
+       $uniqid = uniqid($_SERVER['HTTP_HOST'] . "-" . getmypid() . "-");
 
        $passwdArray = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
'K',
                             'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 
'V',
@@ -185,10 +188,6 @@ function initGlobals() {
                        $mode = "auth";
                }
                if($mode == 'xmlrpccall' || $mode == 'xmlrpcaffiliations') {
-                       // get the semaphore id
-                       if(! ($semid = sem_get(SEMKEY, 1, 0666, 1)))
-                               abort(2);
-                       $semislocked = 0;
                        require_once(".ht-inc/xmlrpcWrappers.php");
                        require_once(".ht-inc/requests.php");
                        require_once(".ht-inc/serverprofiles.php");
@@ -257,29 +256,11 @@ function initGlobals() {
                        $updateUserFunc[$id] = create_function('', 'return 
NULL;');
        }
 
-       // get the semaphore id
-       if(! ($semid = sem_get(SEMKEY, 1, 0666, 1)))
-               abort(2);
-       $semislocked = 0;
-
        # include appropriate files
        switch($actions['pages'][$mode]) {
                case 'blockAllocations':
                        require_once(".ht-inc/blockallocations.php");
                        break;
-               case 'manageComputers':
-                       require_once(".ht-inc/computers.php");
-                       break;
-               case 'managementNodes':
-                       require_once(".ht-inc/managementnodes.php");
-                       break;
-               case 'manageImages':
-                       require_once(".ht-inc/images.php");
-                       require_once(".ht-inc/requests.php");
-                       break;
-               case 'manageSchedules':
-                       require_once(".ht-inc/schedules.php");
-                       break;
                case 'help':
                        require_once(".ht-inc/help.php");
                        break;
@@ -305,6 +286,20 @@ function initGlobals() {
                case 'dashboard':
                        require_once(".ht-inc/dashboard.php");
                        break;
+               case 'siteconfig':
+                       require_once(".ht-inc/siteconfig.php");
+                       break;
+               case 'resource':
+               case 'config':
+               case 'image':
+               case 'computer':
+               case 'managementnode':
+               case 'schedule':
+                       require_once(".ht-inc/resource.php");
+                       break;
+               case 'storebackend':
+                       require_once(".ht-inc/storebackend.php");
+                       break;
                case 'serverProfiles':
                        require_once(".ht-inc/serverprofiles.php");
                        require_once(".ht-inc/requests.php");
@@ -316,6 +311,21 @@ function initGlobals() {
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn __autoload($class)
+///
+/// \param $class - name of a class
+///
+/// \brief handles loading class implementation file for a specified class
+///
+////////////////////////////////////////////////////////////////////////////////
+function __autoload($class) {
+       $class = strtolower($class);
+       require_once(".ht-inc/resource.php");
+       require_once(".ht-inc/$class.php");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn checkAccess()
 ///
 /// \brief gets the user's access level to the locker from the ptsowner_admin
@@ -346,6 +356,11 @@ function checkAccess() {
                                exit;
                        }
                }
+               if(! array_key_exists('HTTP_X_PASS', $_SERVER) || 
strlen($_SERVER['HTTP_X_PASS']) == 0) {
+                       printXMLRPCerror(3);   # access denied
+                       dbDisconnect();
+                       exit;
+               }
                $xmlpass = $_SERVER['HTTP_X_PASS'];
                if(get_magic_quotes_gpc())
                        $xmlpass = stripslashes($xmlpass);
@@ -439,16 +454,16 @@ function checkAccess() {
                                        exit;
                                }
                        }
-            elseif($authMechs[$authtype]['type'] == 'redirect'){
-                $affilid = $authMechs[$authtype]['affiliationid'];
-                if(!(isset($apiValidateFunc) && is_array($apiValidateFunc) &&
-                        array_key_exists($affilid, $apiValidateFunc) && 
-                        $apiValidateFunc[$affilid]($xmluser, $xmlpass))){
-                    printXMLRPCerror(3);    # access denied
-                    dbDisconnect();
-                    exit;
-                }
-            }
+                       elseif($authMechs[$authtype]['type'] == 'redirect') {
+                               $affilid = 
$authMechs[$authtype]['affiliationid'];
+                               if(!(isset($apiValidateFunc) && 
is_array($apiValidateFunc) &&
+                                  array_key_exists($affilid, $apiValidateFunc) 
&& 
+                                  $apiValidateFunc[$affilid]($xmluser, 
$xmlpass))) {
+                                       printXMLRPCerror(3);    # access denied
+                                       dbDisconnect();
+                                       exit;
+                               }
+                       }
                        else {
                                printXMLRPCerror(6);    # unable to auth passed 
in X-User
                                dbDisconnect();
@@ -491,16 +506,6 @@ function checkAccess() {
                        if(! $inContinuation) {
                                # check that user has access to this area
                                switch($mode) {
-                                       case 'viewRequests':
-                                               $requests = 
getUserRequests("all", $user["id"]);
-                                               if(! in_array("imageCheckOut", 
$user["privileges"]) &&
-                                                       ! 
in_array("imageAdmin", $user["privileges"]) &&
-                                                  ! count($requests)) {
-                                                       $mode = "";
-                                                       $actionFunction = 
"main";
-                                                       return;
-                                               }
-                                               break;
                                        case 'viewGroups':
                                                if(! in_array("groupAdmin", 
$user["privileges"])) {
                                                        $mode = "";
@@ -508,34 +513,6 @@ function checkAccess() {
                                                        return;
                                                }
                                                break;
-                                       case 'selectImageOption':
-                                               if(! in_array("imageAdmin", 
$user["privileges"])) {
-                                                       $mode = "";
-                                                       $actionFunction = 
"main";
-                                                       return;
-                                               }
-                                               break;
-                                       case 'viewSchedules':
-                                               if(! in_array("scheduleAdmin", 
$user["privileges"])) {
-                                                       $mode = "";
-                                                       $actionFunction = 
"main";
-                                                       return;
-                                               }
-                                               break;
-                                       case 'selectComputers':
-                                               if(! in_array("computerAdmin", 
$user["privileges"])) {
-                                                       $mode = "";
-                                                       $actionFunction = 
"main";
-                                                       return;
-                                               }
-                                               break;
-                                       case 'selectMgmtnodeOption':
-                                               if(! in_array("mgmtNodeAdmin", 
$user["privileges"])) {
-                                                       $mode = "";
-                                                       $actionFunction = 
"main";
-                                                       return;
-                                               }
-                                               break;
                                        case 'serverProfiles':
                                                if(! 
in_array("serverProfileAdmin", $user["privileges"]) &&
                                                   ! in_array("serverCheckOut", 
$user["privileges"])) {
@@ -756,6 +733,7 @@ function clearPrivCache() {
        $_SESSION['userhaspriv'] = array();
        $_SESSION['compstateflow'] = array();
        $_SESSION['usersessiondata'] = array();
+       $_SESSION['variables'] = array();
        unset($_SESSION['user']);
        unset($_SESSION['locales']);
 }
@@ -808,6 +786,8 @@ function setupSession() {
                $_SESSION['compstateflow'] = array();
        if(! array_key_exists('usersessiondata', $_SESSION))
                $_SESSION['usersessiondata'] = array();
+       if(! array_key_exists('variables', $_SESSION))
+               $_SESSION['variables'] = array();
 }
 
 
////////////////////////////////////////////////////////////////////////////////
@@ -899,7 +879,8 @@ function abort($errcode, $query="") {
                $message = "";
                if($errcode >= 100 && $errcode < 400) {
                        $message .= mysql_error($mysql_link_vcl) . "\n";
-                       $message .= mysql_error($mysql_link_acct) . "\n";
+                       if($ENABLE_ITECSAUTH)
+                               $message .= mysql_error($mysql_link_acct) . 
"\n";
                        $message .= $query . "\n";
                }
                $message .= "ERROR($errcode): " . $ERRORS["$errcode"] . "\n";
@@ -926,10 +907,10 @@ function abort($errcode, $query="") {
                print HELPEMAIL . "</a> " . _("for further assistance.  Please 
include the ");
                print _("steps you took that led up to this problem in your 
email message.");
        }
+       // release semaphore lock
+       cleanSemaphore();
        dbDisconnect();
        printHTMLFooter();
-       // release semaphore lock
-       semUnlock();
        exit;
 }
 
@@ -1115,10 +1096,9 @@ function dbDisconnect() {
 
////////////////////////////////////////////////////////////////////////////////
 function doQuery($query, $errcode=101, $db="vcl", $nolog=0) {
        global $mysql_link_vcl, $mysql_link_acct, $user, $mode, 
$ENABLE_ITECSAUTH;
-       global $totalQueries, $queryTimes;
-       $totalQueries++;
        if($db == "vcl") {
-               if((! $nolog) && preg_match('/^(UPDATE|INSERT|DELETE)/', 
$query)) {
+               if((! $nolog) && preg_match('/^(UPDATE|INSERT|DELETE)/', 
$query) &&
+                  strpos($query, 'UPDATE continuations SET expiretime = ') === 
FALSE) {
                        $logquery = str_replace("'", "\'", $query);
                        $logquery = str_replace('"', '\"', $logquery);
                        if(isset($user['id']))
@@ -1137,7 +1117,12 @@ function doQuery($query, $errcode=101, $
                           .        "'$logquery')";
                        mysql_query($q, $mysql_link_vcl);
                }
-               $qh = mysql_query($query, $mysql_link_vcl) or abort($errcode, 
$query);
+               for($i = 0; ! ($qh = mysql_query($query, $mysql_link_vcl)) && 
$i < 3; $i++) {
+                       if(mysql_errno() == '1213') # DEADLOCK, sleep and retry
+                               usleep(50);
+                       else
+                               abort($errcode, $query);
+               }
        }
        elseif($db == "accounts") {
                if($ENABLE_ITECSAUTH)
@@ -1172,7 +1157,8 @@ function dbLastInsertID() {
 ///
 
////////////////////////////////////////////////////////////////////////////////
 function getOSList() {
-       $qh = doQuery("SELECT id, name, prettyname, type FROM OS", "115");
+       $query = "SELECT id, name, prettyname, type, installtype FROM OS ORDER 
BY prettyname";
+       $qh = doQuery($query, "115");
        $oslist = array();
        while($row = mysql_fetch_assoc($qh))
                $oslist[$row['id']] = $row;
@@ -1197,6 +1183,8 @@ function getOSList() {
 /// \b osid - osid for the os on the image\n
 /// \b os - os the image contains\n
 /// \b installtype - method used to install image\n
+/// \b ostypeid - id of the OS type in the image\n
+/// \b ostype - name of the OS type in the image\n
 /// \b minram - minimum amount of RAM needed for image\n
 /// \b minprocnumber - minimum number of processors needed for image\n
 /// \b minprocspeed - minimum speed of processor(s) needed for image\n
@@ -1255,14 +1243,17 @@ function getImages($includedeleted=0, $i
               .        "CONCAT(u.unityid, '@', a.name) AS user, "
               .        "i.datecreated, "
               .        "DATE_FORMAT(i.datecreated, '%c/%d/%y %l:%i %p') AS 
prettydate, "
+              .        "i.deleted, "
+              .        "i.datedeleted, "
               .        "i.production, "
               .        "i.imagename "
               . "FROM imagerevision i, "
               .      "affiliation a, "
               .      "user u "
-              . "WHERE i.deleted = 0 AND "
-              .       "i.userid = u.id AND "
-              .       "u.affiliationid = a.id";
+              . "WHERE i.userid = u.id AND ";
+       if(! $includedeleted)
+               $query .=   "i.deleted = 0 AND ";
+       $query .=      "u.affiliationid = a.id";
        $qh = doQuery($query, 101);
        while($row = mysql_fetch_assoc($qh)) {
                $id = $row['imageid'];
@@ -1281,6 +1272,8 @@ function getImages($includedeleted=0, $i
               .        "i.OSid AS osid, "
               .        "o.name AS os, "
               .        "o.installtype, "
+              .        "ot.id AS ostypeid, "
+              .        "ot.name AS ostype, "
               .        "i.minram AS minram, "
               .        "i.minprocnumber AS minprocnumber, "
               .        "i.minprocspeed AS minprocspeed, "
@@ -1297,6 +1290,7 @@ function getImages($includedeleted=0, $i
               . "FROM image i, "
               .      "platform p, "
               .      "OS o, "
+              .      "OStype ot, "
               .      "resource r, "
               .      "resourcetype t, "
               .      "user u, "
@@ -1306,6 +1300,7 @@ function getImages($includedeleted=0, $i
               .       "t.name = 'image' AND "
               .       "r.subid = i.id AND "
               .       "i.OSid = o.id AND "
+              .       "o.type = ot.name AND "
               .       "i.ownerid = u.id AND "
               .       "u.affiliationid = a.id ";
        if(! $includedeleted)
@@ -1313,7 +1308,11 @@ function getImages($includedeleted=0, $i
    $query .= "ORDER BY i.prettyname";
        $qh = doQuery($query, 120);
        while($row = mysql_fetch_assoc($qh)) {
+               if(is_null($row['maxconcurrent']))
+                       $row['maxconcurrent'] = 0;
                $imagelist[$includedeleted][$row["id"]] = $row;
+               $imagelist[$includedeleted][$row["id"]]['checkuser'] = 1;
+               $imagelist[$includedeleted][$row["id"]]['rootaccess'] = 1;
                if($row["imagemetaid"] != NULL) {
                        if(array_key_exists($row['imagemetaid'], $allmetadata)) 
{
                                $metaid = $row['imagemetaid'];
@@ -1331,7 +1330,7 @@ function getImages($includedeleted=0, $i
                                }
                        }
                        else
-                               $row["imagemetaid"] = NULL;
+                               
$imagelist[$includedeleted][$row["id"]]["imagemetaid"] = NULL;
                }
                if(array_key_exists($row['id'], $allrevisiondata))
                        
$imagelist[$includedeleted][$row['id']]['imagerevision'] = 
$allrevisiondata[$row['id']];
@@ -1344,6 +1343,131 @@ function getImages($includedeleted=0, $i
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn getServerProfiles($id)
+///
+/// \param $id - (optional) if specified, only return data for specified 
profile
+///
+/// \return an array where each key is a profile id whose value is an array 
with
+/// these values:\n
+/// \b name - profile name\n
+/// \b description - profile description\n
+/// \b imageid - id of image associated with profile\n
+/// \b image - pretty name of image associated with profile\n
+/// \b ownerid - user id of owner of profile\n
+/// \b owner - unityid of owner of profile\n
+/// \b fixedIP - IP address to be used with deployed profile\n
+/// \b fixedMAC - MAC address to be used with deployed profile\n
+/// \b admingroupid - id of admin user group associated with profile\n
+/// \b admingroup - name of admin user group associated with profile\n
+/// \b logingroupid - id of login user group associated with profile\n
+/// \b logingroup - name of login user group associated with profile\n
+/// \b monitored - whether or not deployed profile should be monitored\n
+/// \b resourceid - resource id of profile
+///
+/// \brief gets information about server profiles
+///
+////////////////////////////////////////////////////////////////////////////////
+function getServerProfiles($id=0) {
+       $key = getKey(array('getServerProfiles', $id));
+       if(array_key_exists($key, $_SESSION['usersessiondata']))
+               return $_SESSION['usersessiondata'][$key];
+
+       $fixeddata = array();
+       $query = "SELECT name, value FROM variable WHERE name LIKE 
'fixedIPsp%'";
+       $qh = doQuery($query);
+       while($row = mysql_fetch_assoc($qh)) {
+               $spid = str_replace('fixedIPsp', '', $row['name']);
+               $fixeddata[$spid] = Spyc::YAMLLoad($row['value']);
+       }
+
+       $query = "SELECT s.id, "
+              .        "s.name, "
+              .        "s.description, "
+              .        "s.imageid, "
+              .        "i.prettyname AS image, "
+              .        "s.ownerid, "
+              .        "CONCAT(u.unityid, '@', a.name) AS owner, "
+              .        "s.fixedIP, "
+              .        "s.fixedMAC, "
+              .        "s.admingroupid, "
+              .        "CONCAT(ga.name, '@', aa.name) AS admingroup, "
+              .        "s.logingroupid, "
+              .        "CONCAT(gl.name, '@', al.name) AS logingroup, "
+              .        "s.monitored, "
+              .        "r.id AS resourceid "
+              . "FROM serverprofile s "
+              . "LEFT JOIN image i ON (i.id = s.imageid) "
+              . "LEFT JOIN user u ON (u.id = s.ownerid) "
+              . "LEFT JOIN affiliation a ON (a.id = u.affiliationid) "
+              . "LEFT JOIN usergroup ga ON (ga.id = s.admingroupid) "
+              . "LEFT JOIN affiliation aa ON (aa.id = ga.affiliationid) "
+              . "LEFT JOIN usergroup gl ON (gl.id = s.logingroupid) "
+              . "LEFT JOIN affiliation al ON (al.id = gl.affiliationid) "
+              . "LEFT JOIN resource r ON (r.subid = s.id) "
+              . "WHERE r.resourcetypeid = 17 ";
+       if($id != 0)
+               $query .= "AND s.id = $id";
+       else
+               $query .= "ORDER BY name";
+       $qh = doQuery($query, 101);
+       $profiles = array();
+       while($row = mysql_fetch_assoc($qh)) {
+               $profiles[$row['id']] = $row;
+               if(array_key_exists($row['id'], $fixeddata)) {
+                       $profiles[$row['id']]['netmask'] = 
$fixeddata[$row['id']]['netmask'];
+                       $profiles[$row['id']]['router'] = 
$fixeddata[$row['id']]['router'];
+                       $profiles[$row['id']]['dns'] = implode(',', 
$fixeddata[$row['id']]['dns']);
+               }
+               else {
+                       $profiles[$row['id']]['netmask'] = '';
+                       $profiles[$row['id']]['router'] = '';
+                       $profiles[$row['id']]['dns'] = '';
+               }
+       }
+       $_SESSION['usersessiondata'][$key] = $profiles;
+       return $profiles;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getServerProfileImages($userid)
+///
+/// \param $userid - id from user table
+///
+/// \return array where the key is the id of the image and the value is the
+/// prettyname of the image
+///
+/// \brief builds an array of images that user has access to via server 
profiles
+///
+////////////////////////////////////////////////////////////////////////////////
+function getServerProfileImages($userid) {
+       $key = getKey(array('getServerProfileImages', $userid));
+       if(array_key_exists($key, $_SESSION['usersessiondata']))
+               return $_SESSION['usersessiondata'][$key];
+       $resources = getUserResources(array('serverCheckOut', 
'serverProfileAdmin'),
+                                     array('available', 'administer'));
+       $ids = array_keys($resources['serverprofile']);
+       $inids = implode(',', $ids);
+       if(empty($inids)) {
+               $_SESSION['usersessiondata'][$key] = array();
+               return array();
+       }
+       $query = "SELECT i.id, "
+              .        "i.prettyname AS image "
+              . "FROM serverprofile s, "
+              .      "image i "
+              . "WHERE s.imageid = i.id AND "
+              .       "s.id IN ($inids)";
+       $qh = doQuery($query, 101);
+       $profiles = array();
+       while($row = mysql_fetch_assoc($qh))
+               $profiles[$row['id']] = $row['image'];
+       $_SESSION['usersessiondata'][$key] = $profiles;
+       return $profiles;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn getImageRevisions($imageid, $incdeleted)
 ///
 /// \param $imageid - id of an image
@@ -1420,10 +1544,12 @@ function getImageNotes($imageid) {
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getImageConnectMethods($imageid, $revisionid)
+/// \fn getImageConnectMethods($imageid, $revisionid, $nostatic=0)
 ///
 /// \param $imageid - id of an image
 /// \param $revisionid - (optional, default=0) revision id of image
+/// \param $nostatic - (optional, default=0) pass 1 to keep from using the
+/// static variable defined in the function
 ///
 /// \return an array of connect methods enabled for specified image where the
 /// key is the id of the connect method and the value is the description
@@ -1431,18 +1557,20 @@ function getImageNotes($imageid) {
 /// \brief builds an array of connect methods enabled for the image
 ///
 
////////////////////////////////////////////////////////////////////////////////
-function getImageConnectMethods($imageid, $revisionid=0) {
-       $key = getKey(array('getImageConnectMethods', $imageid, $revisionid));
+function getImageConnectMethods($imageid, $revisionid=0, $nostatic=0) {
+       $key = getKey(array('getImageConnectMethods', (int)$imageid, 
(int)$revisionid));
        if(array_key_exists($key, $_SESSION['usersessiondata']))
                return $_SESSION['usersessiondata'][$key];
        if($revisionid == 0)
-               $revisionid = getProductionRevisionid($imageid);
+               $revisionid = getProductionRevisionid($imageid, $nostatic);
        if($revisionid == '') {
                $_SESSION['usersessiondata'][$key] = array();
                return array();
        }
 
        static $allmethods = array();
+       if($nostatic)
+               $allmethods = array();
        if(empty($allmethods)) {
                $query = "SELECT DISTINCT c.id, "
                      .                  "c.description, "
@@ -1622,9 +1750,11 @@ function checkClearImageMeta($imagemetai
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getProductionRevisionid($imageid)
+/// \fn getProductionRevisionid($imageid, $nostatic=0)
 ///
 /// \param $imageid
+/// \param $nostatic - (optional, default=0) pass 1 to keep from using the
+/// static variable defined in the function
 ///
 /// \return the production revision id for $imageid
 ///
@@ -1632,8 +1762,10 @@ function checkClearImageMeta($imagemetai
 /// table
 ///
 
////////////////////////////////////////////////////////////////////////////////
-function getProductionRevisionid($imageid) {
+function getProductionRevisionid($imageid, $nostatic=0) {
        static $alldata = array();
+       if($nostatic)
+               $alldata = array();
        if(! empty($alldata))
                if(array_key_exists($imageid, $alldata))
                        return $alldata[$imageid];
@@ -1709,6 +1841,8 @@ function removeNoCheckout($images) {
 function getUserResources($userprivs, $resourceprivs=array("available"),
                           $onlygroups=0, $includedeleted=0, $userid=0) {
        global $user;
+       if(in_array('managementnodeAdmin', $userprivs))
+               $userprivs[] = 'mgmtnodeAdmin';
        $key = getKey(array($userprivs, $resourceprivs, $onlygroups, 
$includedeleted, $userid));
        if(array_key_exists($key, $_SESSION['userresources']))
                return $_SESSION['userresources'][$key];
@@ -1840,6 +1974,9 @@ function getUserResources($userprivs, $r
                   getResourcesFromGroups($resourcegroups[$type], $type, 
$includedeleted);
        }
        addOwnedResources($resources, $includedeleted, $userid);
+       $noimageid = getImageId('noimage');
+       if(array_key_exists($noimageid, $resources['image']))
+               unset($resources['image'][$noimageid]);
        $_SESSION['userresources'][$key] = $resources;
        return $resources;
 }
@@ -2082,23 +2219,25 @@ function addUserResources(&$nodeprivs, $
 
////////////////////////////////////////////////////////////////////////////////
 function addOwnedResources(&$resources, $includedeleted, $userid) {
        foreach(array_keys($resources) as $type) {
-               if($type == "image")
-                       $field = "prettyname";
-               elseif($type == "computer")
-                       $field = "hostname";
-               elseif($type == "schedule")
-                       $field = "name";
-               elseif($type == "managementnode")
-                       $field = "hostname";
-               elseif($type == "serverprofile")
-                       $field = "name";
-               else
-                       continue;
+               # TODO make this get name field from classes
+               switch($type) {
+                       case "image":
+                               $field = 'prettyname';
+                               break;
+                       case "computer":
+                       case "managementnode":
+                               $field = 'hostname';
+                               break;
+                       default:
+                               $field = 'name';
+                               break;
+               }
                $query = "SELECT id, "
                       .        "$field "
                       . "FROM $type "
                       . "WHERE ownerid = $userid";
-               if(! $includedeleted && ($type == "image" || $type == 
"computer"))
+               if(! $includedeleted &&
+                  ($type == "image" || $type == "computer" || $type == 
'config'))
                        $query .= " AND deleted = 0";
                $qh = doQuery($query, 101);
                while($row = mysql_fetch_assoc($qh)) {
@@ -2157,18 +2296,18 @@ function addOwnedResourceGroups(&$resour
 
////////////////////////////////////////////////////////////////////////////////
 function getResourcesFromGroups($groups, $type, $includedeleted) {
        $return = array();
-       if($type == "image")
-               $field = "prettyname";
-       elseif($type == "computer")
-               $field = "hostname";
-       elseif($type == "schedule")
-               $field = "name";
-       elseif($type == "managementnode")
-               $field = "hostname";
-       elseif($type == "serverprofile")
-               $field = "name";
-       else
-               return array();
+       switch($type) {
+               case "image":
+                       $field = 'prettyname';
+                       break;
+               case "computer":
+               case "managementnode":
+                       $field = 'hostname';
+                       break;
+               default:
+                       $field = 'name';
+                       break;
+       }
 
        $groups = implode("','", $groups);
        $inlist = "'$groups'";
@@ -2186,7 +2325,8 @@ function getResourcesFromGroups($groups,
               .       "g.name IN ($inlist) AND "
               .       "g.resourcetypeid = rt.id AND "
               .       "rt.name = '$type'";
-       if(! $includedeleted && ($type == "image" || $type == "computer")) {
+       if(! $includedeleted &&
+          ($type == "image" || $type == "computer" || $type == 'config')) {
                $query .= "AND deleted = 0 ";
        }
        /*if($type == "image")
@@ -2354,17 +2494,18 @@ function decryptData($data) {
 }
 
 
////////////////////////////////////////////////////////////////////////////////
-//
-//  \fn encryptDataAsymmetric($data, $public_key)
-//
-//  \param $data - a string
-//
-//  \param $public_key - either a filename for a public key or the public key 
itself
-//
-//  \return hex-encoded, encrypted form of $data
-//
-//  \brief generate public key encrypted data
-//
+///
+/// \fn encryptDataAsymmetric($data, $public_key)
+///
+/// \param $data - a string
+///
+/// \param $public_key - either a filename for a public key or the public key
+/// itself
+///
+/// \return hex-encoded, encrypted form of $data
+///
+/// \brief generate public key encrypted data
+///
 
////////////////////////////////////////////////////////////////////////////////
 function encryptDataAsymmetric($data, $public_key){
        if(file_exists($public_key)){
@@ -3176,7 +3317,7 @@ function processInputVar($vartag, $type,
                if(! is_string($return)) {
                        print "ERROR (code:3)<br>\n";
                        printHTMLFooter();
-                       semUnlock();
+                       cleanSemaphore();
                        exit();
                }
                #print "before - $return<br>\n";
@@ -3198,7 +3339,7 @@ function processInputVar($vartag, $type,
                        if(! is_string($value)) {
                                print "ERROR (code:3)<br>\n";
                                printHTMLFooter();
-                               semUnlock();
+                               cleanSemaphore();
                                exit();
                        }
                }
@@ -3406,6 +3547,8 @@ function getUserInfo($id, $noupdate=0, $
               .        "u.mapserial AS mapserial, "
               .        "u.showallgroups, "
               .        "u.lastupdated AS lastupdated, "
+              .        "u.usepublickeys, "
+              .        "u.sshpublickeys, "
               .        "af.shibonly "
               . "FROM user u, "
               .      "IMtype i, "
@@ -3419,6 +3562,7 @@ function getUserInfo($id, $noupdate=0, $
 
        $qh = doQuery($query, "105");
        if($user = mysql_fetch_assoc($qh)) {
+               $user['sshpublickeys'] = 
htmlspecialchars($user['sshpublickeys']);
                if((datetimeToUnix($user["lastupdated"]) > time() - SECINDAY) ||
                   $user['unityid'] == 'vclreload' ||
                   $user['affiliation'] == 'Local' ||
@@ -3723,9 +3867,10 @@ function getOverallUserPrivs($userid) {
               .                         "WHERE ownerid = $userid))";
        $qh = doQuery($query, 107);
        $privileges = array();
-       while($row = mysql_fetch_row($qh)) {
-               array_push($privileges, $row[0]);
-       }
+       while($row = mysql_fetch_row($qh))
+               $privileges[] = $row[0];
+       if(in_array("mgmtNodeAdmin", $privileges))
+               $privileges[] = 'managementnodeAdmin';
        return $privileges;
 }
 
@@ -3765,14 +3910,17 @@ function getBlockAllocationIDs($user) {
 
////////////////////////////////////////////////////////////////////////////////
 ///
 /// \fn isAvailable($images, $imageid, $imagerevisionid, $start, $end,
-///                 $requestid, $userid, $ignoreprivileges, $forimaging, $ip,
-///                 $mac, $skipconcurrentcheck)
+///                 $holdcomps, $requestid, $userid, $ignoreprivileges,
+///                 $forimaging, $ip, $mac, $skipconcurrentcheck)
 ///
 /// \param $images - array as returned from getImages
 /// \param $imageid - imageid from the image table
 /// \param $imagerevisionid - id of revision of image from imagerevision table
 /// \param $start - unix timestamp for start of reservation
 /// \param $end - unix timestamp for end of reservation
+/// \param $holdcomps - bool - 1 to lock computers for later adding to
+/// reservation, 0 not to; use 0 when just checking for estimated availability,
+/// use 1 when finding computers to actually reserve
 /// \param $requestid - (optional) a requestid; if checking for an available
 /// timeslot to update a request, pass the request id that will be updated;
 /// otherwise, don't pass this argument
@@ -3790,7 +3938,8 @@ function getBlockAllocationIDs($user) {
 /// \param $skipconcurrentcheck (optional, default=0) - set to 1 to skip check
 /// for concurrent use of image; useful for setting up reload reservations
 ///
-/// \return -3 if unavailable due to an ip/mac conflict
+/// \return -4 if unavailable due to an ip/mac conflict with another machine
+///         -3 if unavailable due to an ip/mac conflict with another 
reservation
 ///         -2 if specified time period is during a maintenance window
 ///         -1 if $imageid is limited in the number of concurrent reservations
 ///         available, and the limit has been reached
@@ -3801,16 +3950,25 @@ function getBlockAllocationIDs($user) {
 ///
 
////////////////////////////////////////////////////////////////////////////////
 function isAvailable($images, $imageid, $imagerevisionid, $start, $end,
-                     $requestid=0, $userid=0, $ignoreprivileges=0,
+                     $holdcomps, $requestid=0, $userid=0, $ignoreprivileges=0,
                      $forimaging=0, $ip='', $mac='', $skipconcurrentcheck=0) {
-       global $requestInfo;
+       global $requestInfo, $user;
        $requestInfo["start"] = $start;
        $requestInfo["end"] = $end;
        $requestInfo["imageid"] = $imageid;
+       $requestInfo["ipwarning"] = 0;
        $allocatedcompids = array(0);
 
+       if(! is_array($imagerevisionid))
+               $imagerevisionid = array($imageid => array($imagerevisionid));
+       elseif(empty($imagerevisionid))
+               $imagerevisionid = array($imageid => 
array(getProductionRevisionid($imageid)));
+
        if(schCheckMaintenance($start, $end))
-               return -2;
+               return debugIsAvailable(-2, 1, $start, $end, $imagerevisionid);
+
+       if(! array_key_exists($imageid, $images))
+               return debugIsAvailable(0, 20, $start, $end, $imagerevisionid);
 
        if($requestInfo["start"] <= time()) {
                $now = 1;
@@ -3826,26 +3984,29 @@ function isAvailable($images, $imageid, 
        $requestInfo["computers"] = array();
        $requestInfo["computers"][0] = 0;
        $requestInfo["images"][0] = $imageid;
+       $requestInfo["imagerevisions"][0] = $imagerevisionid[$imageid][0];
 
-       # loop to check for available computers for all needed images
+       # build array of subimages
+       # TODO handle mininstance
        if(! $forimaging && $images[$imageid]["imagemetaid"] != NULL) {
                $count = 1;
                foreach($images[$imageid]["subimages"] as $imgid) {
                        $requestInfo['computers'][$count] = 0;
                        $requestInfo['images'][$count] = $imgid;
+                       if(array_key_exists($imgid, $imagerevisionid) &&
+                          array_key_exists($count, $imagerevisionid[$imgid]))
+                               $requestInfo['imagerevisions'][$count] = 
$imagerevisionid[$imgid][$count];
+                       else
+                               $requestInfo['imagerevisions'][$count] = 
getProductionRevisionid($imgid);
                        $count++;
                }
        }
 
-       // get semaphore lock
-       if(! semLock())
-               abort(3);
-
        $startstamp = unixToDatetime($start);
        $endstamp = unixToDatetime($end + 900);
 
-       # check for overlapping use of mac or ip
        if(! empty($mac) || ! empty($ip)) {
+               # check for overlapping use of mac or ip
                $query = "SELECT rq.id "
                       . "FROM reservation rs, "
                       .      "request rq, "
@@ -3862,8 +4023,17 @@ function isAvailable($images, $imageid, 
                $query .= "LIMIT 1";
                $qh = doQuery($query, 101);
                if(mysql_num_rows($qh)) {
-                       semUnlock();
-                       return -3;
+                       return debugIsAvailable(-3, 2, $start, $end, 
$imagerevisionid);
+               }
+
+               # check for IP being used by a management node
+               $query = "SELECT id "
+                      . "FROM managementnode "
+                      . "WHERE IPaddress = '$ip' AND "
+                      .       "stateid != 1";
+               $qh = doQuery($query, 101);
+               if(mysql_num_rows($qh)) {
+                       return debugIsAvailable(-4, 16, $start, $end, 
$imagerevisionid);
                }
        }
 
@@ -3874,10 +4044,19 @@ function isAvailable($images, $imageid, 
        $ignorestates = "'maintenance','vmhostinuse','hpc','failed'";
        if($now)
                $ignorestates .= ",'reloading','reload','timeout','inuse'";
+
        foreach($requestInfo["images"] as $key => $imageid) {
                # check for max concurrent usage of image
                if(! $skipconcurrentcheck && 
                   $images[$imageid]['maxconcurrent'] != NULL) {
+                       if($userid == 0)
+                               $usersgroups = $user['groups'];
+                       else {
+                               $testuser = getUserInfo($userid, 0, 1);
+                               if(is_null($testuser))
+                                       return debugIsAvailable(0, 17, $start, 
$end, $imagerevisionid);
+                               $usersgroups = $testuser['groups'];
+                       }
                        $decforedit = 0;
                        $compids = array();
                        $reloadid = getUserlistID('vclreload@Local');
@@ -3899,6 +4078,7 @@ function isAvailable($images, $imageid, 
                        }
                        $usagecnt = count($compids);
                        $allids = implode("','", $compids);
+                       $ignoregroups = implode("','", 
array_keys($usersgroups));
                        $query = "SELECT COUNT(bc.imageid) AS currentusage "
                               . "FROM blockComputers bc, "
                               .      "blockRequest br, "
@@ -3907,34 +4087,55 @@ function isAvailable($images, $imageid, 
                               .       "bt.blockRequestid = br.id AND "
                               .       "bc.imageid = $imageid AND "
                               .       "bc.computerid NOT IN ('$allids') AND "
+                              .       "br.groupid NOT IN ('$ignoregroups') AND 
"
                               .       "'$startstamp' < (bt.end + INTERVAL 900 
SECOND) AND "
                               .       "'$endstamp' > bt.start AND "
                               .       "bt.skip != 1 AND "
                               .       "br.status != 'deleted'";
                        $qh = doQuery($query);
                        if(! $row = mysql_fetch_assoc($qh)) {
-                               semUnlock();
-                               return 0;
+                               cleanSemaphore();
+                               return debugIsAvailable(0, 3, $start, $end, 
$imagerevisionid);
                        }
                        if(($usagecnt + $row['currentusage'] - $decforedit) >= 
$images[$imageid]['maxconcurrent']) {
-                               semUnlock();
-                               return -1;
+                               cleanSemaphore();
+                               return debugIsAvailable(-1, 4, $start, $end, 
$imagerevisionid);
                        }
                }
 
                $platformid = getImagePlatform($imageid);
                if(is_null($platformid)) {
-                       semUnlock();
-                       return 0;
+                       cleanSemaphore();
+                       return debugIsAvailable(0, 5, $start, $end, 
$imagerevisionid);
                }
 
                # get computers $imageid maps to
-               $tmp = getMappedResources($imageid, "image", "computer");
-               if(! count($tmp)) {
-                       semUnlock();
-                       return 0;
+               $compids = getMappedResources($imageid, "image", "computer");
+               if(! count($compids)) {
+                       cleanSemaphore();
+                       return debugIsAvailable(0, 6, $start, $end, 
$imagerevisionid);
+               }
+               $mappedcomputers = implode(',', $compids);
+
+               // if $ip specified, only look at computers under management 
nodes that can
+               #   handle that network
+               if($ip != '') {
+                       $mappedmns = getMnsFromImage($imageid);
+                       $mnnets = checkAvailableNetworks($ip);
+                       $intersect = array_intersect($mappedmns, $mnnets);
+                       $tmpcompids = array();
+                       foreach($intersect as $mnid) {
+                               $tmp2 = getMappedResources($mnid, 
'managementnode', 'computer');
+                               $tmpcompids = array_merge($tmpcompids, $tmp2);
+                       }
+                       $tmpcompids = array_unique($tmpcompids);
+                       $newcompids = array_intersect($compids, $tmpcompids);
+                       if(! count($newcompids)) {
+                               cleanSemaphore();
+                               return debugIsAvailable(0, 18, $start, $end, 
$imagerevisionid);
+                       }
+                       $mappedcomputers = implode(',', $newcompids);
                }
-               $mappedcomputers = implode(',', $tmp);
 
                #get computers for available schedules and platforms
                $computerids = array();
@@ -3960,8 +4161,8 @@ function isAvailable($images, $imageid, 
                        $qh = doQuery($query, 128);
                        $row = mysql_fetch_row($qh);
                        if(! in_array($row[0], $scheduleids)) {
-                               semUnlock();
-                               return 0;
+                               cleanSemaphore();
+                               return debugIsAvailable(0, 7, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids);
                        }
                        // set $virtual to 0 so that it is defined later but 
skips the additional code
                        $virtual = 0;
@@ -3975,9 +4176,10 @@ function isAvailable($images, $imageid, 
                               . "WHERE i.id = $imageid";
                        $qh = doQuery($query, 101);
                        if(! ($row = mysql_fetch_assoc($qh))) {
-                               semUnlock();
-                               return 0;
+                               cleanSemaphore();
+                               return debugIsAvailable(0, 8, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids);
                        }
+                       # TODO might need to check for other strings for KVM, 
OpenStack, etc
                        if(preg_match('/(vmware)/', $row['installtype']))
                                $virtual = 1;
                        else
@@ -4006,6 +4208,7 @@ function isAvailable($images, $imageid, 
                               . "LEFT JOIN OSinstalltype oi ON (oi.name = 
o.installtype) "
                               . "LEFT JOIN provisioningOSinstalltype poi ON 
(poi.OSinstalltypeid = oi.id) "
                               . "LEFT JOIN computer c ON (poi.provisioningid = 
c.provisioningid) "
+                              . "LEFT JOIN semaphore se ON (c.id = 
se.computerid) "
                               . "WHERE i.id = $imageid AND "
                               .       "c.scheduleid IN ($schedules) AND "
                               .       "c.platformid = $platformid AND "
@@ -4020,16 +4223,17 @@ function isAvailable($images, $imageid, 
                        if(! $ignoreprivileges)
                                $query .=   "c.id IN ($usercomputers) AND ";
                        $query .=      "c.id IN ($mappedcomputers) AND "
-                              .       "c.id NOT IN ($alloccompids) "
-                              . "ORDER BY (c.procspeed * c.procnumber) DESC, "
-                              .          "RAM DESC, "
-                              .          "network DESC";
+                              .       "c.id NOT IN ($alloccompids) AND "
+                              .       "(se.expires IS NULL OR se.expires < 
NOW()) "
+                              . "ORDER BY RAM, "
+                              .          "(c.procspeed * c.procnumber), "
+                              .          "network";
 
                        $qh = doQuery($query, 129);
                        while($row = mysql_fetch_assoc($qh)) {
                                array_push($computerids, $row['id']);
                                if($row['currentimageid'] == $imageid &&
-                                  $row['imagerevisionid'] == $imagerevisionid) 
{
+                                  $row['imagerevisionid'] == 
$requestInfo['imagerevisions'][$key]) {
                                        array_push($currentids, $row['id']);
                                }
                        }
@@ -4039,6 +4243,10 @@ function isAvailable($images, $imageid, 
                        $blockids = $blockdata['compids'];
                }
 
+               # return 0 if no computers available
+               if(empty($computerids) && empty($blockids))
+                       return debugIsAvailable(0, 21, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
+
                #remove computers from list that are already scheduled
                $usedComputerids = array();
                $query = "SELECT DISTINCT rs.computerid "
@@ -4060,15 +4268,19 @@ function isAvailable($images, $imageid, 
 
                // if modifying a reservation and $computerids is now empty, 
return 0
                if($requestid && empty($computerids)) {
-                       semUnlock();
-                       return 0;
+                       cleanSemaphore();
+                       return debugIsAvailable(0, 9, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
                }
 
+               # return 0 if no computers available
+               if(empty($computerids) && empty($currentids) && 
empty($blockids))
+                       return debugIsAvailable(0, 19, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
+
                # remove computers from list that are allocated to block 
allocations
                if($altRemoveBlockCheck) {
                        if(editRequestBlockCheck($computerids[0], $imageid, 
$start, $end)) {
-                               semUnlock();
-                               return 0;
+                               cleanSemaphore();
+                               return debugIsAvailable(0, 10, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
                        }
                }
                elseif(! count($blockids)) {  # && ! $altRemoveBlockCheck
@@ -4104,7 +4316,7 @@ function isAvailable($images, $imageid, 
                                       . "LEFT JOIN computer c2 ON (v.id = 
c2.vmhostid) "
                                       . "LEFT JOIN image i ON 
(c2.currentimageid = i.id) "
                                       . "WHERE c.stateid = 20 "
-                                      . "GROUP BY c.id";
+                                      . "GROUP BY v.id";
                                doQuery($query, 101);
                        }
 
@@ -4116,9 +4328,9 @@ function isAvailable($images, $imageid, 
                               . "LEFT JOIN image i ON (c.currentimageid = 
i.id) "
                               . "WHERE c.id IN ($inids) AND "
                               .       "(v.allocRAM - i.minram + 
{$images[$imageid]['minram']}) < v.RAM "
-                              . "ORDER BY (c.procspeed * c.procnumber) DESC, "
-                              .          "c.RAM DESC, "
-                              .          "c.network DESC";
+                              . "ORDER BY c.RAM, "
+                              .          "(c.procspeed * c.procnumber), "
+                              .          "c.network";
                        $qh = doQuery($query, 101);
                        $newcompids = array();
                        while($row = mysql_fetch_assoc($qh))
@@ -4126,6 +4338,48 @@ function isAvailable($images, $imageid, 
                        $computerids = $newcompids;
                }
 
+               # check for use of specified IP address, have to wait until here
+               #   because there may be a computer already assigned the IP that
+               #   can be used for this reservation
+               if(! empty($ip) && $now) {
+                       $allcompids = array_merge($computerids, $blockids);
+                       if(empty($allcompids))
+                               return debugIsAvailable(0, 13, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
+                       $inids = implode(',', $allcompids);
+                       $query = "SELECT id "
+                              . "FROM computer "
+                              . "WHERE id NOT IN ($inids) AND "
+                              .       "deleted = 0 AND "
+                              .       "stateid != 1 AND "
+                              .       "IPaddress = '$ip' AND "
+                              .       "(type != 'virtualmachine' OR "
+                              .       "vmhostid IS NOT NULL)";
+                       $qh = doQuery($query);
+                       if(mysql_num_rows($qh)) {
+                               if($now)
+                                       return debugIsAvailable(-4, 18, $start, 
$end, $imagerevisionid, $computerids, $currentids, $blockids, array(), 
$virtual);
+                               $requestInfo['ipwarning'] = 1;
+                       }
+                       $query = "SELECT id "
+                              . "FROM computer "
+                              . "WHERE id in ($inids) AND "
+                              .       "IPaddress = '$ip'";
+                       if($requestid)
+                               $query .= " AND id != $compid"; # TODO test this
+                       $qh = doQuery($query);
+                       $cnt = mysql_num_rows($qh);
+                       if($cnt > 1) {
+                               if($now)
+                                       return debugIsAvailable(-4, 19, $start, 
$end, $imagerevisionid, $computerids, $currentids, $blockids, array(), 
$virtual);
+                               $requestInfo['ipwarning'] = 1;
+                       }
+                       elseif($cnt == 1) {
+                               $row = mysql_fetch_assoc($qh);
+                               $computerids = array($row['id']);
+                               $blockids = array();
+                       }
+               }
+
                # remove any recently reserved computers that could have been an
                #   undetected failure
                $failedids = getPossibleRecentFailures($userid, $imageid);
@@ -4152,14 +4406,17 @@ function isAvailable($images, $imageid, 
                }
 
                # allocate a computer
+               $_imgrevid = $requestInfo['imagerevisions'][$key];
                $comparr = allocComputer($blockids, $currentids, $computerids,
-                                        $startstamp, $nowfuture);
+                                        $startstamp, $endstamp, $nowfuture, 
$imageid, $_imgrevid,
+                                        $holdcomps, $requestid);
                if(empty($comparr) && $shortened)
                        $comparr = allocComputer($origblockids, $origcurrentids,
-                                                $origcomputerids, $startstamp, 
$nowfuture);
+                                                $origcomputerids, $startstamp, 
$endstamp, $nowfuture,
+                                                $imageid, $_imgrevid, 
$holdcomps, $requestid);
                if(empty($comparr)) {
-                       semUnlock();
-                       return 0;
+                       cleanSemaphore();
+                       return debugIsAvailable(0, 11, $start, $end, 
$imagerevisionid, $computerids, $currentids, $blockids, $failedids, $virtual);
                }
 
                $requestInfo["computers"][$key] = $comparr['compid'];
@@ -4171,7 +4428,110 @@ function isAvailable($images, $imageid, 
                array_push($allocatedcompids, $comparr['compid']);
        }
 
-       return 1;
+       return debugIsAvailable(1, 12, $start, $end, $imagerevisionid, 
$computerids, $currentids, $blockids, array(), $virtual);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn debugIsAvailable($rc, $loc, $start, $end, $imagerevisionid,
+///                      $compids=array(), $currentids=array(),
+///                      $blockids=array(), $failedids=array(), $virtual='')
+///
+/// \param $rc - return code
+/// \param $loc - location from isAvailable function
+/// \param $start - as passed to isAvailable function
+/// \param $end - as passed to isAvailable function
+/// \param $imagerevisionid - as passed to isAvailable function
+/// \param $compids - $computerids from isAvailable
+/// \param $currentids - $currentids from isAvailable
+/// \param $blockids - $blockids from isAvailable
+/// \param $failedids - $failedids from isAvailable
+/// \param $virtual - $virtual from isAvailable
+///
+/// \return $rc as passed in
+///
+/// \brief prints debug line about why isAvailable returned specified return
+/// code in javascript console if user has 'View Debug Information' user group
+/// permission and has set the debug flag; most data passed in is currently
+/// unused, but allows for a single place to add code for debugging
+///
+////////////////////////////////////////////////////////////////////////////////
+function debugIsAvailable($rc, $loc, $start, $end, $imagerevisionid,
+                              $compids=array(), $currentids=array(),
+                              $blockids=array(), $failedids=array(), 
$virtual='') {
+       global $user, $mode;
+       $debug = getContinuationVar('debug', 0);
+       if(! $debug ||
+          $mode != 'AJupdateWaitTime' ||
+          ! checkUserHasPerm('View Debug Information'))
+               return $rc;
+       switch($loc) {
+               case "1":
+                       $msg = "site maintenance is scheduled for the requested 
time";
+                       break;
+               case "20":
+                       $msg = "invalid image id submitted - not found in 
images available to the user";
+                       break;
+               case "2":
+                       $msg = "an overlapping server reservation has the same 
fixed IP or MAC address";
+                       break;
+               case "16":
+                       $msg = "the requested fixed IP address is currently in 
use by a management node";
+                       break;
+               case "17":
+                       $msg = "failed to look up information about the 
specified user";
+                       break;
+               case "3":
+                       $msg = "failed to get image usage count for block 
allocations";
+                       break;
+               case "4":
+                       $msg = "max concurrent usage of image exceeded 
(includes those set aside for block allocations)";
+                       break;
+               case "5":
+                       $msg = "failed to get platform of image";
+                       break;
+               case "6":
+                       $msg = "image is not mapped to any computers";
+                       break;
+               case "18":
+                       $msg = "no available computers under a management node 
that can handle the specified IP address";
+                       break;
+               case "7":
+                       $msg = "the schedule of the currently reserved computer 
does not allow for the requested time";
+                       break;
+               case "8":
+                       $msg = "failed to get OSinstalltype for image";
+                       break;
+               case "21":
+                       $msg = "no computers with a matching schedule and 
platform, and in an available state, and available to the user, and mapped to 
the image, and matching image resource requirements";
+                       break;
+               case "9":
+                       $msg = "not able to change existing reservation time 
for currently reserved computer";
+                       break;
+               case "19":
+                       $msg = "no computers available (after removing 
scheduled computers/before performing virtual host resource checks)";
+                       break;
+               case "10":
+                       $msg = "modification time overlaps with time computer 
is set aside for block allocation";
+                       break;
+               case "13":
+                       $msg = "no computers available (after virtual host 
resource checks/before performing overlapping IP address check)";
+                       break;
+               case "18":
+                       $msg = "requested IP address in use by another 
computer";
+                       break;
+               case "19":
+                       $msg = "at least 2 computers have the requested IP 
address assigned to them";
+                       break;
+               case "11":
+                       $msg = "unable to get either a management node or 
semaphore for available computer";
+                       break;
+               case "12":
+                       $msg = "successfully found a computer";
+                       break;
+       }
+       print "console.log('$msg');";
+       return $rc;
 }
 
 
////////////////////////////////////////////////////////////////////////////////
@@ -4274,14 +4634,20 @@ function schCheckMaintenance($start, $en
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn allocComputer($blockids, $currentids, $computerids, $start,
-///                   $nowfuture)
+/// \fn allocComputer($blockids, $currentids, $computerids, $start, $end,
+///                   $nowfuture, $imageid, $imagerevisionid, $holdcomps,
+///                   $requestid)
 ///
 /// \param $blockids - array of computer ids
 /// \param $currentids - array of computer ids
 /// \param $computerids - array of computer ids
 /// \param $start - start time in datetime format
+/// \param $start - end time in datetime format
 /// \param $nowfuture - "now" or "future"
+/// \param $holdcomps - bool - 1 to lock computers for later adding to
+/// reservation, 0 not to; use 0 when just checking for estimated availability,
+/// use 1 when finding computers to actually reserve
+/// \param $requestid - id of request if called for editing an existing one
 ///
 /// \return empty array if failed to allocate a computer; array with these keys
 /// on success:\n
@@ -4294,8 +4660,10 @@ function schCheckMaintenance($start, $en
 /// tries to allocate a management node for it
 ///
 
////////////////////////////////////////////////////////////////////////////////
-function allocComputer($blockids, $currentids, $computerids, $start,
-                       $nowfuture) {
+function allocComputer($blockids, $currentids, $computerids, $start, $end,
+                       $nowfuture, $imageid, $imagerevisionid, $holdcomps,
+                       $requestid) {
+       global $requestInfo;
        $ret = array();
        if(SCHEDULER_ALLOCATE_RANDOM_COMPUTER) {
                shuffle($blockids);
@@ -4306,6 +4674,8 @@ function allocComputer($blockids, $curre
                $mgmtnodeid = findManagementNode($compid, $start, $nowfuture);
                if($mgmtnodeid == 0)
                        continue;
+               if($holdcomps && ! getSemaphore($imageid, $imagerevisionid, 
$mgmtnodeid, $compid, $start, $end, $requestid))
+                       continue;
                $ret['compid'] = $compid;
                $ret['mgmtid'] = $mgmtnodeid;
                $ret['loaded'] = 1;
@@ -4316,6 +4686,8 @@ function allocComputer($blockids, $curre
                $mgmtnodeid = findManagementNode($compid, $start, $nowfuture);
                if($mgmtnodeid == 0)
                        continue;
+               if($holdcomps && ! getSemaphore($imageid, $imagerevisionid, 
$mgmtnodeid, $compid, $start, $end, $requestid))
+                       continue;
                $ret['compid'] = $compid;
                $ret['mgmtid'] = $mgmtnodeid;
                $ret['loaded'] = 1;
@@ -4326,6 +4698,8 @@ function allocComputer($blockids, $curre
                $mgmtnodeid = findManagementNode($compid, $start, $nowfuture);
                if($mgmtnodeid == 0)
                        continue;
+               if($holdcomps && ! getSemaphore($imageid, $imagerevisionid, 
$mgmtnodeid, $compid, $start, $end, $requestid))
+                       continue;
                $ret['compid'] = $compid;
                $ret['mgmtid'] = $mgmtnodeid;
                $ret['loaded'] = 0;
@@ -4337,6 +4711,103 @@ function allocComputer($blockids, $curre
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn getSemaphore($imageid, $imagerevisionid, $mgmtnodeid, $compid, $start,
+///                  $end, $requestid=0)
+///
+/// \param $imageid - id of image
+/// \param $imagerevisionid - id of image revision
+/// \param $mgmtnodeid - id of management node
+/// \param $compid - id of computer
+/// \param $start - start of reservation in datetime format
+/// \param $end - end of reservation in datetime format
+/// \param $requestid - (optional) if passed, ignores checking for conflict
+/// in request table for matching id
+///
+/// \return 0 on failure, 1 on success
+///
+/// \brief tries to get a semaphore for the requested computer
+///
+////////////////////////////////////////////////////////////////////////////////
+function getSemaphore($imageid, $imagerevisionid, $mgmtnodeid, $compid, $start,
+                      $end, $requestid=0) {
+       global $mysql_link_vcl, $uniqid;
+       $query = "INSERT INTO semaphore "
+              . "SELECT c.id, "
+              .        "$imageid, "
+              .        "$imagerevisionid, "
+              .        "$mgmtnodeid, "
+              .        "NOW() + INTERVAL " . SEMTIMEOUT . " SECOND, "
+              .        "'$uniqid' "
+                        . "FROM computer c "
+              . "LEFT JOIN semaphore s ON (c.id = s.computerid) "
+              . "WHERE c.id = $compid AND "
+              .       "(s.expires IS NULL OR s.expires < NOW()) "
+              . "LIMIT 1";
+       doQuery($query);
+       $rc = mysql_affected_rows($mysql_link_vcl);
+
+       # check to see if another process allocated this one
+       if($rc) {
+               $query = "SELECT rq.id "
+                      . "FROM request rq, "
+                      .      "reservation rs "
+                      . "WHERE rs.requestid = rq.id AND "
+                      .       "rs.computerid = $compid AND "
+                      .       "rq.start < '$end' AND "
+                      .       "rq.end > '$start' AND "
+                      .       "rq.stateid NOT IN (1, 5, 12)";
+               if($requestid)
+                       $query .= " AND rq.id != $requestid";
+               $qh = doQuery($query);
+               $rc2 = mysql_num_rows($qh);
+               if($rc2) {
+                       $query = "DELETE FROM semaphore "
+                              . "WHERE computerid = $compid AND "
+                              .       "procid = '$uniqid'";
+                       doQuery($query);
+                       return 0;
+               }
+       }
+       return $rc;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn retryGetSemaphore($imageid, $imagerevisionid, $mgmtnodeid, $compid,
+///                       $start, $end, $requestid=0, $tries=5, $delay=200000)
+///
+/// \param $imageid - id of image
+/// \param $imagerevisionid - id of image revision
+/// \param $mgmtnodeid - id of management node
+/// \param $compid - id of computer
+/// \param $start - start of reservation in datetime format
+/// \param $end - end of reservation in datetime format
+/// \param $requestid - (optional) if passed, ignores checking for conflict
+/// \param $tries - (optional, default=5) number of attempts to make for 
getting
+/// a semaphore
+/// \param $delay - (optional, default=200000) microseconds to wait between
+/// tries
+///
+/// \return 0 on failure, 1 on success
+///
+/// \brief makes multiple attempts to get a semaphore for a computer; useful
+/// when needing to do an operation on a specific computer
+///
+////////////////////////////////////////////////////////////////////////////////
+function retryGetSemaphore($imageid, $imagerevisionid, $mgmtnodeid, $compid,
+                           $start, $end, $requestid=0, $tries=5, 
$delay=200000) {
+       for($i = 0; $i < $tries; $i++) {
+               if(getSemaphore($imageid, $imagerevisionid, $mgmtnodeid, 
$compid, $start, $end, $requestid)) {
+                       return 1;
+               }
+               else
+                       usleep($delay);
+       }
+       return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn getPossibleRecentFailures($userid, $imageid)
 ///
 /// \param $userid - check log data for this user; if $userid = 0, check for
@@ -4373,8 +4844,7 @@ function getPossibleRecentFailures($user
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getMappedResources($resourcesubid, $resourcetype1,
-///                                 $resourcetype2)
+/// \fn getMappedResources($resourcesubid, $resourcetype1, $resourcetype2)
 ///
 /// \param $resourcesubid - id of a resource from its table (ie an imageid)
 /// \param $resourcetype1 - type of $resourcesubid (name or id)
@@ -4606,7 +5076,7 @@ function getMaxOverlap($userid) {
 ///
 
////////////////////////////////////////////////////////////////////////////////
 function addRequest($forimaging=0, $revisionid=array()) {
-       global $requestInfo, $user;
+       global $requestInfo, $user, $uniqid, $mysql_link_vcl;
        $startstamp = unixToDatetime($requestInfo["start"]);
        $endstamp = unixToDatetime($requestInfo["end"]);
        $now = time();
@@ -4628,18 +5098,6 @@ function addRequest($forimaging=0, $revi
        }
        $logid = $row[0];
 
-       $query = "INSERT INTO changelog "
-              .        "(logid, "
-              .        "start, "
-              .        "end, "
-              .        "timestamp) "
-              . "VALUES "
-              .        "($logid, "
-              .        "'$start', "
-              .        "'$endstamp', "
-              .        "NOW())";
-       doQuery($query, 136);
-
        # add single entry to request table
        $query = "INSERT INTO request "
               .        "(stateid, "
@@ -4690,24 +5148,51 @@ function addRequest($forimaging=0, $revi
                else
                        $blockdata = array();
 
-               $query = "INSERT INTO reservation "
-                      .        "(requestid, "
-                      .        "computerid, "
-                      .        "imageid, "
-                      .        "imagerevisionid, "
-                      .        "managementnodeid) "
-                      . "VALUES "
-                      .       "($requestid, "
-                      .       "$computerid, "
-                      .       "$imageid, "
-                      .       "$imagerevisionid, "
-                      .       "$mgmtnodeid)";
-               doQuery($query, 133);
                addSublogEntry($logid, $imageid, $imagerevisionid, $computerid,
                               $mgmtnodeid, $fromblock, $blockdata);
        }
+
+       $query = "INSERT INTO reservation "
+              .        "(requestid, "
+              .        "computerid, "
+              .        "imageid, "
+              .        "imagerevisionid, "
+              .        "managementnodeid) "
+              . "SELECT $requestid, "
+              .        "computerid, "
+              .        "imageid, "
+              .        "imagerevisionid, "
+              .        "managementnodeid " 
+              . "FROM semaphore "
+              . "WHERE expires > NOW() AND "
+              .       "procid = '$uniqid'";
+       doQuery($query);
+       $cnt = mysql_affected_rows($mysql_link_vcl);
+       if($cnt == 0) {
+               # reached this point SEMTIMEOUT seconds after getting 
semaphore, clean up and abort
+               $query = "DELETE FROM request WHERE id = $requestid";
+               doQuery($query);
+               $query = "UPDATE log SET wasavailable = 0 WHERE id = $logid";
+               doQuery($query);
+               $query = "DELETE FROM sublog WHERE logid = $logid";
+               doQuery($query);
+               abort(400);
+       }
+       else {
+               $query = "INSERT INTO changelog "
+                      .        "(logid, "
+                      .        "start, "
+                      .        "end, "
+                      .        "timestamp) "
+                      . "VALUES "
+                      .        "($logid, "
+                      .        "'$start', "
+                      .        "'$endstamp', "
+                      .        "NOW())";
+               doQuery($query, 136);
+       }
        // release semaphore lock
-       semUnlock();
+       cleanSemaphore();
 
        return $requestid;
 }
@@ -5033,7 +5518,11 @@ function updateRequest($requestid) {
                       .     "managementnodeid = $mgmtnodeid "
                       . "WHERE requestid = $requestid AND "
                       .       "imageid = $imgid AND "
-                      .       "computerid = $oldCompid";
+                      .       "computerid = $oldCompid "
+                                . "LIMIT 1"; # without this, it can update one 
row to have the
+                                   # same computer as another row; then, the 
later row
+                                   # could be updated, which would end up 
setting both
+                                   # rows to the same computer
                doQuery($query, 147);
                addChangeLogEntry($logid, NULL, $endstamp, $startstamp, 
$computerid, NULL, 
                                  1);
@@ -5120,12 +5609,17 @@ function deleteRequest($request) {
        }
 
        if($request['serverrequest']) {
-               $query = "DELETE FROM serverrequest WHERE requestid = 
{$request['id']}";
-               $qh = doQuery($query, 152);
+               $query = "SELECT id FROM serverrequest WHERE requestid = 
{$request['id']}";
+               $qh = doQuery($query);
+               if($row = mysql_fetch_assoc($qh)) {
+                       $query = "DELETE FROM serverrequest WHERE requestid = 
{$request['id']}";
+                       doQuery($query, 152);
+                       deleteVariable("fixedIPsr{$row['id']}");
+               }
        }
 
        $query = "DELETE FROM request WHERE id = {$request['id']}";
-       $qh = doQuery($query, 153);
+       doQuery($query, 153);
 
        $query = "DELETE FROM reservation WHERE requestid = {$request['id']}";
        doQuery($query, 154);
@@ -5145,7 +5639,8 @@ function deleteRequest($request) {
 /// reservations were found on $compid
 ///
 /// \brief attempts to move reservations off of a $compid - if $compid is not
-/// given, removes all reservations from the computer with the least number
+/// given, removes all reservations from the computer with the least number;
+/// NOTE - cleanSemaphore should be called after this by the calling function
 ///
 
////////////////////////////////////////////////////////////////////////////////
 function moveReservationsOffComputer($compid=0, $count=0) {
@@ -5198,10 +5693,14 @@ function moveReservationsOffComputer($co
                return -1;
        $images = getImages();
        $allmovable = 1;
+       # TODO eventually, for clusters, there will probably be restrictions on 
how the computers
+       # relate to each other; at that point, this will need to be updated to 
make sure the computer
+       # a reservation is reassigned to meets the same restrictions
        foreach($resInfo as $res) {
+               // pass forimaging = 1 so that isAvailable only looks at one 
computer
                $rc = isAvailable($images, $res["imageid"], 
$res['imagerevisionid'], 
                      datetimeToUnix($res["start"]), 
datetimeToUnix($res["end"]), 0,
-                     $res["userid"]);
+                     0, $res["userid"], 0, 1);
                if($rc < 1) {
                        $allmovable = 0;
                        break;
@@ -5211,8 +5710,8 @@ function moveReservationsOffComputer($co
                return 0;
        foreach($resInfo as $res) {
                $rc = isAvailable($images, $res["imageid"], 
$res['imagerevisionid'],
-                     datetimeToUnix($res["start"]), 
datetimeToUnix($res["end"]), 0, 
-                     $res["userid"]);
+                     datetimeToUnix($res["start"]), 
datetimeToUnix($res["end"]), 1, 
+                     0, $res["userid"], 0, 1);
                if($rc > 0) {
                        $newcompid = array_shift($requestInfo["computers"]);
                        # get mgmt node for computer
@@ -5227,7 +5726,8 @@ function moveReservationsOffComputer($co
                        addChangeLogEntry($res['logid'], NULL, NULL, NULL, 
$newcompid);
                        # update sublog entry
                        $query = "UPDATE sublog "
-                              . "SET computerid = $newcompid "
+                              . "SET computerid = $newcompid, "
+                              .     "managementnodeid = $mgmtnodeid "
                               . "WHERE logid = {$res['logid']} AND "
                               .       "computerid = $compid";
                        doQuery($query, 101);
@@ -5240,23 +5740,71 @@ function moveReservationsOffComputer($co
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getCompFinalReservationTime($compid)
+/// \fn moveReservationsOffVMs($compid, $sem)
+///
+/// \param $compid - id of host computer from which to move
+/// reservations
+/// \param $sem - (optional) array of data to be used when getting a semaphore
+/// for a VM after reservations have been moved off of it; must include these
+/// keys: imageid, revid, mnid, start (datetime), end (datetime)
+///
+/// \return 0 if failed to move reservations, 1 if succeeded, -1 if no
+/// reservations were found on $compid
+///
+/// \brief attempts to move reservations off of any VMs assigned to a $compid
+/// NOTE - cleanSemaphore should be called after this by the calling function
+///
+////////////////////////////////////////////////////////////////////////////////
+function moveReservationsOffVMs($compid, $sem=0) {
+       if(! is_array($sem)) {
+               $sem = array();
+               $resources = getUserResources(array("imageAdmin", 
"imageCheckOut"));
+               $tmp = array_keys($resources['image']);
+               $sem['imageid'] = $tmp[0];
+               $sem['revid'] = getProductionRevisionid($sem['imageid']);
+               $tmp = array_keys($resources['managementnode']);
+               $sem['mnid'] = $tmp[0];
+               $sem['start'] = unixToDatetime(time());
+               $sem['end'] = '2038-01-01 00:00:00';
+       }
+       $query = "SELECT vm.id "
+              . "FROM computer vm, "
+              .      "vmhost v "
+              . "WHERE v.computerid = $compid AND "
+              .       "vm.vmhostid = v.id";
+       $qh = doQuery($query);
+       while($row = mysql_fetch_assoc($qh)) {
+               $rc = moveReservationsOffComputer($row['id']);
+               if($rc != 0)
+                       # lock computer so that reservations on other VMs on 
this host do not get moved to it
+                       getSemaphore($sem['imageid'], $sem['revid'], 
$sem['mnid'], $row['id'], $sem['start'], $sem['end']);
+       }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getCompFinalReservationTime($compid, $extraskipstate=0)
 ///
 /// \param $compid - a computer id
+/// \param $extraskipstate - (default=0) id of an additional request state to
+/// ignore; if 0 passed, no additional states are ignored
 ///
 /// \return unix timestamp of last end time of any reservations for $compid
 ///
 /// \brief determines the final end time of all reservations on a computer
 ///
 
////////////////////////////////////////////////////////////////////////////////
-function getCompFinalReservationTime($compid) {
+function getCompFinalReservationTime($compid, $extraskipstate=0) {
        $end = 0;
+       $skipstates = "1,5,12";
+       if($extraskipstate)
+               $skipstates .= ",$extraskipstate";
        $query = "SELECT UNIX_TIMESTAMP(rq.end) as end "
               . "FROM request rq, "
               .      "reservation rs "
               . "WHERE rs.requestid = rq.id AND "
               .       "rs.computerid = $compid AND "
-              .       "rq.stateid NOT IN (1,5,12) "
+              .       "rq.stateid NOT IN ($skipstates) "
               . "ORDER BY rq.end DESC "
               . "LIMIT 1";
        $qh = doQuery($query, 101);
@@ -5279,6 +5827,153 @@ function getCompFinalReservationTime($co
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn getCompFinalVMReservationTime($hostid, $addsemaphores, 
$notomaintenance)
+///
+/// \param $hostid - computer id of a vm host
+/// \param $addsemaphores - (optional, default = 0) 1 to add semaphores for 
each
+/// of the VMs
+/// \param $notomaintenance - (optional, default = 0) 1 to ignore any
+/// tomaintenance reservations
+///
+/// \return unix timestamp of last end time of any reservations for VMs on
+/// $hostid; 0 if no reservations; -1 if $addsemaphores = 1 and failed to get
+/// semaphores
+///
+/// \brief determines the final end time of all reservations of all VMs on a
+/// VM host computer
+///
+////////////////////////////////////////////////////////////////////////////////
+function getCompFinalVMReservationTime($hostid, $addsemaphores=0,
+                                       $notomaintenance=0) {
+       global $uniqid, $mysql_link_vcl;
+       if($addsemaphores) {
+               $query = "SELECT vm.id "
+                      . "FROM computer vm, "
+                      .      "vmhost v "
+                      . "WHERE v.computerid = $hostid AND "
+                      .       "vm.vmhostid = v.id";
+               $qh = doQuery($query);
+               $compids = array();
+               while($row = mysql_fetch_assoc($qh))
+                       $compids[] = $row['id'];
+               if(empty($compids))
+                       return 0;
+               $allcompids = implode(',', $compids);
+               $imageid = getImageId('noimage');
+               $revid = getProductionRevisionid($imageid);
+               $tmp = getManagementNodes();
+               $tmp = array_keys($tmp);
+               $mnid = $tmp[0];
+               $query = "INSERT INTO semaphore "
+                      . "SELECT c.id, "
+                      .        "$imageid, "
+                      .        "$revid, "
+                      .        "$mnid, "
+                      .        "NOW() + INTERVAL " . SEMTIMEOUT . " SECOND, "
+                      .        "'$uniqid' "
+                                . "FROM computer c "
+                      . "LEFT JOIN semaphore s ON (c.id = s.computerid) "
+                      . "WHERE c.id IN ($allcompids) AND "
+                      .       "(s.expires IS NULL OR s.expires < NOW()) "
+                      . "GROUP BY c.id";
+               doQuery($query);
+               $cnt = mysql_affected_rows($mysql_link_vcl);
+               if($cnt != count($compids))
+                       return -1;
+       }
+
+       $end = 0;
+       $skipstates = '1,5,12';
+       if($notomaintenance)
+               $skipstates .= ',18';
+       $query = "SELECT UNIX_TIMESTAMP(rq.end) as end "
+              . "FROM request rq, "
+              .      "reservation rs "
+              . "WHERE rs.requestid = rq.id AND "
+              .       "rq.stateid NOT IN ($skipstates) AND "
+              .       "rs.computerid IN (SELECT vm.id "
+              .                          "FROM computer vm, "
+              .                               "vmhost v "
+              .                          "WHERE v.computerid = $hostid AND "
+              .                                "vm.vmhostid = v.id) "
+              . "ORDER BY rq.end DESC "
+              . "LIMIT 1";
+       $qh = doQuery($query, 101);
+       if($row = mysql_fetch_assoc($qh))
+               $end = $row['end'];
+       $query = "SELECT UNIX_TIMESTAMP(t.end) as end "
+              . "FROM blockComputers c, "
+              .      "blockTimes t "
+              . "WHERE c.blockTimeid = t.id AND "
+              .       "t.end > NOW() AND "
+              .       "c.computerid IN (SELECT vm.id "
+              .                        "FROM computer vm, "
+              .                             "vmhost v "
+              .                        "WHERE v.computerid = $hostid AND "
+              .                              "vm.vmhostid = v.id) "
+              . "ORDER BY t.end DESC "
+              . "LIMIT 1";
+       $qh = doQuery($query, 101);
+       if($row = mysql_fetch_assoc($qh))
+               if($row['end'] > $end)
+                       $end = $row['end'];
+       return $end;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getExistingChangeStateStartTime($compid, $stateid)
+///
+/// \param $compid - computer id
+/// \param $stateid - id of state of reservation
+///
+/// \return unix timestamp
+///
+/// \brief gets the start time for the earliest existing reservation for 
$compid
+/// that has a state of $stateid
+///
+////////////////////////////////////////////////////////////////////////////////
+function getExistingChangeStateStartTime($compid, $stateid) {
+       $query = "SELECT rq.start "
+              . "FROM request rq, "
+              .      "reservation rs "
+              . "WHERE rs.requestid = rq.id AND "
+              .       "rs.computerid = $compid AND "
+              .       "rq.stateid = $stateid AND "
+              .       "rq.start > NOW() "
+              . "ORDER BY rq.start "
+              . "LIMIT 1";
+       $qh = doQuery($query);
+       if($row = mysql_fetch_assoc($qh))
+               return datetimeToUnix($row['start']);
+       return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn updateExistingToState($compid, $start, $stateid)
+///
+/// \param $compid - computer id
+/// \param $start - new start time in datetime format
+/// \param $stateid - id of state for existing reservation
+///
+/// \brief updates the start time of an existing reservation for $compid with a
+/// state of $stateid
+///
+////////////////////////////////////////////////////////////////////////////////
+function updateExistingToState($compid, $start, $stateid) {
+       $query = "UPDATE request rq, "
+              .        "reservation rs "
+              . "SET rq.start = '$start' "
+              . "WHERE rs.requestid = rq.id AND "
+              .       "rq.stateid = $stateid AND "
+              .       "rq.start > '$start' AND "
+              .       "rs.computerid = $compid";
+       doQuery($query);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn getUserRequests($type, $id)
 ///
 /// \param $type - "normal", "forimaging", or "all"
@@ -5850,6 +6545,50 @@ function getProvisioning() {
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn getProvisioningTypes()
+///
+/// \return array of provisioning types allowed for each computer type of this
+/// form:\n
+/// {[blade] => {[id] => type,\n
+///              [id] => type},\n
+///  [lab] => {[id] => type}\n
+///  [virtualmachine] => {[id] => type,\n
+///                       [id] => type}\n
+///
+/// \brief generates an array of provisioning types allowed for each computer
+/// type
+///
+////////////////////////////////////////////////////////////////////////////////
+function getProvisioningTypes() {
+       $query = "SELECT id, prettyname FROM provisioning WHERE name = 'none'";
+       $qh = doQuery($query);
+       $none = mysql_fetch_assoc($qh);
+       $query = "SELECT p.id, "
+              .        "p.prettyname, "
+              .        "o.name AS `type` "
+              . "FROM provisioning p, "
+              .      "OSinstalltype o, "
+              .      "provisioningOSinstalltype po "
+              . "WHERE po.provisioningid = p.id AND "
+              .       "po.OSinstalltypeid = o.id "
+              . "ORDER BY o.name";
+       $qh = doQuery($query);
+       $types = array('blade' => array($none['id'] => $none['prettyname']),
+                      'lab' => array(),
+                      'virtualmachine' => array());
+       while($row = mysql_fetch_assoc($qh)) {
+               if($row['type'] == 'kickstart' || $row['type'] == 'partimage')
+                       $types['blade'][$row['id']] = $row['prettyname'];
+               if($row['type'] == 'none')
+                       $types['lab'][$row['id']] = $row['prettyname'];
+               if($row['type'] == 'vbox' || $row['type'] == 'vmware')
+                       $types['virtualmachine'][$row['id']] = 
$row['prettyname'];
+       }
+       return $types;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn getSchedules()
 ///
 /// \return array of schedules where the index are the id from the schedule 
table,
@@ -5857,6 +6596,7 @@ function getProvisioning() {
 /// \b name - name of schedule\n
 /// \b ownerid - user id of owner\n
 /// \b owner - unity id of owner\n
+/// \b resourceid - id from resource table\n
 /// \b times - array of start and end times for the schedule
 ///
 /// \brief gets information for schedules in schedule table
@@ -5943,11 +6683,15 @@ function formatMinOfWeek($min) {
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getManagementNodes($alive)
+/// \fn getManagementNodes($alive, $includedeleted, $id)
 ///
 /// \param $alive - (optional) if given, only return "alive" nodes, can be
 ///                 either "now" or "future" so we know how recently it must
 ///                 have checked in
+/// \param $includedeleted - (optional, default=0) 1 to include management
+///                 nodes with a state of deleted, 0 to leave them out
+/// \param $id - (optional, default is all nodes) specify an id of a management
+///                 node to only have data for that node returned
 ///
 /// \return an array of management nodes where eash index is the id from the
 /// managementnode table and each element is an array of data about the node
@@ -5957,7 +6701,7 @@ function formatMinOfWeek($min) {
 /// if $alive = future, must have checked in within 1 hour
 ///
 
////////////////////////////////////////////////////////////////////////////////
-function getManagementNodes($alive="neither") {
+function getManagementNodes($alive="neither", $includedeleted=0, $id=0) {
        if($alive == "now")
                $lastcheckin = unixToDatetime(time() - 300);
        elseif($alive == "future")
@@ -5988,7 +6732,9 @@ function getManagementNodes($alive="neit
               .        "m.sharedMailBox AS sharedmailbox, "
               .        "r.id as resourceid, "
               .        "m.predictivemoduleid, "
-              .        "mo.prettyname AS predictivemodule "
+              .        "mo.prettyname AS predictivemodule, "
+              .        "m.availablenetworks, "
+              .        "m.NOT_STANDALONE AS federatedauth "
               . "FROM user u, "
               .      "state s, "
               .      "resource r, "
@@ -6004,6 +6750,10 @@ function getManagementNodes($alive="neit
               .       "rt.name = 'managementnode' AND "
               .       "u.affiliationid = a.id AND "
               .       "m.predictivemoduleid = mo.id";
+       if($id != 0)
+               $query .= " AND m.id = $id";
+       if($includedeleted == 0)
+               $query .= " AND s.name != 'deleted'";
        if($alive == "now" || $alive == "future") {
                $query .= " AND m.lastcheckin > '$lastcheckin'"
                       .  " AND s.name != 'maintenance'";
@@ -6012,12 +6762,107 @@ function getManagementNodes($alive="neit
        $return = array();
        while($row = mysql_fetch_assoc($qh)) {
                $return[$row["id"]] = $row;
+               $return[$row['id']]['availablenetworks'] = explode(',', 
$row['availablenetworks']);
+               if($row['state'] == 'deleted')
+                       $return[$row['id']]['deleted'] = 1;
+               else
+                       $return[$row['id']]['deleted'] = 0;
        }
+
+       # Get items from variable table for specific management node id
+       foreach ($return as $mn_id => $value ) {
+               if(array_key_exists("hostname", $value)) {
+                       $mn_hostname = $value['hostname'];
+                       $timeservers = getVariable('timesource|'.$mn_hostname);
+                       if($timeservers == NULL) {
+                               $timeservers = getVariable('timesource|global');
+                       }
+                       $return[$mn_id]['timeservers'] = $timeservers;
+               }
+       }
+       
        return $return;
 }
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn getMnsFromImage($imageid)
+///
+/// \param $imageid - id of an image
+///
+/// \return array of management node ids
+///
+/// \brief determines which management nodes can handle an image based on
+/// image group to computer group, then computer group to management node group
+/// mapping
+///
+////////////////////////////////////////////////////////////////////////////////
+function getMnsFromImage($imageid) {
+       $comps = getMappedResources($imageid, 'image', 'computer');
+       if(empty($comps))
+               return array();
+       $inlist = implode(',', $comps);
+       $query = "SELECT DISTINCT rgm.resourcegroupid "
+              . "FROM resourcegroupmembers rgm, "
+              .      "resource r, "
+              .      "computer c "
+              . "WHERE c.id = r.subid AND "
+              .       "r.resourcetypeid = 12 AND "
+              .       "r.id = rgm.resourceid AND "
+              .       "c.id in ($inlist)";
+       $qh = doQuery($query);
+       $compgroups = array();
+       while($row = mysql_fetch_assoc($qh))
+               $compgroups[] = $row['resourcegroupid'];
+       $mngrps = array();
+       foreach($compgroups as $grpid) {
+               $mngrpset = getResourceMapping('managementnode', 'computer', 
'', implode(',', $compgroups));
+               foreach($mngrpset as $mngrpid => $compgrpset)
+                       $mngrps[$mngrpid] = 1;
+       }
+       $mngrpnames = array();
+       foreach(array_keys($mngrps) as $mnid) {
+               $mngrpnames[] = getResourceGroupName($mnid);
+       }
+       $mns = getResourcesFromGroups($mngrpnames, 'managementnode', 0);
+       $mnids = array();
+       foreach($mns as $mnid => $name)
+               $mnids[$mnid] = 1;
+       return array_keys($mnids);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn checkAvailableNetworks($ip)
+///
+/// \param $ip - public ip address for a reservation
+///
+/// \return array of management node ids that can handle $ip
+///
+/// \brief finds any management nodes that can manage networks containing $ip
+///
+////////////////////////////////////////////////////////////////////////////////
+function checkAvailableNetworks($ip) {
+       $ip = ip2long($ip);
+       $mnids = array();
+       $mns = getManagementNodes();
+       foreach($mns as $mn) {
+               foreach($mn['availablenetworks'] as $net) {
+                       if($net == '')
+                               continue;
+                       list($net, $netmask) = explode('/', $net);
+                       $net = ip2long($net);
+                       $mask = pow(2, (32 - $netmask)) - 1;
+                       $mask = ~ $mask;
+                       if(($ip & $mask) == ($net & $mask))
+                               $mnids[$mn['id']] = 1;
+               }
+       }
+       return array_keys($mnids);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn getPredictiveModules()
 ///
 /// \return an array of predictive loading modules where the index is the 
module
@@ -6043,8 +6888,9 @@ function getPredictiveModules() {
 
 
////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getTimeSlots($end, $start)
+/// \fn getTimeSlots($compids, $end, $start)
 ///
+/// \param $compids - array of computer ids
 /// \param $end - (optional) end time as unix timestamp
 /// \param $start - (optional) start time as unix timestamp
 ///
@@ -6755,12 +7601,19 @@ function findAvailableTimes($start, $end
        $query = "SELECT c.id AS compid "
               . "FROM computer c, "
               .      "image i, "
-              .      "state s "
+              .      "state s, "
+              .      "provisioningOSinstalltype poi, "
+              .      "OSinstalltype oi, "
+              .      "OS o "
               . "WHERE c.stateid = s.id AND "
               .       "i.id = $imageid AND "
               .       "s.name NOT IN ($nowignorestates) AND "
               .       "c.platformid = $platformid AND "
               .       "c.scheduleid IN ($schedules) AND "
+              .       "i.OSid = o.id AND "
+              .       "o.installtype = oi.name AND "
+              .       "oi.id = poi.OSinstalltypeid AND "
+              .       "poi.provisioningid = c.provisioningid AND "
               .       "c.RAM >= i.minram AND "
               .       "c.procnumber >= i.minprocnumber AND "
               .       "c.procspeed >= i.minprocspeed AND "
@@ -6774,9 +7627,9 @@ function findAvailableTimes($start, $end
        $query .=                         "DATE_ADD(rq.end, INTERVAL 15 MINUTE) 
>= '$startdt' AND "
               .                          "rs.computerid IN ($incompids)) AND "
               .       "c.id IN ($incompids) "
-              . "ORDER BY (c.procspeed * c.procnumber) DESC, "
-              .          "RAM DESC, "
-              .          "network DESC";
+              . "ORDER BY RAM, "
+              .          "(c.procspeed * c.procnumber), "
+              .          "network";
        $qh = doQuery($query, 101);
        while($row = mysql_fetch_assoc($qh)) {
                $row['duration'] = $reqduration;
@@ -6799,7 +7652,10 @@ function findAvailableTimes($start, $end
                       .      "reservation rs2, "
                       .      "image i, "
                       .      "state s, "
-                      .      "computer c "
+                      .      "computer c, "
+                      .      "provisioningOSinstalltype poi, "
+                      .      "OSinstalltype oi, "
+                      .      "OS o "
                       . "WHERE rq1.id = rs1.requestid AND "
                       .       "rs2.requestid = rq2.id AND "
                       .       "rq1.id != rq2.id AND "
@@ -6811,6 +7667,10 @@ function findAvailableTimes($start, $end
                       .       "c.id = rs1.computerid AND "
                       .       "c.platformid = $platformid AND "
                       .       "c.scheduleid IN ($schedules) AND "
+                      .       "i.OSid = o.id AND "
+                      .       "o.installtype = oi.name AND "
+                      .       "oi.id = poi.OSinstalltypeid AND "
+                      .       "poi.provisioningid = c.provisioningid AND "
                       .       "c.RAM >= i.minram AND "
                       .       "c.procnumber >= i.minprocnumber AND "
                       .       "c.procspeed >= i.minprocspeed AND "
@@ -6857,7 +7717,10 @@ function findAvailableTimes($start, $end
               .      "reservation rs, "
               .      "image i, "
               .      "state s, "
-              .      "computer c "
+              .      "computer c, "
+              .      "provisioningOSinstalltype poi, "
+              .      "OSinstalltype oi, "
+              .      "OS o "
               . "WHERE rs.requestid = rq.id AND "
               .       "(rq.start > '$startdt' OR "
               .        "(DATE_ADD(rq.end, INTERVAL 15 MINUTE) > '$startdt' AND 
rq.start <= '$startdt')) AND "
@@ -6866,6 +7729,10 @@ function findAvailableTimes($start, $end
               .       "c.id = rs.computerid AND "
               .       "c.platformid = $platformid AND "
               .       "c.scheduleid IN ($schedules) AND "
+              .       "i.OSid = o.id AND "
+              .       "o.installtype = oi.name AND "
+              .       "oi.id = poi.OSinstalltypeid AND "
+              .       "poi.provisioningid = c.provisioningid AND "
               .       "c.RAM >= i.minram AND "
               .       "c.procnumber >= i.minprocnumber AND "
               .       "c.procspeed >= i.minprocspeed AND "
@@ -6904,7 +7771,10 @@ function findAvailableTimes($start, $end
               .      "reservation rs, "
               .      "image i, "
               .      "state s, "
-              .      "computer c "
+              .      "computer c, "
+              .      "provisioningOSinstalltype poi, "
+              .      "OSinstalltype oi, "
+              .      "OS o "
               . "WHERE rs.requestid = rq.id AND "
               .       "(rq.start > '$startdt' OR "
               .        "(DATE_ADD(rq.end, INTERVAL 15 MINUTE) > '$startdt' AND 
rq.start <= '$startdt')) AND "
@@ -6913,10 +7783,15 @@ function findAvailableTimes($start, $end
               .       "c.id = rs.computerid AND "
               .       "c.platformid = $platformid AND "
               .       "c.scheduleid IN ($schedules) AND "
+              .       "i.OSid = o.id AND "
+              .       "o.installtype = oi.name AND "
+              .       "oi.id = poi.OSinstalltypeid AND "
+              .       "poi.provisioningid = c.provisioningid AND "
               .       "c.RAM >= i.minram AND "
               .       "c.procnumber >= i.minprocnumber AND "
               .       "c.procspeed >= i.minprocspeed AND "
               .       "c.network >= i.minnetwork AND "
+              .       "c.deleted = 0 AND "
               .       "c.stateid = s.id AND "
               .       "s.name NOT IN ($ignorestates) AND ";
        if($reqid != '')
@@ -7249,7 +8124,7 @@ function getComputers($sort=0, $included
               .        "c.platformid AS platformid, "
               .        "sc.name AS schedule, "
               .        "c.scheduleid AS scheduleid, "
-              .        "cur.name AS currentimg, "
+              .        "cur.prettyname AS currentimg, "
               .        "c.currentimageid AS currentimgid, "
               .        "c.imagerevisionid, "
               .        "next.name AS nextimg, "
@@ -7269,6 +8144,7 @@ function getComputers($sort=0, $included
               .        "c.notes, "
               .        "c.vmhostid, "
               .        "c2.hostname AS vmhost, "
+              .        "c2.id AS vmhostcomputerid, "
               .        "c.location, "
               .        "c.provisioningid, "
               .        "pr.prettyname AS provisioning, "
@@ -7852,7 +8728,7 @@ function isImageBlockTimeActive($imageid
 /// \param $selectedid - (optional) index of $dataArr to be initially selected;
 /// use -1 for nothing to be selected
 /// \param $skip - (optional) this is used if the array from getImages is 
passed
-/// as $dataArr so we know to skip index 4 since it is the noimage element
+/// as $dataArr so we know to skip the 'No Image" element
 /// \param $multiple - (optional) use this to print select input with the
 /// multiple tag set
 /// \param $domid - (optional) use this to pass in the javascript id to be used
@@ -7867,6 +8743,66 @@ function isImageBlockTimeActive($imageid
 
////////////////////////////////////////////////////////////////////////////////
 function printSelectInput($name, $dataArr, $selectedid=-1, $skip=0, 
$multiple=0,
                           $domid="", $extra="") {
+       print selectInputHTML($name, $dataArr, $domid, $extra, $selectedid, 
$skip,
+                             $multiple);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn selectInputAutoDijitHTML($name, $dataArr, $domid='', $extra='',
+///                              $selectedid=-1) {
+///
+/// \param $name - name of input element
+/// \param $dataArr - array containing options
+/// \param $domid - (optional) use this to pass in the javascript id to be used
+/// for the select object
+/// \param $extra - (optional) any extra attributes that need to be set
+/// \param $selectedid - (optional) index of $dataArr to be initially selected;
+/// use -1 for nothing to be selected
+///
+/// \return html
+///
+/// \brief wrapper for calling selectInputHTML with the resulting element
+/// being a dijit.form.Select if number of items is <= 10 and being a
+/// dijit.form.FilteringSelect if number of items is > 10
+///
+////////////////////////////////////////////////////////////////////////////////
+function selectInputAutoDijitHTML($name, $dataArr, $domid='', $extra='',
+                                  $selectedid=-1) {
+       if(count($dataArr) > 10 &&
+          USEFILTERINGSELECT && count($dataArr) < FILTERINGSELECTTHRESHOLD)
+               $type = 'dojoType="dijit.form.FilteringSelect" 
queryExpr="*${0}*"';
+       else
+               $type = 'dojoType="dijit.form.Select" maxHeight="250"';
+       return selectInputHTML($name, $dataArr, $domid, "$type $extra", 
$selectedid);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn selectInputHTML($name, $dataArr, $domid, $extra, $selectedid, $skip,
+///                     $multiple)
+///
+/// \param $name - name of input element
+/// \param $dataArr - array containing options
+/// \param $domid - (optional) use this to pass in the javascript id to be used
+/// for the select object
+/// \param $extra - (optional) any extra attributes that need to be set
+/// \param $selectedid - (optional) index of $dataArr to be initially selected;
+/// use -1 for nothing to be selected
+/// \param $skip - (optional) this is used if the array from getImages is 
passed
+/// as $dataArr so we know to skip the 'No Image" element
+/// \param $multiple - (optional) use this to print select input with the
+/// multiple tag set
+///
+/// \brief generates HTML for select input
+/// it is assumed that if $selectedid is left off, we assume $dataArr has no 
+/// index '-1'\n
+/// each OPTION's value is the index of that element of the array
+///
+////////////////////////////////////////////////////////////////////////////////
+function selectInputHTML($name, $dataArr, $domid="", $extra="", $selectedid=-1,
+                         $skip=0, $multiple=0) {
+       $h = '';
        if(! empty($domid))
                $domid = "id=\"$domid\"";
        if($multiple)
@@ -7874,52 +8810,218 @@ function printSelectInput($name, $dataAr
        else
                $multiple = "";
        if($name != '')
-               print "      <select name=$name $multiple $domid $extra>\n";
+               $h .= "      <select name=$name $multiple $domid $extra>\n";
        else
-               print "      <select $multiple $domid $extra>\n";
+               $h .= "      <select $multiple $domid $extra>\n";
        foreach(array_keys($dataArr) as $id) {
-               if(($skip && $id == 4) || ($dataArr[$id] != 0 && 
empty($dataArr[$id])))
+               if(($dataArr[$id] != 0 && empty($dataArr[$id])))
                        continue;
                if($id == $selectedid)
-                  print "        <option value=\"$id\" selected=\"selected\">";

[... 2112 lines stripped ...]

Reply via email to