source: admin/WebConsole/rest/remotepc.php @ d610135

918-git-images-111dconfigfileconfigure-oglivegit-imageslgromero-new-oglivemainmaint-cronmount-efivarfsmultivmmultivm-ogboot-installerogClonningEngineogboot-installer-jenkinsoglive-ipv6test-python-scriptsticket-301ticket-50ticket-50-oldticket-577ticket-585ticket-611ticket-612ticket-693ticket-700ubu24tplunification2use-local-agent-oglivevarios-instalacionwebconsole3
Last change on this file since d610135 was 4073d14, checked in by ramon <ramongomez@…>, 8 years ago

#708: Añadir depuración a rutas REST para proyecto RemotePC y preparar nueva ruta de datos de sesión.

git-svn-id: https://opengnsys.es/svn/branches/version1.1@5462 a21b9725-9963-47de-94b9-378ad31fedc9

  • Property mode set to 100644
File size: 17.2 KB
Line 
1<?php
2/**
3 * @file    remotepc.php
4 * @brief   OpenGnsys Server REST API consumed by UDS Server for Remote PC implementation.
5 * @warning All input and output messages are formatted in JSON.
6 * @note    Some ideas are based on article "How to create REST API for Android app using PHP, Slim and MySQL" by Ravi Tamada, thanx.
7 * @license GNU GPLv3+
8 * @author  Ramón M. Gómez, ETSII Univ. Sevilla
9 * @version 1.1.0 - First version
10 * @date    2017-02-01
11 */
12
13// OGAgent sessions log file.
14define('REMOTEPC_LOGFILE', '/opt/opengnsys/log/remotepc.log');
15
16// Function to write a line into log file.
17function writeRemotepcLog($message = "") {
18        file_put_contents(REMOTEPC_LOGFILE, date(DATE_ISO8601).": $message\n", FILE_APPEND);
19}
20
21
22// REST routes.
23
24/**
25 * @brief    Reserve a client with an installed image and the older reservation time, then send a boot/reboot operation depending on its status.
26 * @warning  If "lab" parameter is specified, then choose a client from this lab.
27 * @note     Route: /ous/:ouid/images/:imageid/reserve, Method: POST
28 * @param    integer ouid      OU identificator
29 * @param    integer imageid   image identificator
30 * @note     Input JSON message: {"labid":int_labid,"maxtime":int_hours}
31 */
32$app->post('/ous/:ouid/images/:imageid/reserve(/)', 'validateApiKey',
33    function($ouid, $imageid) use ($app) {
34        global $cmd;
35        global $AMBITO_ORDENADORES;
36        global $EJECUCION_COMANDO;
37        global $ACCION_INICIADA;
38        global $ACCION_FINALIZADA;
39        global $ACCION_SINRESULTADO;
40        global $ACCION_FALLIDA;
41        global $userid;
42        $response = Array();
43        $ogagent = Array();
44
45        if ($app->settings['debug'])
46                writeRemotepcLog($app->request()->getResourceUri(). ": Init.");
47        // Checking parameters.
48        try {
49                if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) {
50                        throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']);
51                }
52                if (!checkIds($ouid, $imageid)) {
53                        throw new Exception("Ids. must be positive integers");
54                }
55                // Reading POST parameters in JSON format.
56                $input = json_decode($app->request()->getBody());
57                // Default: no lab. filter.
58                if (isset($input->labid)) {
59                        $labid = $input->labid != "0" ? $input->labid : '%';
60                } else {
61                        $labid = '%';
62                }
63                $maxtime = isset($input->maxtime) ? $input->maxtime : 24;       // Default: 24 h.
64                $opts = Array('options' => Array('min_range' => 1));    // Check for int>0
65                if (!filter_var($labid, FILTER_VALIDATE_INT, $opts) and $labid !== '%') {
66                        throw new Exception("Lab id. must be positive integer");
67                }
68                if (!filter_var($maxtime, FILTER_VALIDATE_INT, $opts)) {
69                        throw new Exception("Time must be positive integer (in hours)");
70                }
71        } catch (Exception $e) {
72                // Communication error.
73                $response["message"] = $e->getMessage();
74                if ($app->settings['debug'])
75                        writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"].".");
76                jsonResponse(400, $response);
77                $app->stop();
78        }
79
80        if ($app->settings['debug'])
81                writeRemotepcLog($app->request()->getResourceUri(). ": Parameters: labid=$labid, maxtime=$maxtime");
82        // Choose older not-reserved client with image installed and get ogAdmServer data.
83        $cmd->texto = <<<EOD
84SELECT adm.idadministradorcentro, entornos.ipserveradm, entornos.portserveradm,
85       ordenadores.idordenador, ordenadores.nombreordenador, ordenadores.ip,
86       ordenadores.mac, ordenadores.agentkey, ordenadores_particiones.numdisk,
87       ordenadores_particiones.numpar, aulas.idaula, aulas.idcentro
88  FROM entornos, ordenadores
89  JOIN aulas USING(idaula)
90 RIGHT JOIN administradores_centros AS adm USING(idcentro)
91 RIGHT JOIN usuarios USING(idusuario)
92 RIGHT JOIN ordenadores_particiones USING(idordenador)
93 RIGHT JOIN imagenes USING(idimagen)
94  LEFT JOIN remotepc ON remotepc.id=ordenadores.idordenador
95 WHERE adm.idadministradorcentro = '$userid'
96   AND aulas.idcentro = '$ouid' AND aulas.idaula LIKE '$labid' AND aulas.inremotepc = 1
97   AND imagenes.idimagen = '$imageid' AND imagenes.inremotepc = 1
98   AND (remotepc.reserved < NOW() OR ISNULL(reserved))
99 ORDER BY remotepc.reserved ASC LIMIT 1;
100EOD;
101        $rs=new Recordset;
102        $rs->Comando=&$cmd;
103        if (!$rs->Abrir()) return(false);       // Error opening recordset.
104        // Check if user is admin and client exists.
105        $rs->Primero();
106        if (checkAdmin($rs->campos["idadministradorcentro"]) and checkParameter($rs->campos["idordenador"])) {
107                // Read query data.
108                $serverip = $rs->campos["ipserveradm"];
109                $serverport = $rs->campos["portserveradm"];
110                $clntid = $rs->campos["idordenador"];
111                $clntname = $rs->campos["nombreordenador"];
112                $clntip = $rs->campos["ip"];
113                $clntmac = $rs->campos["mac"];
114                $agentkey = $rs->campos["agentkey"];
115                $disk = $rs->campos["numdisk"];
116                $part = $rs->campos["numpar"];
117                $labid = $rs->campos["idaula"];
118                $ouid = $rs->campos["idcentro"];
119                // Check client's status.
120                $ogagent[$clntip]['url'] = "https://$clntip:8000/opengnsys/status";
121                if ($app->settings['debug'])
122                        writeRemotepcLog($app->request()->getResourceUri(). ": OGAgent status, url=".$ogagent[$clntip]['url'].".");
123                $result = multiRequest($ogagent);
124                if (empty($result[$clntip]['data'])) {
125                        // Client is off, send a boot command to ogAdmServer.
126                        // TODO: if client is busy?????
127                        $reqframe = "nfn=Arrancar\r".
128                                    "ido=$clntid\r".
129                                    "iph=$clntip\r".
130                                    "mac=$clntmac\r".
131                                    "mar=1\r";
132                        if ($app->settings['debug'])
133                                writeRemotepcLog($app->request()->getResourceUri(). "Send Boot command to ogAdmClient, ido=$clntid,iph=$clntip,mac=$clntmac.");
134                        sendCommand($serverip, $serverport, $reqframe, $values);
135                } else {
136                        // Client is on, send a rieboot command to its OGAgent.
137                        $ogagent[$clntip]['url'] = "https://$clntip:8000/opengnsys/reboot";
138                        $ogagent[$clntip]['header'] = Array("Authorization: ".$agentkey);
139                        if ($app->settings['debug'])
140                                writeRemotepcLog($app->request()->getResourceUri(). ": OGAgent reboot, url=".$ogagent[$clntip]['url'].".");
141                        $result = multiRequest($ogagent);
142                        // ... (check response)
143                        //if ($result[$clntip]['code'] != 200) {
144                        // ...
145                }
146                // DB Transaction: mark choosed client as reserved and
147                // create an init session command into client's actions queue.
148                $cmd->texto = "START TRANSACTION;";
149                $cmd->Ejecutar();
150                $timestamp = time();
151                $cmd->texto = <<<EOD
152INSERT INTO remotepc
153   SET id='$clntid', reserved=NOW() + INTERVAL $maxtime HOUR, urllogin=NULL, urllogout=NULL
154    ON DUPLICATE KEY UPDATE
155       id=VALUES(id), reserved=VALUES(reserved),
156       urllogin=VALUES(urllogin), urllogout=VALUES(urllogout);
157EOD;
158                $t1 = $cmd->Ejecutar();
159                $cmd->texto = <<<EOD
160INSERT INTO acciones
161   SET tipoaccion=$EJECUCION_COMANDO,
162       idtipoaccion=9,
163       idcomando=9,
164       parametros='nfn=IniciarSesion\rdsk=$disk\rpar=$part',
165       descriaccion='RemotePC Session',
166       idordenador=$clntid,
167       ip='$clntip',
168       sesion=$timestamp,
169       fechahorareg=NOW(),
170       estado=$ACCION_INICIADA,
171       resultado=$ACCION_SINRESULTADO,
172       ambito=$AMBITO_ORDENADORES,
173       idambito=$clntid,
174       restrambito='$clntip',
175       idcentro=$ouid;
176EOD;
177                $t2 = $cmd->Ejecutar();
178                // Create event to remove reservation on timeout (15 min.).
179                $timeout = "15 MINUTE";
180                $cmd->texto = <<<EOD
181CREATE EVENT e_timeout_$clntid
182       ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL $timeout DO
183       BEGIN
184            SET @clntid = NULL;
185            UPDATE acciones
186               SET estado = $ACCION_FINALIZADA, resultado = $ACCION_FALLIDA,
187                   descrinotificacion = 'Timeout'
188             WHERE descriaccion = 'RemotePC Session' AND estado = $ACCION_INICIADA
189               AND idordenador = (SELECT @clntid := '$clntid');
190            IF @clntid IS NOT NULL THEN
191               UPDATE remotepc
192                  SET reserved=NOW() - INTERVAL 1 SECOND, urllogin=NULL, urllogout=NULL
193                WHERE id = @clntid;
194               DELETE FROM acciones
195                WHERE idordenador = @clntid
196                  AND descriaccion = 'RemotePC Session'
197                  AND descrinotificacion = 'Timeout';
198            END IF;
199       END
200EOD;
201                $t3 = $cmd->Ejecutar();
202                if ($t1 and $t2 and $t3) {
203                        // Commit transaction on success.
204                        $cmd->texto = "COMMIT;";
205                        $cmd->Ejecutar();
206                        if ($app->settings['debug'])
207                                writeRemotepcLog($app->request()->getResourceUri(). ": DB tables and events updated, clntid=$clntid.");
208                        // Send init session command if client is booted on ogLive.
209                        $reqframe = "nfn=IniciarSesion\r".
210                                    "ido=$clntid\r".
211                                    "iph=$clntip\r".
212                                    "dsk=$disk\r".
213                                    "par=$part\r";
214                        if ($app->settings['debug'])
215                                writeRemotepcLog($app->request()->getResourceUri(). ": Send Init Session command to ogAdmClient, ido=$clntid,iph=$clntip,dsk=$disk,par=$part.");
216                        sendCommand($serverip, $serverport, $reqframe, $values);
217                        // Compose JSON response.
218                        $response['id'] = $clntid;
219                        $response['name'] = $clntname;
220                        $response['ip'] = $clntip;
221                        $response['mac'] = $clntmac;
222                        $response['lab']['id'] = $labid;
223                        $response['ou']['id'] = $ouid;
224                        if ($app->settings['debug'])
225                                writeRemotepcLog($app->request()->getResourceUri(). ": Response, ".var_export($response,true).".");
226                        jsonResponse(200, $response);
227                } else {
228                        // Roll-back transaction on DB error.
229                        $cmd->texto = "ROLLBACK;";
230                        $cmd->Ejecutar();
231                        // Error message.
232                        $response["message"] = "Database error: $t1, $t2, $t3";
233                        if ($app->settings['debug'])
234                                writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"].".");
235                        jsonResponse(400, $response);
236                        $app->stop();
237                }
238        } else {
239                if ($app->settings['debug'])
240                        writeRemotepcLog($app->request()->getResourceUri(). ": UNASSIGNED");
241        }
242        $rs->Cerrar();
243    }
244);
245
246
247/**
248 * @brief    Store UDS server URLs to resend some events recieved from OGAgent.
249 * @note     Route: /ous/:ouid/labs/:labid/clients/:clntid/events, Method: POST
250 * @param    string urlLogin   URL to redirect login notification.
251 * @param    string urlLogout  URL to redirect logout notification.
252 * @warning  Events parameters will be stored in a new "remotepc" table.
253 */
254$app->post('/ous/:ouid/labs/:labid/clients/:clntid/events', 'validateApiKey',
255    function($ouid, $labid, $clntid) use ($app) {
256        global $cmd;
257        global $userid;
258        $response = Array();
259
260        if ($app->settings['debug'])
261                writeRemotepcLog($app->request()->getResourceUri(). ": Init.");
262        // Checking parameters.
263        try {
264                if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) {
265                        throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']);
266                }
267                if (!checkIds($ouid, $labid, $clntid)) {
268                        throw new Exception("Ids. must be positive integers");
269                }
270                // Reading JSON parameters.
271                $input = json_decode($app->request()->getBody());
272                $urlLogin = htmlspecialchars($input->urlLogin);
273                $urlLogout = htmlspecialchars($input->urlLogout);
274                if (!filter_var($urlLogin, FILTER_VALIDATE_URL)) {
275                        throw new Exception("Must be a valid URL for login notification");
276                }
277                if (!filter_var($urlLogout, FILTER_VALIDATE_URL)) {
278                        throw new Exception("Must be a valid URL for logout notification");
279                }
280        } catch (Exception $e) {
281                // Error message.
282                $response["message"] = $e->getMessage();
283                if ($app->settings['debug'])
284                        writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"].".");
285                jsonResponse(400, $response);
286                $app->stop();
287        }
288
289        if ($app->settings['debug'])
290                writeRemotepcLog($app->request()->getResourceUri(). ": Parameters: urlLogin=$urlLogin, urlLogout=$urlLogout");
291        // Select client data for UDS compatibility.
292        $cmd->texto = <<<EOD
293SELECT adm.idadministradorcentro, ordenadores.idordenador, remotepc.*
294  FROM remotepc
295 RIGHT JOIN ordenadores ON remotepc.id=ordenadores.idordenador
296  JOIN aulas USING(idaula)
297 RIGHT JOIN administradores_centros AS adm USING(idcentro)
298 RIGHT JOIN usuarios USING(idusuario)
299 WHERE adm.idadministradorcentro = '$userid'
300   AND idcentro = '$ouid' AND aulas.idaula ='$labid'
301   AND ordenadores.idordenador = '$clntid';
302EOD;
303        $rs=new Recordset;
304        $rs->Comando=&$cmd;
305        if (!$rs->Abrir()) return(false);       // Error opening recordset.
306        // Check if user is admin and client exists.
307        $rs->Primero();
308        if (checkAdmin($rs->campos["idadministradorcentro"]) and checkParameter($rs->campos["idordenador"])) {
309                // Check if client is reserved.
310                if (! is_null($rs->campos["reserved"])) {
311                        // Updating DB if client is reserved.
312                        $cmd->CreaParametro("@urllogin", $urlLogin, 0);
313                        $cmd->CreaParametro("@urllogout", $urlLogout, 0);
314                        $cmd->texto = <<<EOD
315UPDATE remotepc
316   SET urllogin=@urllogin, urllogout=@urllogout
317 WHERE id='$clntid';
318EOD;
319                        if ($cmd->Ejecutar()) {
320                                // Confirm operation.
321                                jsonResponse(200, "");
322                        } else {
323                                // Error message.
324                                $response["message"] = "Database error";
325                                jsonResponse(400, $response);
326                                $app->stop();
327                        }
328                } else {
329                        // Error message.
330                        $response["message"] = "Client is not reserved";
331                        jsonResponse(400, $response);
332                        $app->stop();
333                }
334        }
335        $rs->Cerrar();
336    }
337);
338
339
340/*
341 * @brief    Store session time (in sec).
342 * @note     Route: /ous/:ouid/labs/:labid/clients/:clntid/session, Method: POST
343 * @param    int    deadLine   maximum time session will be active (in seconds)
344 * @warning  Parameters will be stored in a new "remotepc" table.
345 */
346$app->post('/ous/:ouid/labs/:labid/clients/:clntid/session', 'validateApiKey',
347    function($ouid, $labid, $clntid) use ($app) {
348        global $cmd;
349        global $userid;
350        $response = Array();
351
352        if ($app->settings['debug'])
353                writeRemotepcLog($app->request()->getResourceUri(). ": Init.");
354        // Checking parameters.
355        try {
356                if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) {
357                        throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']);
358                }
359                if (!checkIds($ouid, $labid, $clntid)) {
360                        throw new Exception("Ids. must be positive integers");
361                }
362                // Reading JSON parameters.
363                $input = json_decode($app->request()->getBody());
364                $deadLine = $input->deadLine;
365                if (!filter_var($deadLine, FILTER_VALIDATE_INT)) {
366                        throw new Exception("Deadline must be integer");
367                }
368        } catch (Exception $e) {
369                // Error message.
370                $response["message"] = $e->getMessage();
371                if ($app->settings['debug'])
372                        writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"].".");
373                jsonResponse(400, $response);
374                $app->stop();
375        }
376
377        if ($app->settings['debug'])
378                writeRemotepcLog($app->request()->getResourceUri(). ": Parameters: deadLine=$deadLine");
379        # ...
380        # ...
381        #$rs->Cerrar();
382    }
383);
384
385
386/**
387 * @brief    Store UDS server URLs to resend some events recieved from OGAgent.
388 * @brief    Unreserve a client and send a poweroff operation.
389 * @note     Route: /ous/:ouid/labs/:labid/clients/:clntid/unreserve, Method: DELETE
390 */
391$app->delete('/ous/:ouid/labs/:labid/clients/:clntid/unreserve', 'validateApiKey',
392    function($ouid, $labid, $clntid) use ($app) {
393        global $cmd;
394        global $userid;
395        global $ACCION_INICIADA;
396        $response = Array();
397        $ogagent = Array();
398
399        if ($app->settings['debug'])
400                writeRemotepcLog($app->request()->getResourceUri(). ": Init.");
401        // Checking parameters.
402        try {
403                if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) {
404                        throw new Exception("Bad agent: sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']);
405                }
406                if (!checkIds($ouid, $labid, $clntid)) {
407                        throw new Exception("Ids. must be positive integers");
408                }
409        } catch (Exception $e) {
410                // Error message.
411                $response["message"] = $e->getMessage();
412                if ($app->settings['debug'])
413                        writeRemotepcLog($app->request()->getResourceUri(). ": ERROR: ".$response["message"].".");
414                jsonResponse(400, $response);
415                $app->stop();
416        }
417
418        // Select client data for UDS compatibility.
419        $cmd->texto = <<<EOD
420SELECT adm.idadministradorcentro, ordenadores.idordenador, ordenadores.ip, ordenadores.agentkey, remotepc.reserved
421  FROM remotepc
422 RIGHT JOIN ordenadores ON remotepc.id=ordenadores.idordenador
423  JOIN aulas USING(idaula)
424 RIGHT JOIN administradores_centros AS adm USING(idcentro)
425 RIGHT JOIN usuarios USING(idusuario)
426 WHERE adm.idadministradorcentro = '$userid'
427   AND idcentro = '$ouid' AND aulas.idaula ='$labid'
428   AND ordenadores.idordenador = '$clntid';
429EOD;
430        $rs=new Recordset;
431        $rs->Comando=&$cmd;
432        if (!$rs->Abrir()) return(false);       // Error opening recordset.
433        // Check if user is admin and client exists.
434        $rs->Primero();
435        if (checkAdmin($rs->campos["idadministradorcentro"]) and checkParameter($rs->campos["idordenador"])) {
436                // Check if client is reserved.
437                if (! is_null($rs->campos["reserved"])) {
438                        // Read query data.
439                        $clntip = $rs->campos["ip"];
440                        $agentkey = $rs->campos["agentkey"];
441                        // DB Transaction: set reservation time to the past and
442                        // remove pending boot commands from client's actions queue.
443                        $cmd->texto = "START TRANSACTION;";
444                        $cmd->Ejecutar();
445                        $cmd->texto = <<<EOD
446UPDATE remotepc
447   SET reserved=NOW() - INTERVAL 1 SECOND, urllogin=NULL, urllogout=NULL
448 WHERE id='$clntid';
449EOD;
450                        $cmd->Ejecutar();
451                        $cmd->texto = <<<EOD
452DELETE FROM acciones
453 WHERE idordenador = '$clntid'
454   AND descriaccion = 'RemotePC Session';
455EOD;
456                        $cmd->Ejecutar();
457                        $cmd->texto = "COMMIT;";
458                        $cmd->Ejecutar();
459                        // Send a poweroff command to client's OGAgent.
460                        $ogagent[$clntip]['url'] = "https://$clntip:8000/opengnsys/poweroff";
461                        $ogagent[$clntip]['header'] = Array("Authorization: ".$agentkey);
462                        $result = multiRequest($ogagent);
463                        // ... (check response)
464                        //if ($result[$clntip]['code'] != 200) {
465                        // ...
466                        // Confirm operation.
467                        jsonResponse(200, "");
468                } else {
469                        // Error message.
470                        $response["message"] = "Client is not reserved";
471                        jsonResponse(400, $response);
472                }
473        }
474        $rs->Cerrar();
475    }
476);
477
478?>
Note: See TracBrowser for help on using the repository browser.