source: ogClient-Git/src/ogRest.py @ 082079a

Last change on this file since 082079a was cb9edc8, checked in by OpenGnSys Support Team <soporte-og@…>, 4 years ago

ogClient is AGPLv3+

Update license header in files.

  • Property mode set to 100644
File size: 12.1 KB
Line 
1#
2# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
3#
4# This program is free software: you can redistribute it and/or modify it under
5# the terms of the GNU Affero General Public License as published by the
6# Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
8
9import threading
10import platform
11import time
12from enum import Enum
13import json
14import queue
15import sys
16import os
17import signal
18import syslog
19
20from src.restRequest import *
21
22class ThreadState(Enum):
23        IDLE = 0
24        BUSY = 1
25
26class jsonBody():
27        def __init__(self, dictionary=None):
28                if dictionary:
29                        self.jsontree = dictionary
30                else:
31                        self.jsontree = {}
32
33        def add_element(self, key, value):
34                self.jsontree[key] = value
35
36        def dump(self):
37                return json.dumps(self.jsontree)
38
39class restResponse():
40        def __init__(self, response, json_body=None):
41                self.msg = ''
42                if response == ogResponses.BAD_REQUEST:
43                        self.msg = 'HTTP/1.0 400 Bad Request'
44                elif response == ogResponses.IN_PROGRESS:
45                        self.msg = 'HTTP/1.0 202 Accepted'
46                elif response == ogResponses.OK:
47                        self.msg = 'HTTP/1.0 200 OK'
48                elif response == ogResponses.INTERNAL_ERR:
49                        self.msg = 'HTTP/1.0 500 Internal Server Error'
50                elif response == ogResponses.UNAUTHORIZED:
51                        self.msg = 'HTTP/1.0 401 Unauthorized'
52                elif response == ogResponses.SERVICE_UNAVAILABLE:
53                        self.msg = 'HTTP/1.0 503 Service Unavailable'
54                else:
55                        return self.msg
56
57                if response in {ogResponses.OK, ogResponses.IN_PROGRESS}:
58                        syslog.syslog(syslog.LOG_INFO,
59                                      self.msg[:ogRest.LOG_LENGTH])
60                else:
61                        syslog.syslog(syslog.LOG_ERR,
62                                      self.msg[:ogRest.LOG_LENGTH])
63
64                self.msg += '\r\n'
65
66                if json_body:
67                        self.msg += 'Content-Length: ' + str(len(json_body.dump()))
68                        self.msg += '\r\nContent-Type: application/json'
69                        self.msg += '\r\n\r\n' + json_body.dump()
70                else:
71                        self.msg += 'Content-Length: 0\r\n' \
72                                    'Content-Type: application/json\r\n\r\n'
73
74
75        def get(self):
76                return self.msg
77
78class ogThread():
79        def shellrun(client, request, ogRest):
80                if not request.getrun():
81                        response = restResponse(ogResponses.BAD_REQUEST)
82                        client.send(response.get())
83                        ogRest.state = ThreadState.IDLE
84                        return
85
86                try:
87                        shellout = ogRest.operations.shellrun(request, ogRest)
88                except ValueError as err:
89                        response = restResponse(ogResponses.INTERNAL_ERR)
90                        client.send(response.get())
91                        ogRest.state = ThreadState.IDLE
92                        return
93
94                if request.getEcho():
95                        json_body = jsonBody()
96                        json_body.add_element('out', shellout)
97                        response = restResponse(ogResponses.OK, json_body)
98                        client.send(response.get())
99                else:
100                        response = restResponse(ogResponses.OK)
101                        client.send(response.get())
102
103                ogRest.state = ThreadState.IDLE
104
105        def poweroff(ogRest):
106                time.sleep(2)
107                ogRest.operations.poweroff()
108
109        def reboot(ogRest):
110                ogRest.operations.reboot()
111
112        def session(client, request, ogRest):
113                try:
114                        ogRest.operations.session(request, ogRest)
115                except ValueError as err:
116                        response = restResponse(ogResponses.INTERNAL_ERR)
117                        client.send(response.get())
118                        ogRest.state = ThreadState.IDLE
119                        return
120
121                response = restResponse(ogResponses.OK)
122                client.send(response.get())
123                client.disconnect()
124
125        def software(client, request, path, ogRest):
126                try:
127                        software = ogRest.operations.software(request, path, ogRest)
128                except ValueError as err:
129                        response = restResponse(ogResponses.INTERNAL_ERR)
130                        client.send(response.get())
131                        ogRest.state = ThreadState.IDLE
132                        return
133
134                json_body = jsonBody()
135                json_body.add_element('partition', request.getPartition())
136                json_body.add_element('software', software)
137
138                response = restResponse(ogResponses.OK, json_body)
139                client.send(response.get())
140                ogRest.state = ThreadState.IDLE
141
142        def hardware(client, path, ogRest):
143                try:
144                        ogRest.operations.hardware(path, ogRest)
145                except ValueError as err:
146                        response = restResponse(ogResponses.INTERNAL_ERR)
147                        client.send(response.get())
148                        ogRest.state = ThreadState.IDLE
149                        return
150
151                json_body = jsonBody()
152                with open(path, 'r') as f:
153                        json_body.add_element('hardware', f.read())
154
155                response = restResponse(ogResponses.OK, json_body)
156                client.send(response.get())
157                ogRest.state = ThreadState.IDLE
158
159        def setup(client, request, ogRest):
160                try:
161                        out = ogRest.operations.setup(request, ogRest)
162                except ValueError as err:
163                        response = restResponse(ogResponses.INTERNAL_ERR)
164                        client.send(response.get())
165                        ogRest.state = ThreadState.IDLE
166                        return
167
168                json_body = jsonBody(out)
169
170                response = restResponse(ogResponses.OK, json_body)
171                client.send(response.get())
172                ogRest.state = ThreadState.IDLE
173
174        def image_restore(client, request, ogRest):
175                try:
176                        ogRest.operations.image_restore(request, ogRest)
177                except ValueError as err:
178                        response = restResponse(ogResponses.INTERNAL_ERR)
179                        client.send(response.get())
180                        ogRest.state = ThreadState.IDLE
181                        return
182
183                json_body = jsonBody()
184                json_body.add_element('disk', request.getDisk())
185                json_body.add_element('partition', request.getPartition())
186                json_body.add_element('image_id', request.getId())
187
188                response = restResponse(ogResponses.OK, json_body)
189                client.send(response.get())
190                ogRest.state = ThreadState.IDLE
191
192        def image_create(client, path, request, ogRest):
193                try:
194                        image_info = ogRest.operations.image_create(path,
195                                                                    request,
196                                                                    ogRest)
197                        software = ogRest.operations.software(request, path, ogRest)
198                except ValueError as err:
199                        response = restResponse(ogResponses.INTERNAL_ERR)
200                        client.send(response.get())
201                        ogRest.state = ThreadState.IDLE
202                        return
203
204                kibi = 1024
205                datasize = int(image_info['datasize']) * kibi
206
207                json_body = jsonBody()
208                json_body.add_element('disk', request.getDisk())
209                json_body.add_element('partition', request.getPartition())
210                json_body.add_element('code', request.getCode())
211                json_body.add_element('id', request.getId())
212                json_body.add_element('name', request.getName())
213                json_body.add_element('repository', request.getRepo())
214                json_body.add_element('software', software)
215                json_body.add_element('clonator', image_info['clonator'])
216                json_body.add_element('compressor', image_info['compressor'])
217                json_body.add_element('filesystem', image_info['filesystem'])
218                json_body.add_element('datasize', datasize)
219
220                response = restResponse(ogResponses.OK, json_body)
221                client.send(response.get())
222                ogRest.state = ThreadState.IDLE
223
224        def refresh(client, ogRest):
225                try:
226                        out = ogRest.operations.refresh(ogRest)
227                except ValueError as err:
228                        response = restResponse(ogResponses.INTERNAL_ERR)
229                        client.send(response.get())
230                        ogRest.state = ThreadState.IDLE
231                        return
232
233                json_body = jsonBody(out)
234
235                response = restResponse(ogResponses.OK, json_body)
236                client.send(response.get())
237                ogRest.state = ThreadState.IDLE
238
239class ogResponses(Enum):
240        BAD_REQUEST=0
241        IN_PROGRESS=1
242        OK=2
243        INTERNAL_ERR=3
244        UNAUTHORIZED=4
245        SERVICE_UNAVAILABLE=5
246
247class ogRest():
248        LOG_LENGTH = 32
249
250        def __init__(self, config):
251                self.proc = None
252                self.terminated = False
253                self.state = ThreadState.IDLE
254                self.CONFIG = config
255                self.mode = self.CONFIG['opengnsys']['mode']
256                self.samba_config = self.CONFIG['samba']
257
258                if self.mode == 'live':
259                        from src.live.ogOperations import OgLiveOperations
260                        self.operations = OgLiveOperations(self.CONFIG)
261                elif self.mode == 'virtual':
262                        from src.virtual.ogOperations import \
263                                OgVirtualOperations
264                        self.operations = OgVirtualOperations()
265                        threading.Thread(target=self.operations.check_vm_state_loop,
266                                         args=(self,)).start()
267                else:
268                        raise ValueError('Mode not supported.')
269
270        def process_request(self, request, client):
271                method = request.get_method()
272                URI = request.get_uri()
273
274                syslog.syslog(syslog.LOG_DEBUG, f'{method}{URI[:ogRest.LOG_LENGTH]}')
275
276                if (not "stop" in URI and
277                    not "reboot" in URI and
278                    not "poweroff" in URI and
279                    not "probe" in URI):
280                        if self.state == ThreadState.BUSY:
281                                syslog.syslog(syslog.LOG_ERR,
282                                              'Request has been received '
283                                              'while ogClient is busy')
284                                response = restResponse(ogResponses.SERVICE_UNAVAILABLE)
285                                client.send(response.get())
286                                return
287                        else:
288                                self.state = ThreadState.BUSY
289
290                if ("GET" in method):
291                        if "hardware" in URI:
292                                self.process_hardware(client)
293                        elif ("software" in URI):
294                                self.process_software(client, request)
295                        elif ("run/schedule" in URI):
296                                self.process_schedule(client)
297                        elif "refresh" in URI:
298                                self.process_refresh(client)
299                        else:
300                                syslog.syslog(syslog.LOG_ERR,
301                                              f'Unsupported request: '
302                                              f'{method[:ogRest.LOG_LENGTH]}')
303                                response = restResponse(ogResponses.BAD_REQUEST)
304                                client.send(response.get())
305                                self.state = ThreadState.IDLE
306                elif ("POST" in method):
307                        if ("poweroff" in URI):
308                                self.process_poweroff(client)
309                        elif "probe" in URI:
310                                self.process_probe(client)
311                        elif ("reboot" in URI):
312                                self.process_reboot(client)
313                        elif ("shell/run" in URI):
314                                self.process_shellrun(client, request)
315                        elif ("session" in URI):
316                                self.process_session(client, request)
317                        elif ("setup" in URI):
318                                self.process_setup(client, request)
319                        elif ("image/restore" in URI):
320                                self.process_imagerestore(client, request)
321                        elif ("stop" in URI):
322                                self.process_stop(client)
323                        elif ("image/create" in URI):
324                                self.process_imagecreate(client, request)
325                        else:
326                                syslog.syslog(syslog.LOG_ERR,
327                                              f'Unsupported request: '
328                                              f'{method[:ogRest.LOG_LENGTH]}')
329                                response = restResponse(ogResponses.BAD_REQUEST)
330                                client.send(response.get())
331                                self.state = ThreadState.IDLE
332                else:
333                        response = restResponse(ogResponses.BAD_REQUEST)
334                        client.send(response.get())
335                        self.state = ThreadState.IDLE
336
337                return 0
338
339        def kill_process(self):
340                try:
341                        os.kill(self.proc.pid, signal.SIGTERM)
342                except:
343                        pass
344
345                time.sleep(2)
346                try:
347                        os.kill(self.proc.pid, signal.SIGKILL)
348                except:
349                        pass
350
351                self.state = ThreadState.IDLE
352
353        def process_reboot(self, client):
354                response = restResponse(ogResponses.IN_PROGRESS)
355                client.send(response.get())
356
357                if self.mode != 'virtual':
358                        client.disconnect()
359                        if self.state == ThreadState.BUSY:
360                                self.kill_process()
361
362                threading.Thread(target=ogThread.reboot, args=(self,)).start()
363
364        def process_poweroff(self, client):
365                response = restResponse(ogResponses.IN_PROGRESS)
366                client.send(response.get())
367
368                if self.mode != 'virtual':
369                        client.disconnect()
370                        if self.state == ThreadState.BUSY:
371                                self.kill_process()
372
373                threading.Thread(target=ogThread.poweroff, args=(self,)).start()
374
375        def process_probe(self, client):
376                try:
377                        status = self.operations.probe(self)
378                except:
379                        response = restResponse(ogResponses.INTERNAL_ERR)
380                        client.send(response.get())
381                        return
382
383                json_body = jsonBody()
384                for k, v in status.items():
385                        json_body.add_element(k, v)
386
387                if self.state != ThreadState.BUSY:
388                        response = restResponse(ogResponses.OK, json_body)
389                else:
390                        response = restResponse(ogResponses.IN_PROGRESS, json_body)
391
392                client.send(response.get())
393
394        def process_shellrun(self, client, request):
395                threading.Thread(target=ogThread.shellrun, args=(client, request, self,)).start()
396
397        def process_session(self, client, request):
398                threading.Thread(target=ogThread.session, args=(client, request, self,)).start()
399
400        def process_software(self, client, request):
401                path = '/tmp/CSft-' + client.ip + '-' + str(request.getPartition())
402                threading.Thread(target=ogThread.software, args=(client, request, path, self,)).start()
403
404        def process_hardware(self, client):
405                path = '/tmp/Chrd-' + client.ip
406                threading.Thread(target=ogThread.hardware, args=(client, path, self,)).start()
407
408        def process_schedule(self, client):
409                response = restResponse(ogResponses.OK)
410                client.send(response.get())
411                self.state = ThreadState.IDLE
412
413        def process_setup(self, client, request):
414                threading.Thread(target=ogThread.setup, args=(client, request, self,)).start()
415
416        def process_imagerestore(self, client, request):
417                threading.Thread(target=ogThread.image_restore, args=(client, request, self,)).start()
418
419        def process_stop(self, client):
420                client.disconnect()
421                if self.state == ThreadState.BUSY:
422                        self.kill_process()
423                        self.terminated = True
424
425                sys.exit(0)
426
427        def process_imagecreate(self, client, request):
428                path = '/tmp/CSft-' + client.ip + '-' + request.getPartition()
429                threading.Thread(target=ogThread.image_create, args=(client, path, request, self,)).start()
430
431        def process_refresh(self, client):
432                threading.Thread(target=ogThread.refresh, args=(client, self,)).start()
Note: See TracBrowser for help on using the repository browser.