post('/ous/:ouid/images/:imageid/reserve(/)', 'validateApiKey', function($ouid, $imageid) use ($app) { global $cmd; global $AMBITO_ORDENADORES; global $EJECUCION_COMANDO; global $ACCION_INICIADA; global $ACCION_FINALIZADA; global $ACCION_SINRESULTADO; global $ACCION_FALLIDA; global $userid; $response = []; $ogagent = []; $repo = []; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Init."); // Checking parameters. try { if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']); } if (!checkIds($ouid, $imageid)) { throw new Exception("Ids. must be positive integers"); } // Reading POST parameters in JSON format. $input = json_decode($app->request()->getBody()); // Default: no lab. filter. if (isset($input->labid)) { $labid = $input->labid != "0" ? $input->labid : '%'; } else { $labid = '%'; } $maxtime = isset($input->maxtime) ? $input->maxtime : 24; // Default: 24 h. $opts = Array('options' => Array('min_range' => 1)); // Check for int>0 if (filter_var($labid, FILTER_VALIDATE_INT, $opts) === false and $labid !== '%') { throw new Exception("Lab id. must be positive integer"); } if (filter_var($maxtime, FILTER_VALIDATE_INT, $opts) === false) { throw new Exception("Time must be positive integer (in hours)"); } } catch (Exception $e) { // Communication error. $response["message"] = $e->getMessage(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"]."."); jsonResponse(400, $response); $app->stop(); } if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Parameters: labid=$labid, maxtime=$maxtime"); // Choose older not-reserved client with image installed and get ogAdmServer data. $cmd->texto = <<Comando=&$cmd; if (!$rs->Abrir()) return(false); // Error opening recordset. // Check if user is admin and client exists. $rs->Primero(); if (checkAdmin($rs->campos["idusuario"]) and checkParameter($rs->campos["idordenador"])) { // Read query data. $clntid = $rs->campos["idordenador"]; $clntname = $rs->campos["nombreordenador"]; $clntip = $rs->campos["ip"]; $clntmac = $rs->campos["mac"]; $clntnetmask = $rs->campos["mascara"]; $agentkey = $rs->campos["agentkey"]; $disk = $rs->campos["numdisk"]; $part = $rs->campos["numpar"]; $labid = $rs->campos["idaula"]; $ouid = $rs->campos["idcentro"]; $repoip = $rs->campos["repoip"]; $repokey = $rs->campos["repokey"]; // Check client's status. $ogagent = [['url' => "https://$clntip:8000/opengnsys/status"]]; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": OGAgent status, url=".$ogagent[0]['url']."."); $result = multiRequest($ogagent); if (empty($result[0]['data'])) { // Client is off, send WOL command to ogAdmServer. // TODO: if client is busy????? if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Send boot command through ogAdmServer: iph=$clntip,mac=$clntmac."); wol(1, [$clntip]); // Send WOL command to client repository. $repo = [['url' => "https://$repoip/opengnsys/rest/repository/poweron", 'header' => ["Authorization: $repokey"], 'post' => '{"macs": ["'.$clntmac.'"], "ips": ["'.$clntip.'"]}']]; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Send Boot command through repo: repo=$repoip,ip=$clntip,mac=$clntmac."); $result = multiRequest($repo); // ... (check response) //if ($result[0]['code'] != 200) { // ... } else { // Client is on, send a reboot command to its OGAgent. $ogagent = [['url' => "https://$clntip:8000/opengnsys/reboot", 'header' => ["Authorization: ".$agentkey]]]; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": OGAgent reboot, url=".$ogagent[0]['url']."."); $result = multiRequest($ogagent); // ... (check response) //if ($result[0]['code'] != 200) { // ... } // DB Transaction: mark choosed client as reserved and // create an init session command into client's actions queue. $cmd->texto = "START TRANSACTION;"; $cmd->Ejecutar(); $timestamp = time(); $cmd->texto = <<Ejecutar(); $cmd->texto = <<Ejecutar(); create_schedule_now(strval($timestamp), $EJECUCION_COMANDO, "auto-queue-remotepc-reserve-".$timestamp); // Create event to remove reservation on timeout (15 min.). $timeout = "15 MINUTE"; $cmd->texto = <<Ejecutar(); if ($t1 and $t2 and $t3) { // Commit transaction on success. $cmd->texto = "COMMIT;"; $cmd->Ejecutar(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": DB tables and events updated, clntid=$clntid."); // Send init session command if client is booted on ogLive. if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Send Init Session command to ogAdmClient, ido=$clntid,iph=$clntip,dsk=$disk,par=$part."); session($clntip, "$disk\r$part"); // Compose JSON response. $response['id'] = (int)$clntid; $response['name'] = $clntname; $response['ip'] = $clntip; $response['mac'] = $clntmac; $response['lab']['id'] = $labid; $response['ou']['id'] = (int)$ouid; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Response, ".var_export($response,true)."."); jsonResponse(200, $response); } else { // Roll-back transaction on DB error. $cmd->texto = "ROLLBACK;"; $cmd->Ejecutar(); // Error message. $response["message"] = "Database error: $t1, $t2, $t3"; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"]."."); jsonResponse(400, $response); } } else { if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": UNASSIGNED"); } $rs->Cerrar(); $app->stop(); } ); /** * @brief Store UDS server URLs to resend some events recieved from OGAgent. * @note Route: /ous/:ouid/labs/:labid/clients/:clntid/events, Method: POST * @param string urlLogin URL to redirect login notification. * @param string urlLogout URL to redirect logout notification. * @param string urlRelease URL to release a session * @warning Events parameters will be stored in a new "remotepc" table. */ $app->post('/ous/:ouid/labs/:labid/clients/:clntid/events', 'validateApiKey', function($ouid, $labid, $clntid) use ($app) { global $cmd; global $userid; $response = Array(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Init."); // Checking parameters. try { if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']); } if (!checkIds($ouid, $labid, $clntid)) { throw new Exception("Ids. must be positive integers"); } // Reading JSON parameters. $input = json_decode($app->request()->getBody()); $urlLogin = htmlspecialchars($input->urlLogin); $urlLogout = htmlspecialchars($input->urlLogout); $urlRelease = htmlspecialchars($input->urlRelease ?? ""); if (filter_var($urlLogin, FILTER_VALIDATE_URL) === false) { throw new Exception("Must be a valid URL for login notification"); } if (filter_var($urlLogout, FILTER_VALIDATE_URL) === false) { throw new Exception("Must be a valid URL for logout notification"); } } catch (Exception $e) { // Error message. $response["message"] = $e->getMessage(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"]."."); jsonResponse(400, $response); $app->stop(); } if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Parameters: urlLogin=$urlLogin, urlLogout=$urlLogout"); // Select client data for UDS compatibility. $cmd->texto = <<Comando=&$cmd; if (!$rs->Abrir()) return(false); // Error opening recordset. // Check if user is admin and client exists. $rs->Primero(); if (checkAdmin($rs->campos["idusuario"]) and checkParameter($rs->campos["idordenador"])) { // Check if client is reserved. if (! is_null($rs->campos["reserved"])) { // Updating DB if client is reserved. $cmd->CreaParametro("@urllogin", $urlLogin, 0); $cmd->CreaParametro("@urllogout", $urlLogout, 0); $cmd->CreaParametro("@urlrelease", $urlRelease, 0); $cmd->texto = <<Ejecutar()) { // Confirm operation. $response = ""; jsonResponse(200, $response); } else { // Error message. $response["message"] = "Database error"; jsonResponse(400, $response); } } else { // Error message. $response["message"] = "Client is not reserved"; jsonResponse(400, $response); } } $rs->Cerrar(); $app->stop(); } ); /* * @brief Store session time (in sec). * @note Route: /ous/:ouid/labs/:labid/clients/:clntid/session, Method: POST * @param int deadLine maximum session time, in seconds (0 for unlimited) * @warning Parameters will be stored in a new "remotepc" table. */ $app->post('/ous/:ouid/labs/:labid/clients/:clntid/session', 'validateApiKey', function($ouid, $labid, $clntid) use ($app) { global $cmd; global $userid; $response = Array(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Init."); // Checking parameters. try { if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']); } if (!checkIds($ouid, $labid, $clntid)) { throw new Exception("Ids. must be positive integers"); } // Reading JSON parameters. $input = json_decode($app->request()->getBody()); $deadLine = $input->deadLine; if (filter_var($deadLine, FILTER_VALIDATE_INT) === false) { throw new Exception("Deadline must be integer"); } if ($deadLine < 0) { throw new Exception("Resource unavailable"); } } catch (Exception $e) { // Error message. $response["message"] = $e->getMessage(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"]."."); jsonResponse(400, $response); $app->stop(); } if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Parameters: deadLine=$deadLine"); // Get client's data. $cmd->texto = <<Comando=&$cmd; if (!$rs->Abrir()) return(false); // Error opening recordset. // Check if user is admin and client exists. $rs->Primero(); if (checkAdmin($rs->campos["idusuario"]) and checkParameter($rs->campos["idordenador"])) { // Check if client is reserved. if (! is_null($rs->campos["urllogin"])) { // Read query data. $clntid = $rs->campos["idordenador"]; # Removing previous commands from OGAgent operations queue. if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Updating database."); $cmd->texto = <<Ejecutar(); # Add new commands to OGAgent operations queue. $cmd->texto = "INSERT INTO ogagent_queue (clientid, exectime, operation) VALUES"; if ($deadLine > 600) { # Add reminder 10 min. before deadline. $cmd->texto .= " ($clntid, NOW() + INTERVAL $deadLine SECOND - INTERVAL 10 MINUTE, 'popup-10'),"; } if ($deadLine > 300) { # Add reminder 5 min. before deadline. $cmd->texto .= " ($clntid, NOW() + INTERVAL $deadLine SECOND - INTERVAL 5 MINUTE, 'popup-5'),"; } # Add power off command at deadline time. $cmd->texto .= " ($clntid, NOW() + INTERVAL $deadLine SECOND, 'poweroff');"; if ($deadLine == 0 or $cmd->Ejecutar()) { // Confirm operation. $cmd->texto = ""; $response = ""; jsonResponse(200, $response); } else { // Error message. $response["message"] = "Database error"; jsonResponse(400, $response); } } else { // Error message. $response["message"] = "Client is not reserved"; jsonResponse(400, $response); } } else { // Error message. $response["message"] = "Client does not exist"; jsonResponse(404, $response); } $rs->Cerrar(); } ); /** * @brief Store UDS server URLs to resend some events recieved from OGAgent. * @brief Unreserve a client and send a poweroff operation. * @note Route: /ous/:ouid/labs/:labid/clients/:clntid/unreserve, Method: DELETE */ $app->delete('/ous/:ouid/labs/:labid/clients/:clntid/unreserve', 'validateApiKey', function($ouid, $labid, $clntid) use ($app) { global $cmd; global $userid; global $ACCION_INICIADA; $response = []; $ogagent = []; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Init."); // Checking parameters. try { if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']); } if (!checkIds($ouid, $labid, $clntid)) { throw new Exception("Ids. must be positive integers"); } } catch (Exception $e) { // Error message. $response["message"] = $e->getMessage(); if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"]."."); jsonResponse(400, $response); $app->stop(); } // Select client data for UDS compatibility. $cmd->texto = <<Comando=&$cmd; if (!$rs->Abrir()) return(false); // Error opening recordset. // Check if user is admin and client exists. $rs->Primero(); if (checkAdmin($rs->campos["idusuario"]) and checkParameter($rs->campos["idordenador"])) { // Check if client is reserved. if (! is_null($rs->campos["reserved"])) { // Read query data. $clntip = $rs->campos["ip"]; $agentkey = $rs->campos["agentkey"]; // DB Transaction: set reservation time to the past, remove pending // boot commands from client's and agent's queues, and drop its event. if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": Updating database."); $cmd->texto = "START TRANSACTION;"; $cmd->Ejecutar(); $cmd->texto = <<Ejecutar(); $cmd->texto = <<Ejecutar(); $cmd->texto = <<Ejecutar(); $cmd->texto = "DROP EVENT IF EXISTS e_timeout_$clntid;"; $cmd->Ejecutar(); $cmd->texto = "COMMIT;"; $cmd->Ejecutar(); // Send a poweroff command to client's OGAgent. $ogagent = [['url' => "https://$clntip:8000/opengnsys/poweroff", 'header' => ["Authorization: ".$agentkey]]]; if ($app->settings['debug']) writeRemotepcLog($app->request()->getResourceUri(). ": OGAgent poweroff, url=".$ogagent[0]['url']."."); $result = multiRequest($ogagent); // ... (check response) //if ($result[0]['code'] != 200) { // ... // Confirm operation. $response = ""; jsonResponse(200, $response); } else { // Error message. $response["message"] = "Client is not reserved"; jsonResponse(400, $response); } } else { // Error message. $response["message"] = "Client does not exist"; jsonResponse(404, $response); } $rs->Cerrar(); } ); /* * @brief Send an init operation to a client with an image installed. * @note Route: /ous/:ouid/labs/:labid/clients/:clntid/init, Method: POST * @param int image Image id. */ $app->post('/ous/:ouid/labs/:labid/clients/:clntid/init', 'validateApiKey', function($ouid, $labid, $clntid) use ($app) { global $cmd; global $userid; global $AMBITO_ORDENADORES; global $EJECUCION_COMANDO; global $ACCION_INICIADA; global $ACCION_FINALIZADA; global $ACCION_SINRESULTADO; global $ACCION_FALLIDA; if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: Init."); // Checking parameters. try { if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']); } if (!checkIds($ouid, $labid, $clntid)) { throw new Exception("Ids. must be positive integers"); } // Reading POST parameters in JSON format. $input = json_decode($app->request()->getBody()); $imageid = $input->image ?? 0; $opts = Array('options' => Array('min_range' => 1)); // Check for int>0 if (filter_var($imageid, FILTER_VALIDATE_INT, $opts) === false) { throw new Exception("Image id. must be positive integer"); } } catch (Exception $e) { // Communication error. $response["message"] = $e->getMessage(); if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: ERROR: {$response["message"]}."); jsonResponse(400, $response); $app->stop(); } // Select data to init a session. $cmd->texto = <<Comando=&$cmd; if (!$rs->Abrir()) return(false); // Error opening recordset. // Check if user is admin and client exists. $rs->Primero(); if (checkAdmin($rs->campos["idusuario"]) and checkParameter($rs->campos["idordenador"])) { // Read query data. $clntip = $rs->campos["ip"]; $clntmac = $rs->campos["mac"]; $clntnetmask = $rs->campos["mascara"]; $agentkey = $rs->campos["agentkey"]; $disk = $rs->campos["numdisk"]; $part = $rs->campos["numpar"]; $repoip = $rs->campos["repoip"]; $repokey = $rs->campos["repokey"]; // Check client's status. $ogagent = [['url' => "https://$clntip:8000/opengnsys/status"]]; if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: OGAgent status, url={$ogagent[0]['url']}."); $result = multiRequest($ogagent); if (empty($result[0]['data'])) { // Client is off, send WOL command to ogAdmServer. // TODO: if client is busy????? if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: Send boot command through ogAdmServer: iph=$clntip,mac=$clntmac."); wol(1, [$clntip]); // Send WOL command to client repository. $repo = [['url' => "https://$repoip/opengnsys/rest/repository/poweron", 'header' => ["Authorization: $repokey"], 'post' => '{"macs": ["'.$clntmac.'"], "ips": ["'.$clntip.'"]}']]; if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: Send Boot command through repo: repo=$repoip,ip=$clntip,mac=$clntmac."); $result = multiRequest($repo); } // Create an init session on opertions queue. $timestamp = time(); $cmd->texto = <<Ejecutar(); create_schedule_now(strval($timestamp), $EJECUCION_COMANDO, "auto-queue-remotepc-init-".$timestamp); // Create event to remove the operation on timeout (15 min.). $timeout = "15 MINUTE"; $cmd->texto = <<Ejecutar(); if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: DB tables and events updated, clntid=$clntid."); // Send init session command if client is booted on ogLive. if ($app->settings['debug']) writeRemotepcLog("{$app->request()->getResourceUri()}: Send Init Session command to ogAdmClient, ido=$clntid,iph=$clntip,dsk=$disk,par=$part."); session($clntip, "$disk\r$part"); // Confirm operation. $response = []; jsonResponse(200, $response); } else { // Error message. $response["message"] = "Client with this image not found"; jsonResponse(404, $response); } $rs->Cerrar(); } );