source: ogClient-Git/src/ogRest.py @ cc0d987

Last change on this file since cc0d987 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
RevLine 
[05b1088]1#
[cb9edc8]2# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
[05b1088]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
[cb9edc8]6# Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
[05b1088]8
[dfc97ff]9import threading
10import platform
11import time
[694bc49]12from enum import Enum
[e20daf6]13import json
14import queue
[d5dca0f]15import sys
16import os
17import signal
[93f1b35]18import syslog
[694bc49]19
[e39fe2f]20from src.restRequest import *
[2fa8aa4]21
[583057b]22class ThreadState(Enum):
23        IDLE = 0
24        BUSY = 1
25
[3917c36]26class jsonBody():
[e96e187]27        def __init__(self, dictionary=None):
28                if dictionary:
29                        self.jsontree = dictionary
30                else:
31                        self.jsontree = {}
[7c26c55]32
[3917c36]33        def add_element(self, key, value):
[7c26c55]34                self.jsontree[key] = value
35
[3917c36]36        def dump(self):
[7c26c55]37                return json.dumps(self.jsontree)
38
[0f32b9c]39class restResponse():
[3917c36]40        def __init__(self, response, json_body=None):
[86eb703]41                self.msg = ''
[0f32b9c]42                if response == ogResponses.BAD_REQUEST:
[86eb703]43                        self.msg = 'HTTP/1.0 400 Bad Request'
[0f32b9c]44                elif response == ogResponses.IN_PROGRESS:
[86eb703]45                        self.msg = 'HTTP/1.0 202 Accepted'
[0f32b9c]46                elif response == ogResponses.OK:
[86eb703]47                        self.msg = 'HTTP/1.0 200 OK'
[0f32b9c]48                elif response == ogResponses.INTERNAL_ERR:
[86eb703]49                        self.msg = 'HTTP/1.0 500 Internal Server Error'
[d5dca0f]50                elif response == ogResponses.UNAUTHORIZED:
[86eb703]51                        self.msg = 'HTTP/1.0 401 Unauthorized'
[583057b]52                elif response == ogResponses.SERVICE_UNAVAILABLE:
53                        self.msg = 'HTTP/1.0 503 Service Unavailable'
[0f32b9c]54                else:
[86eb703]55                        return self.msg
[0f32b9c]56
[93f1b35]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
[86eb703]64                self.msg += '\r\n'
[3a44e48]65
[3917c36]66                if json_body:
67                        self.msg += 'Content-Length: ' + str(len(json_body.dump()))
[f86999d]68                        self.msg += '\r\nContent-Type: application/json'
[3917c36]69                        self.msg += '\r\n\r\n' + json_body.dump()
[3a44e48]70                else:
[b53f8d0]71                        self.msg += 'Content-Length: 0\r\n' \
72                                    'Content-Type: application/json\r\n\r\n'
[0f32b9c]73
[86eb703]74
75        def get(self):
76                return self.msg
[0f32b9c]77
[59a2823]78class ogThread():
[5964e48]79        def shellrun(client, request, ogRest):
[9890d60]80                if not request.getrun():
[86eb703]81                        response = restResponse(ogResponses.BAD_REQUEST)
82                        client.send(response.get())
[583057b]83                        ogRest.state = ThreadState.IDLE
[230bdca]84                        return
85
86                try:
[269c7b5]87                        shellout = ogRest.operations.shellrun(request, ogRest)
[230bdca]88                except ValueError as err:
[86eb703]89                        response = restResponse(ogResponses.INTERNAL_ERR)
90                        client.send(response.get())
[583057b]91                        ogRest.state = ThreadState.IDLE
[230bdca]92                        return
93
[8fc251e]94                if request.getEcho():
[3917c36]95                        json_body = jsonBody()
96                        json_body.add_element('out', shellout)
97                        response = restResponse(ogResponses.OK, json_body)
[86eb703]98                        client.send(response.get())
[230bdca]99                else:
[86eb703]100                        response = restResponse(ogResponses.OK)
101                        client.send(response.get())
[59a2823]102
[583057b]103                ogRest.state = ThreadState.IDLE
104
[4f03c31]105        def poweroff(ogRest):
[59a2823]106                time.sleep(2)
[4f03c31]107                ogRest.operations.poweroff()
[59a2823]108
[4f03c31]109        def reboot(ogRest):
110                ogRest.operations.reboot()
[59a2823]111
[2e80653]112        def session(client, request, ogRest):
[0f32b9c]113                try:
[4f03c31]114                        ogRest.operations.session(request, ogRest)
[0f32b9c]115                except ValueError as err:
[86eb703]116                        response = restResponse(ogResponses.INTERNAL_ERR)
117                        client.send(response.get())
[583057b]118                        ogRest.state = ThreadState.IDLE
[0f32b9c]119                        return
120
[86eb703]121                response = restResponse(ogResponses.OK)
122                client.send(response.get())
[0593119]123                client.disconnect()
[2fa8aa4]124
[2e80653]125        def software(client, request, path, ogRest):
[683afa6]126                try:
[2e3d47b]127                        software = ogRest.operations.software(request, path, ogRest)
[683afa6]128                except ValueError as err:
[86eb703]129                        response = restResponse(ogResponses.INTERNAL_ERR)
130                        client.send(response.get())
[583057b]131                        ogRest.state = ThreadState.IDLE
[683afa6]132                        return
133
[3917c36]134                json_body = jsonBody()
135                json_body.add_element('partition', request.getPartition())
[2e3d47b]136                json_body.add_element('software', software)
[683afa6]137
[3917c36]138                response = restResponse(ogResponses.OK, json_body)
[86eb703]139                client.send(response.get())
[583057b]140                ogRest.state = ThreadState.IDLE
[6d1e79b]141
[2e80653]142        def hardware(client, path, ogRest):
[1ced3dd]143                try:
[4f03c31]144                        ogRest.operations.hardware(path, ogRest)
[1ced3dd]145                except ValueError as err:
[86eb703]146                        response = restResponse(ogResponses.INTERNAL_ERR)
147                        client.send(response.get())
[583057b]148                        ogRest.state = ThreadState.IDLE
[1ced3dd]149                        return
150
[3917c36]151                json_body = jsonBody()
[1fd9f2e]152                with open(path, 'r') as f:
[3917c36]153                        json_body.add_element('hardware', f.read())
[86eb703]154
[3917c36]155                response = restResponse(ogResponses.OK, json_body)
[86eb703]156                client.send(response.get())
[583057b]157                ogRest.state = ThreadState.IDLE
[261a5ed]158
[2e80653]159        def setup(client, request, ogRest):
[86eb703]160                try:
[4f03c31]161                        out = ogRest.operations.setup(request, ogRest)
[86eb703]162                except ValueError as err:
163                        response = restResponse(ogResponses.INTERNAL_ERR)
164                        client.send(response.get())
[583057b]165                        ogRest.state = ThreadState.IDLE
[86eb703]166                        return
167
[3917c36]168                json_body = jsonBody(out)
[86eb703]169
[3917c36]170                response = restResponse(ogResponses.OK, json_body)
[86eb703]171                client.send(response.get())
[583057b]172                ogRest.state = ThreadState.IDLE
[efbe8a7]173
[2e80653]174        def image_restore(client, request, ogRest):
[a306b8b]175                try:
[4f03c31]176                        ogRest.operations.image_restore(request, ogRest)
[a306b8b]177                except ValueError as err:
[86eb703]178                        response = restResponse(ogResponses.INTERNAL_ERR)
179                        client.send(response.get())
[583057b]180                        ogRest.state = ThreadState.IDLE
[a306b8b]181                        return
182
[3917c36]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())
[fe4236d]187
[3917c36]188                response = restResponse(ogResponses.OK, json_body)
[86eb703]189                client.send(response.get())
[583057b]190                ogRest.state = ThreadState.IDLE
[cc11d8f]191
[2e80653]192        def image_create(client, path, request, ogRest):
[b2fd0b5]193                try:
[c86eae4]194                        image_info = ogRest.operations.image_create(path,
195                                                                    request,
196                                                                    ogRest)
[2e3d47b]197                        software = ogRest.operations.software(request, path, ogRest)
[b2fd0b5]198                except ValueError as err:
[86eb703]199                        response = restResponse(ogResponses.INTERNAL_ERR)
200                        client.send(response.get())
[583057b]201                        ogRest.state = ThreadState.IDLE
[b2fd0b5]202                        return
203
[b138fbc]204                kibi = 1024
205                datasize = int(image_info['datasize']) * kibi
206
[3917c36]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())
[2e3d47b]214                json_body.add_element('software', software)
[c86eae4]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'])
[b138fbc]218                json_body.add_element('datasize', datasize)
[86eb703]219
[3917c36]220                response = restResponse(ogResponses.OK, json_body)
[86eb703]221                client.send(response.get())
[583057b]222                ogRest.state = ThreadState.IDLE
[b2fd0b5]223
[2e80653]224        def refresh(client, ogRest):
[b5e182f]225                try:
[4f03c31]226                        out = ogRest.operations.refresh(ogRest)
[b5e182f]227                except ValueError as err:
[86eb703]228                        response = restResponse(ogResponses.INTERNAL_ERR)
229                        client.send(response.get())
[583057b]230                        ogRest.state = ThreadState.IDLE
[b5e182f]231                        return
232
[3917c36]233                json_body = jsonBody(out)
[b5e182f]234
[3917c36]235                response = restResponse(ogResponses.OK, json_body)
[86eb703]236                client.send(response.get())
[583057b]237                ogRest.state = ThreadState.IDLE
[b5e182f]238
[694bc49]239class ogResponses(Enum):
240        BAD_REQUEST=0
241        IN_PROGRESS=1
242        OK=2
[0f32b9c]243        INTERNAL_ERR=3
[d5dca0f]244        UNAUTHORIZED=4
[583057b]245        SERVICE_UNAVAILABLE=5
[694bc49]246
[dfc97ff]247class ogRest():
[93f1b35]248        LOG_LENGTH = 32
249
[38b6d77]250        def __init__(self, config):
[d5dca0f]251                self.proc = None
252                self.terminated = False
[583057b]253                self.state = ThreadState.IDLE
[38b6d77]254                self.CONFIG = config
255                self.mode = self.CONFIG['opengnsys']['mode']
256                self.samba_config = self.CONFIG['samba']
[4f03c31]257
[1377ace]258                if self.mode == 'live':
[f0aa3df]259                        from src.live.ogOperations import OgLiveOperations
260                        self.operations = OgLiveOperations(self.CONFIG)
[4f03c31]261                elif self.mode == 'virtual':
[b29b2eb]262                        from src.virtual.ogOperations import \
263                                OgVirtualOperations
[4f03c31]264                        self.operations = OgVirtualOperations()
[b29b2eb]265                        threading.Thread(target=self.operations.check_vm_state_loop,
[6ca16dd]266                                         args=(self,)).start()
[4f03c31]267                else:
268                        raise ValueError('Mode not supported.')
[d5dca0f]269
[a85c113]270        def process_request(self, request, client):
271                method = request.get_method()
[d69841e]272                URI = request.get_uri()
[d5dca0f]273
[93f1b35]274                syslog.syslog(syslog.LOG_DEBUG, f'{method}{URI[:ogRest.LOG_LENGTH]}')
275
[583057b]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:
[93f1b35]281                                syslog.syslog(syslog.LOG_ERR,
282                                              'Request has been received '
283                                              'while ogClient is busy')
[583057b]284                                response = restResponse(ogResponses.SERVICE_UNAVAILABLE)
285                                client.send(response.get())
286                                return
287                        else:
288                                self.state = ThreadState.BUSY
[d5dca0f]289
[a85c113]290                if ("GET" in method):
[9c34a8e]291                        if "hardware" in URI:
[261a5ed]292                                self.process_hardware(client)
[900a1c8]293                        elif ("software" in URI):
294                                self.process_software(client, request)
[9fd8f2d]295                        elif ("run/schedule" in URI):
296                                self.process_schedule(client)
[dabc7eb]297                        elif "refresh" in URI:
298                                self.process_refresh(client)
[6764fc4]299                        else:
[93f1b35]300                                syslog.syslog(syslog.LOG_ERR,
301                                              f'Unsupported request: '
302                                              f'{method[:ogRest.LOG_LENGTH]}')
[86eb703]303                                response = restResponse(ogResponses.BAD_REQUEST)
304                                client.send(response.get())
[f8e566b]305                                self.state = ThreadState.IDLE
[a85c113]306                elif ("POST" in method):
[6764fc4]307                        if ("poweroff" in URI):
308                                self.process_poweroff(client)
[9c34a8e]309                        elif "probe" in URI:
310                                self.process_probe(client)
[6764fc4]311                        elif ("reboot" in URI):
312                                self.process_reboot(client)
313                        elif ("shell/run" in URI):
[8fc251e]314                                self.process_shellrun(client, request)
[2fa8aa4]315                        elif ("session" in URI):
[8fc251e]316                                self.process_session(client, request)
[efbe8a7]317                        elif ("setup" in URI):
[8fc251e]318                                self.process_setup(client, request)
[cc11d8f]319                        elif ("image/restore" in URI):
[2e80653]320                                self.process_imagerestore(client, request)
[d5dca0f]321                        elif ("stop" in URI):
322                                self.process_stop(client)
[b2fd0b5]323                        elif ("image/create" in URI):
[2e80653]324                                self.process_imagecreate(client, request)
[6764fc4]325                        else:
[93f1b35]326                                syslog.syslog(syslog.LOG_ERR,
327                                              f'Unsupported request: '
328                                              f'{method[:ogRest.LOG_LENGTH]}')
[86eb703]329                                response = restResponse(ogResponses.BAD_REQUEST)
330                                client.send(response.get())
[f8e566b]331                                self.state = ThreadState.IDLE
[dfc97ff]332                else:
[86eb703]333                        response = restResponse(ogResponses.BAD_REQUEST)
334                        client.send(response.get())
[f8e566b]335                        self.state = ThreadState.IDLE
[dfc97ff]336
337                return 0
338
[6f7ba32]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
[dfc97ff]353        def process_reboot(self, client):
[86eb703]354                response = restResponse(ogResponses.IN_PROGRESS)
355                client.send(response.get())
356
[b576836]357                if self.mode != 'virtual':
358                        client.disconnect()
359                        if self.state == ThreadState.BUSY:
360                                self.kill_process()
[583057b]361
[f56065a]362                threading.Thread(target=ogThread.reboot, args=(self,)).start()
[dfc97ff]363
364        def process_poweroff(self, client):
[86eb703]365                response = restResponse(ogResponses.IN_PROGRESS)
366                client.send(response.get())
367
[b576836]368                if self.mode != 'virtual':
369                        client.disconnect()
370                        if self.state == ThreadState.BUSY:
371                                self.kill_process()
[583057b]372
[f56065a]373                threading.Thread(target=ogThread.poweroff, args=(self,)).start()
[dfc97ff]374
375        def process_probe(self, client):
[bd98dd1]376                try:
377                        status = self.operations.probe(self)
378                except:
379                        response = restResponse(ogResponses.INTERNAL_ERR)
380                        client.send(response.get())
381                        return
382
[3917c36]383                json_body = jsonBody()
[bd98dd1]384                for k, v in status.items():
385                        json_body.add_element(k, v)
[86eb703]386
[583057b]387                if self.state != ThreadState.BUSY:
388                        response = restResponse(ogResponses.OK, json_body)
389                else:
390                        response = restResponse(ogResponses.IN_PROGRESS, json_body)
391
[86eb703]392                client.send(response.get())
[e20daf6]393
[8fc251e]394        def process_shellrun(self, client, request):
[5964e48]395                threading.Thread(target=ogThread.shellrun, args=(client, request, self,)).start()
[2fa8aa4]396
[8fc251e]397        def process_session(self, client, request):
[2e80653]398                threading.Thread(target=ogThread.session, args=(client, request, self,)).start()
[6d1e79b]399
[8fc251e]400        def process_software(self, client, request):
[ca0a62f]401                path = '/tmp/CSft-' + client.ip + '-' + str(request.getPartition())
[2e80653]402                threading.Thread(target=ogThread.software, args=(client, request, path, self,)).start()
[261a5ed]403
404        def process_hardware(self, client):
405                path = '/tmp/Chrd-' + client.ip
[2e80653]406                threading.Thread(target=ogThread.hardware, args=(client, path, self,)).start()
[9fd8f2d]407
408        def process_schedule(self, client):
[86eb703]409                response = restResponse(ogResponses.OK)
410                client.send(response.get())
[4e1ad0f]411                self.state = ThreadState.IDLE
[efbe8a7]412
[8fc251e]413        def process_setup(self, client, request):
[2e80653]414                threading.Thread(target=ogThread.setup, args=(client, request, self,)).start()
[cc11d8f]415
[2e80653]416        def process_imagerestore(self, client, request):
417                threading.Thread(target=ogThread.image_restore, args=(client, request, self,)).start()
[d5dca0f]418
419        def process_stop(self, client):
420                client.disconnect()
[583057b]421                if self.state == ThreadState.BUSY:
[6f7ba32]422                        self.kill_process()
[d5dca0f]423                        self.terminated = True
[583057b]424
425                sys.exit(0)
[b2fd0b5]426
[2e80653]427        def process_imagecreate(self, client, request):
[8fc251e]428                path = '/tmp/CSft-' + client.ip + '-' + request.getPartition()
[2e80653]429                threading.Thread(target=ogThread.image_create, args=(client, path, request, self,)).start()
[b5e182f]430
431        def process_refresh(self, client):
[2e80653]432                threading.Thread(target=ogThread.refresh, args=(client, self,)).start()
Note: See TracBrowser for help on using the repository browser.